diff --git a/gitea.yml b/gitea.yml new file mode 100644 index 0000000..a18f594 --- /dev/null +++ b/gitea.yml @@ -0,0 +1,13 @@ +--- +- name: Configure gitea + hosts: + - gitea + roles: + - common + - ipa_client + - role: geerlingguy.mysql + when: gitea_database == 'mysql' or gitea_database == 'mariadb' + - gitea + - community.zabbix.zabbix_agent + - devsec.hardening.os_hardening + - devsec.hardening.ssh_hardening diff --git a/group_vars/all.yml b/group_vars/all.yml index 9ce1715..3dc2044 100644 --- a/group_vars/all.yml +++ b/group_vars/all.yml @@ -1,3 +1,6 @@ +# overridden in group/host vars for staging hosts +staging: false + ssh_authorized_keys: - "https://github.com/codyro.keys" - "https://github.com/jonathanspw.keys" diff --git a/group_vars/gitea.yml b/group_vars/gitea.yml new file mode 100644 index 0000000..916899f --- /dev/null +++ b/group_vars/gitea.yml @@ -0,0 +1,124 @@ +--- +firewalld_extra_allow_ports: + - "22/tcp" + - "80/tcp" + - "443/tcp" + +gitea_version: 1.21.11 +gitea_database: mysql +gitea_hostname: git.almalinux.org + +mysql_root_password: + "{{ lookup('community.hashi_vault.hashi_vault', 'kv/data/infra/{% if staging %}stg/{% endif %}gitea:mysql_root_password', + token=lookup('env', 'VAULT_TOKEN'), url=secrets_url) }}" +mysql_databases: + - name: gitea +mysql_users: + - name: gitea + password: + "{{ lookup('community.hashi_vault.hashi_vault', 'kv/data/infra/{% if staging %}stg/{% endif %}gitea:mysql_gitea_user_password', + token=lookup('env', 'VAULT_TOKEN'), url=secrets_url) }}" + priv: gitea.*:ALL +mysql_innodb_buffer_pool_size: "2G" +mysql_innodb_log_file_size: "512M" +mysql_innodb_log_buffer_size: "128M" +mysql_query_cache_size: "0" +mysql_key_buffer_size: "0" + +gitea_config: + no_section: + APP_NAME: AlmaLinux OS Foundation Git Server + RUN_MODE: prod + RUN_USER: git + WORK_PATH: /var/lib/gitea + sections: + database: + DB_TYPE: mysql + HOST: /var/lib/mysql/mysql.sock + NAME: gitea + USER: gitea + PASSWD: + "{{ lookup('community.hashi_vault.hashi_vault', 'kv/data/infra/{% if staging %}stg/{% endif %}gitea:mysql_gitea_user_password', + token=lookup('env', 'VAULT_TOKEN'), url=secrets_url) }}" + LOG_SQL: "false" + SSL_MODE: disable + CHARSET: utf8 + server: + ROOT_URL: https://{{ gitea_hostname }} + APP_DATA_PATH: /var/lib/gitea/data + DOMAIN: "{{ gitea_hostname }}" + SSH_DOMAIN: "{{ gitea_hostname }}" + HTTP_PORT: 3000 + DISABLE_SSH: "false" + SSH_PORT: 22 + SSH_LISTEN_PORT: 22 + OFFLINE_MODE: "false" + LANDING_PAGE: explore + repository: + ROOT: /var/lib/gitea/data/repositories + DEFAULT_REPO_UNITS: repo.code,repo.releases,repo.issues,repo.pulls + indexer: + ISSUE_INDEXER_PATH: /var/lib/gitea/indexers/issues.bleve + lfs: + PATH: /var/lib/gitea/data/lfs + mailer: + ENABLED: "false" + service: + REGISTER_EMAIL_CONFIRM: "false" + ENABLE_NOTIFY_MAIL: "false" + DISABLE_REGISTRATION: "false" + ALLOW_ONLY_EXTERNAL_REGISTRATION: "false" + ENABLE_CAPTCHA: "false" + REQUIRE_SIGNIN_VIEW: "false" + DEFAULT_KEEP_EMAIL_PRIVATE: "false" + DEFAULT_ALLOW_CREATE_ORGANIZATION: "true" + DEFAULT_ENABLE_TIMETRACKING: "true" + NO_REPLY_ADDRESS: noreply.localhost + openid: + ENABLE_OPENID_SIGNIN: "true" + ENABLE_OPENID_SIGNUP: "true" + cron.update_checker: + ENABLED: "false" + session: + PROVIDER: file + log: + MODE: console + LEVEL: info + ROOT_PATH: /var/lib/gitea/log + security: + INSTALL_LOCK: "true" + REVERSE_PROXY_LIMIT: 1 + REVERSE_PROXY_TRUSTED_PROXIES: "*" + PASSWORD_HASH_ALGO: pbkdf2 + SECRET_KEY: + "{{ lookup('community.hashi_vault.hashi_vault', 'kv/data/infra/{% if staging %}stg/{% endif %}gitea:secret_key', + token=lookup('env', 'VAULT_TOKEN'), url=secrets_url) }}" + INTERNAL_TOKEN: + "{{ lookup('community.hashi_vault.hashi_vault', 'kv/data/infra/{% if staging %}stg/{% endif %}gitea:internal_token', + token=lookup('env', 'VAULT_TOKEN'), url=secrets_url) }}" + oauth2: + JWT_SECRET: + "{{ lookup('community.hashi_vault.hashi_vault', 'kv/data/infra/{% if staging %}stg/{% endif %}gitea:jwt_secret', + token=lookup('env', 'VAULT_TOKEN'), url=secrets_url) }}" + log.file: + LOG_ROTATE: "true" + MAX_SIZE_SHIFT: 28 + DAILY_ROTATE: "true" + MAX_DAYS: 31 + COMPRESS: "true" + repository.pull_request: + DEFAULT_MERGE_STYLE: merge + repository.signing: + DEFAULT_TRUST_MODEL: committer + repository.local: + LOCAL_COPY_PATH: /var/lib/gitea/tmp/local-repo + repository.upload: + TEMP_PATH: /var/lib/gitea/uploads + picture: + AVATAR_UPLOAD_PATH: /var/lib/gitea/data/avatars + REPOSITORY_AVATAR_UPLOAD_PATH: /var/lib/gitea/data/repo-avatars + DISABLE_GRAVATAR: "false" + cron.archive_cleanup: + ENABLED: "true" + SHCEDULE: "@midnight" + OLDER_THAN: 24h diff --git a/host_vars/git.almalinux.org.yml b/host_vars/git.almalinux.org.yml new file mode 100644 index 0000000..fff2d6a --- /dev/null +++ b/host_vars/git.almalinux.org.yml @@ -0,0 +1,2 @@ +--- +gitea_hostname: git.almalinux.org diff --git a/host_vars/stg.git.almalinux.org.yml b/host_vars/stg.git.almalinux.org.yml new file mode 100644 index 0000000..1985c37 --- /dev/null +++ b/host_vars/stg.git.almalinux.org.yml @@ -0,0 +1,3 @@ +--- +staging: true +gitea_hostname: stg.git.almalinux.org diff --git a/hosts b/hosts index b1965e1..cf876a3 100644 --- a/hosts +++ b/hosts @@ -49,3 +49,13 @@ el7_mirrors [hashivault] vault[01:05].secrets.almalinux.org + +[gitea_prod] +git.almalinux.org + +[gitea_stg] +stg.git.almalinux.org + +[gitea:children] +gitea_prod +gitea_stg diff --git a/requirements.yml b/requirements.yml index 038c66b..89c8532 100644 --- a/requirements.yml +++ b/requirements.yml @@ -4,7 +4,10 @@ collections: - name: devsec.hardening - name: freeipa.ansible_freeipa - name: community.hashi_vault + # 3.0.4 fixes a bug related to zabbix gpg keys + # https://github.com/ansible-collections/community.zabbix/pull/1272 - name: community.zabbix + version: ">=3.0.4" - name: community.general roles: @@ -12,3 +15,4 @@ roles: - name: ansible-modules-hashivault src: git+https://github.com/TerryHowe/ansible-modules-hashivault.git - name: geerlingguy.postgresql + - name: geerlingguy.mysql diff --git a/roles/gitea/defaults/main.yml b/roles/gitea/defaults/main.yml new file mode 100644 index 0000000..fe68aa1 --- /dev/null +++ b/roles/gitea/defaults/main.yml @@ -0,0 +1,4 @@ +--- +gitea_database: sqlite +gitea_hostname: localhost.localdomain +gitea_cache: '' diff --git a/roles/gitea/handlers/main.yml b/roles/gitea/handlers/main.yml new file mode 100644 index 0000000..9255b87 --- /dev/null +++ b/roles/gitea/handlers/main.yml @@ -0,0 +1,14 @@ +--- +- name: Reload systemd + ansible.builtin.systemd_service: + daemon_reload: true + +- name: Restart Gitea + ansible.builtin.systemd_service: + name: gitea + state: restarted + +- name: Restart caddy + ansible.builtin.systemd_service: + name: caddy.service + state: restarted diff --git a/roles/gitea/tasks/caddy.yml b/roles/gitea/tasks/caddy.yml new file mode 100644 index 0000000..2bb735f --- /dev/null +++ b/roles/gitea/tasks/caddy.yml @@ -0,0 +1,36 @@ +--- +- name: Install Caddy + ansible.builtin.dnf: + name: caddy + state: present + tags: + - caddy + +- name: Create Caddy log dir + ansible.builtin.file: + path: /var/log/caddy/ + owner: caddy + group: caddy + mode: "0700" + state: directory + tags: + - caddy + +- name: Distribute /etc/caddy/Caddyfile + ansible.builtin.template: + src: Caddyfile.j2 + dest: /etc/caddy/Caddyfile + mode: "0600" + owner: caddy + group: caddy + notify: Restart caddy + tags: + - caddy + +- name: Start and enable Caddy + ansible.builtin.systemd: + name: caddy.service + state: started + enabled: true + tags: + - caddy diff --git a/roles/gitea/tasks/main.yml b/roles/gitea/tasks/main.yml new file mode 100644 index 0000000..ab13231 --- /dev/null +++ b/roles/gitea/tasks/main.yml @@ -0,0 +1,57 @@ +--- +- name: Install dependencies + ansible.builtin.package: + name: + - git + state: present + +- name: Download gitea binary + ansible.builtin.get_url: + url: https://dl.gitea.com/gitea/{{ gitea_version }}/gitea-{{ gitea_version }}-linux-amd64 + dest: /usr/local/bin/gitea + mode: "0755" + +- name: Create system user + ansible.builtin.user: + name: git + comment: Gitea user + +- name: Create directory structure + ansible.builtin.file: + name: "{{ item }}" + state: directory + mode: "0750" + owner: git + group: git + loop: + - /var/lib/gitea/custom + - /var/lib/gitea/data + - /var/lib/gitea/log + - /etc/gitea + +- name: Copy systemd service template + ansible.builtin.template: + src: gitea.service.j2 + dest: /etc/systemd/system/gitea.service + owner: root + group: root + mode: "0644" + notify: Reload systemd + +- name: Write Gitea conf + ansible.builtin.template: + src: app.ini.j2 + dest: /etc/gitea/app.ini + mode: "0640" + owner: git + group: git + notify: Restart Gitea + +- name: Start gitea + ansible.builtin.systemd_service: + name: gitea + state: started + enabled: true + +- name: Configure caddy reverse proxy + ansible.builtin.include_tasks: caddy.yml diff --git a/roles/gitea/templates/Caddyfile.j2 b/roles/gitea/templates/Caddyfile.j2 new file mode 100644 index 0000000..3291b83 --- /dev/null +++ b/roles/gitea/templates/Caddyfile.j2 @@ -0,0 +1,13 @@ +# {{ ansible_managed }} + +{{ inventory_hostname }} { + + reverse_proxy :3000 + + log { + output file /var/log/caddy/{{ inventory_hostname }}.access.log { + roll_keep_for 30d + } + } + +} diff --git a/roles/gitea/templates/app.ini.j2 b/roles/gitea/templates/app.ini.j2 new file mode 100644 index 0000000..f3905c6 --- /dev/null +++ b/roles/gitea/templates/app.ini.j2 @@ -0,0 +1,12 @@ +# {{ ansible_managed }} +{% for key,value in gitea_config.no_section.items() %} +{{ key }} = {{ value }} +{% endfor %} + +{% for section,values in gitea_config.sections.items() %} +[{{ section }}] +{% for key,value in values.items() %} +{{ key }} = {{ value }} +{% endfor %} + +{% endfor %} diff --git a/roles/gitea/templates/gitea.service.j2 b/roles/gitea/templates/gitea.service.j2 new file mode 100644 index 0000000..21ba037 --- /dev/null +++ b/roles/gitea/templates/gitea.service.j2 @@ -0,0 +1,44 @@ +# {{ ansible_managed }} +[Unit] +Description=Gitea (Git with a cup of tea) +After=network-online.target + +{% if gitea_database == 'mysql' %} +Wants=mysql.service +After=mysql.service +{% elif gitea_database == 'mariadb' %} +Wants=mariadb.service +After=mariadb.service +{% elif gitea_database == 'postgresql' %} +Wants=postgresql.service +After=postgresql.service +{% endif %} + +{% if gitea_cache == 'memcached' %} +Wants=memcached.service +After=memcached.service +{% elif gitea_cache == 'redis' %} +Wants=redis.service +After=redis.Service +{% elif gitea_cache == 'valkey' %} +Wants=valkey.service +After=valkey.service +{% endif %} + +[Service] +# Uncomment the next line if you have repos with lots of files and get a HTTP 500 error because of that +# LimitNOFILE=524288:524288 +RestartSec=2s +Type=simple +User=git +Group=git +WorkingDirectory=/var/lib/gitea/ +# If using Unix socket: tells systemd to create the /run/gitea folder, which will contain the gitea.sock file +# (manually creating /run/gitea doesn't work, because it would not persist across reboots) +#RuntimeDirectory=gitea +ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini +Restart=always +Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea + +[Install] +WantedBy=multi-user.target