From 1f63633c3068f5c8ed67e96dfd42fd5552193aa8 Mon Sep 17 00:00:00 2001 From: Jonathan Wright Date: Fri, 11 Sep 2026 11:46:38 -0500 Subject: [PATCH 1/4] Fix remaining ansible >= 2.19 boolean conditionals Pin geerlingguy.mysql to 6.5.0; releases before 6.0.0 use non-boolean conditionals that ansible >= 2.19 rejects. In ipa_client, `lookup('env', ...) | default(false)` leaves the empty string an unset env var returns, since `default` only substitutes on undefined. Pipe through `bool` so the conditional is a real boolean. --- requirements.yml | 3 +++ roles/ipa_client/tasks/main.yml | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/requirements.yml b/requirements.yml index 5fa23fe..821a385 100644 --- a/requirements.yml +++ b/requirements.yml @@ -16,4 +16,7 @@ roles: src: git+https://github.com/TerryHowe/ansible-modules-hashivault.git - name: geerlingguy.postgresql - name: geerlingguy.mysql + # 6.0.0 is the first release whose conditionals are boolean, as + # ansible >= 2.19 requires + version: 6.5.0 - name: geerlingguy.php diff --git a/roles/ipa_client/tasks/main.yml b/roles/ipa_client/tasks/main.yml index a669e28..4930cd9 100644 --- a/roles/ipa_client/tasks/main.yml +++ b/roles/ipa_client/tasks/main.yml @@ -7,7 +7,9 @@ - name: Configure freeipa client vars: - ipaclient_reinstall: "{{ lookup('env', 'IPACLIENT_REINSTALL') | default(false) }}" # noqa var-naming[no-role-prefix] + # `default(false)` does not replace the empty string an unset env var + # returns, and ansible >= 2.19 rejects a non-boolean conditional result + ipaclient_reinstall: "{{ lookup('env', 'IPACLIENT_REINSTALL') | bool }}" # noqa var-naming[no-role-prefix] state: present ansible.builtin.include_role: name: freeipa.ansible_freeipa.ipaclient From 577ee152b69e83f79a4ae99a3351862c509bffee Mon Sep 17 00:00:00 2001 From: Jonathan Wright Date: Fri, 11 Sep 2026 11:46:38 -0500 Subject: [PATCH 2/4] Wrap artis3n.tailscale in a local tailscale role artis3n.tailscale re-runs `tailscale up --reset` whenever its state file changes -- a new host, a different remote user or a changed login server is enough -- which re-keys a node that is already on the tailnet. Wrap it in a local role that checks `tailscale status` first and only brings the node up when it isn't already connected, with TAILSCALE_REAUTH to force it when re-keying is actually wanted. Since the wrapped role only registers the node addresses when it runs `tailscale up`, register them here too so roles/keycloak can still build its cluster IP list. --- roles/ipa_client/meta/main.yml | 2 +- roles/keycloak/meta/main.yml | 2 +- roles/tailscale/defaults/main.yml | 4 ++++ roles/tailscale/tasks/main.yml | 31 +++++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 roles/tailscale/defaults/main.yml create mode 100644 roles/tailscale/tasks/main.yml diff --git a/roles/ipa_client/meta/main.yml b/roles/ipa_client/meta/main.yml index 555a6e0..2c78faf 100644 --- a/roles/ipa_client/meta/main.yml +++ b/roles/ipa_client/meta/main.yml @@ -1,3 +1,3 @@ --- dependencies: - - role: artis3n.tailscale + - role: tailscale diff --git a/roles/keycloak/meta/main.yml b/roles/keycloak/meta/main.yml index 30d952e..6cab1c6 100644 --- a/roles/keycloak/meta/main.yml +++ b/roles/keycloak/meta/main.yml @@ -1,6 +1,6 @@ --- dependencies: - - role: artis3n.tailscale + - role: tailscale collections: - middleware_automation.keycloak diff --git a/roles/tailscale/defaults/main.yml b/roles/tailscale/defaults/main.yml new file mode 100644 index 0000000..f6122cb --- /dev/null +++ b/roles/tailscale/defaults/main.yml @@ -0,0 +1,4 @@ +--- +# Force `tailscale up` to run even when the node is already connected, e.g. to +# re-key a node or move it to a different tailnet +tailscale_reauth: "{{ lookup('env', 'TAILSCALE_REAUTH') | bool }}" diff --git a/roles/tailscale/tasks/main.yml b/roles/tailscale/tasks/main.yml new file mode 100644 index 0000000..421cfa4 --- /dev/null +++ b/roles/tailscale/tasks/main.yml @@ -0,0 +1,31 @@ +--- +# artis3n.tailscale re-runs `tailscale up --reset` whenever its state file +# changes -- a new host, a different remote user or a changed login server is +# enough -- which re-keys a node that is already on the tailnet. Only bring +# the node up if it isn't already connected. +- name: Check if tailscale is already connected + ansible.builtin.command: tailscale status --json + register: tailscale_status_check + changed_when: false + failed_when: false + +- name: Record tailscale connection state + ansible.builtin.set_fact: + tailscale_connected: + "{{ tailscale_status_check.rc == 0 and (tailscale_status_check.stdout | from_json).Self.Online | default(false) }}" + +- name: Configure tailscale + vars: + tailscale_up_skip: "{{ tailscale_connected and not tailscale_reauth }}" + ansible.builtin.include_role: + name: artis3n.tailscale + +# artis3n.tailscale only registers these when it runs `tailscale up`, and +# roles/keycloak builds its cluster IP list from them +- name: Register tailscale node addresses + vars: + tailscale_ips: "{{ (tailscale_status_check.stdout | from_json).Self.TailscaleIPs | default([]) }}" + ansible.builtin.set_fact: + tailscale_node_ipv4: "{{ tailscale_ips | reject('contains', ':') | first | default('') }}" + tailscale_node_ipv6: "{{ tailscale_ips | select('contains', ':') | first | default('') }}" + when: tailscale_connected and not tailscale_reauth From 4916e8eecb9d31b41d7eeabd4a31b37dd64bacca Mon Sep 17 00:00:00 2001 From: Jonathan Wright Date: Fri, 11 Sep 2026 11:46:38 -0500 Subject: [PATCH 3/4] Switch gitea to forgejo binaries Track forgejo upstream rather than gitea; the release layout differs, so point the download at code.forgejo.org and follow its versioning. Verify the binary against the .sha256 sidecar published beside each release. Without a checksum, get_url only looks at whether dest exists and never at the URL, so the versioned URL changed but the binary at /usr/local/bin/gitea never did. With it, get_url hashes the installed binary and re-downloads only on a mismatch, so a version bump actually takes effect and correctly notifies the restart handler. --- group_vars/gitea.yml | 2 +- roles/gitea/tasks/main.yml | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/group_vars/gitea.yml b/group_vars/gitea.yml index bd7d035..6b94646 100644 --- a/group_vars/gitea.yml +++ b/group_vars/gitea.yml @@ -4,7 +4,7 @@ firewalld_extra_allow_ports: - "80/tcp" - "443/tcp" -gitea_version: 1.22.6 +gitea_version: 15.0.8 gitea_database: mysql gitea_hostname: git.almalinux.org diff --git a/roles/gitea/tasks/main.yml b/roles/gitea/tasks/main.yml index f65bc02..c11ff9c 100644 --- a/roles/gitea/tasks/main.yml +++ b/roles/gitea/tasks/main.yml @@ -10,9 +10,13 @@ - name: Download gitea binary ansible.builtin.get_url: - url: https://dl.gitea.com/gitea/{{ gitea_version }}/gitea-{{ gitea_version }}-linux-amd64 + url: "{{ _gitea_url }}" + checksum: "sha256:{{ _gitea_url }}.sha256" dest: /usr/local/bin/gitea mode: "0755" + vars: + _gitea_url: https://code.forgejo.org/forgejo/forgejo/releases/download/v{{ gitea_version }}/forgejo-{{ gitea_version }}-linux-amd64 + notify: Restart Gitea - name: Create system user ansible.builtin.user: From 46fee159ca27c14df198a397cf9dc32ba1727872 Mon Sep 17 00:00:00 2001 From: Jonathan Wright Date: Fri, 11 Sep 2026 11:46:38 -0500 Subject: [PATCH 4/4] Replace caddy with nginx and certbot for gitea Bring gitea in line with the other public-facing roles, which all terminate TLS on nginx and manage certificates with certbot rather than relying on Caddy's automatic HTTPS. Gitea now listens on 127.0.0.1:3000 and nginx proxies to it, with a body size limit large enough for git pushes and LFS uploads. Certificates support webroot, Route53 and Cloudflare authentication; nginx is seeded with a self-signed certificate so it can start before the first certificate is issued, and the vhost is rewritten with the real paths once certbot succeeds. DNS-authenticated certificates are fetched before nginx starts, since they need no running webserver. --- group_vars/gitea.yml | 3 +- roles/gitea/defaults/main.yml | 19 ++++ roles/gitea/handlers/main.yml | 9 +- roles/gitea/tasks/caddy.yml | 36 ------ roles/gitea/tasks/certbot-obtain.yml | 57 ++++++++++ roles/gitea/tasks/certbot.yml | 107 ++++++++++++++++++ roles/gitea/tasks/cloudflare.yml | 8 ++ roles/gitea/tasks/main.yml | 10 +- roles/gitea/tasks/nginx.yml | 87 ++++++++++++++ roles/gitea/tasks/route53.yml | 15 +++ roles/gitea/templates/Caddyfile.j2 | 13 --- roles/gitea/templates/aws_config.j2 | 5 + .../gitea/templates/certbot-deploy-hook.sh.j2 | 5 + roles/gitea/templates/cloudflare.ini.j2 | 2 + roles/gitea/templates/nginx/gitea.conf.j2 | 75 ++++++++++++ roles/gitea/templates/nginx/nginx.conf.j2 | 62 ++++++++++ 16 files changed, 459 insertions(+), 54 deletions(-) delete mode 100644 roles/gitea/tasks/caddy.yml create mode 100644 roles/gitea/tasks/certbot-obtain.yml create mode 100644 roles/gitea/tasks/certbot.yml create mode 100644 roles/gitea/tasks/cloudflare.yml create mode 100644 roles/gitea/tasks/nginx.yml create mode 100644 roles/gitea/tasks/route53.yml delete mode 100644 roles/gitea/templates/Caddyfile.j2 create mode 100644 roles/gitea/templates/aws_config.j2 create mode 100644 roles/gitea/templates/certbot-deploy-hook.sh.j2 create mode 100644 roles/gitea/templates/cloudflare.ini.j2 create mode 100644 roles/gitea/templates/nginx/gitea.conf.j2 create mode 100644 roles/gitea/templates/nginx/nginx.conf.j2 diff --git a/group_vars/gitea.yml b/group_vars/gitea.yml index 6b94646..d36a2fa 100644 --- a/group_vars/gitea.yml +++ b/group_vars/gitea.yml @@ -53,7 +53,8 @@ gitea_config: APP_DATA_PATH: /var/lib/gitea/data DOMAIN: "{{ gitea_hostname }}" SSH_DOMAIN: "{{ gitea_hostname }}" - HTTP_PORT: 3000 + HTTP_ADDR: "{{ gitea_listen_address }}" + HTTP_PORT: "{{ gitea_listen_port }}" DISABLE_SSH: "false" SSH_PORT: 22 SSH_LISTEN_PORT: 22 diff --git a/roles/gitea/defaults/main.yml b/roles/gitea/defaults/main.yml index fe68aa1..cae4b94 100644 --- a/roles/gitea/defaults/main.yml +++ b/roles/gitea/defaults/main.yml @@ -2,3 +2,22 @@ gitea_database: sqlite gitea_hostname: localhost.localdomain gitea_cache: '' + +# Backend gitea listens on; nginx proxies to it +gitea_listen_address: 127.0.0.1 +gitea_listen_port: 3000 + +# Extra names to include on the certificate / vhost +gitea_extra_domains: [] + +# Git pushes and LFS uploads can be large +gitea_client_max_body_size: 512M + +# Certbot authentication method: http | dns_route53 | dns_cloudflare +gitea_certbot_auth: http +gitea_cloudflare_propagation_seconds: 30 + +# Only required when gitea_certbot_auth is dns_route53 / dns_cloudflare +gitea_route53_access_key: '' +gitea_route53_secret_key: '' +gitea_cloudflare_api_token: '' diff --git a/roles/gitea/handlers/main.yml b/roles/gitea/handlers/main.yml index 9255b87..ef08e38 100644 --- a/roles/gitea/handlers/main.yml +++ b/roles/gitea/handlers/main.yml @@ -8,7 +8,12 @@ name: gitea state: restarted -- name: Restart caddy +- name: Reload nginx ansible.builtin.systemd_service: - name: caddy.service + name: nginx.service + state: reloaded + +- name: Restart nginx + ansible.builtin.systemd_service: + name: nginx.service state: restarted diff --git a/roles/gitea/tasks/caddy.yml b/roles/gitea/tasks/caddy.yml deleted file mode 100644 index 2bb735f..0000000 --- a/roles/gitea/tasks/caddy.yml +++ /dev/null @@ -1,36 +0,0 @@ ---- -- 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/certbot-obtain.yml b/roles/gitea/tasks/certbot-obtain.yml new file mode 100644 index 0000000..f360bf1 --- /dev/null +++ b/roles/gitea/tasks/certbot-obtain.yml @@ -0,0 +1,57 @@ +--- +- name: Check for existing certbot-managed certificate + ansible.builtin.stat: + path: /etc/letsencrypt/renewal/{{ gitea_hostname }}.conf + register: gitea_certbot_renewal_check + tags: certbot + +- name: Remove placeholder certificate before certbot provisioning + ansible.builtin.file: + path: /etc/letsencrypt/live/{{ gitea_hostname }} + state: absent + when: not gitea_certbot_renewal_check.stat.exists + tags: certbot + +- name: Obtain SSL certificate with certbot # noqa no-changed-when + ansible.builtin.shell: > + certbot certonly + {% if gitea_certbot_auth == 'dns_route53' %} + --dns-route53 + {% elif gitea_certbot_auth == 'dns_cloudflare' %} + --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini + --dns-cloudflare-propagation-seconds {{ gitea_cloudflare_propagation_seconds }} + {% else %} + --webroot -w /var/lib/letsencrypt + {% endif %} + --cert-name {{ gitea_hostname }} + -d {{ gitea_hostname }} + {% for domain in gitea_extra_domains %} + -d {{ domain }} + {% endfor %} + -m {{ certbot_email }} --agree-tos -n + --deploy-hook /etc/letsencrypt/deploy-nginx.sh + when: not gitea_certbot_renewal_check.stat.exists + register: gitea_certbot_result + failed_when: + - gitea_certbot_result.rc is defined + - gitea_certbot_result.rc != 0 + - gitea_certbot_auth != 'http' + tags: certbot + +- name: Check for SSL certificate after obtainment + ansible.builtin.stat: + path: /etc/letsencrypt/live/{{ gitea_hostname }}/fullchain.pem + register: gitea_le_cert_check + tags: certbot + +- name: Update vhost config with real certificate paths + ansible.builtin.template: + src: nginx/gitea.conf.j2 + dest: /etc/nginx/conf.d/{{ gitea_hostname }}.conf + mode: "0644" + owner: root + group: root + vars: + _ssl_cert_path: "{{ '/etc/letsencrypt/live/' ~ gitea_hostname if gitea_le_cert_check.stat.exists else '/etc/pki/nginx/' ~ gitea_hostname }}" + notify: Reload nginx + tags: certbot diff --git a/roles/gitea/tasks/certbot.yml b/roles/gitea/tasks/certbot.yml new file mode 100644 index 0000000..6ff15f6 --- /dev/null +++ b/roles/gitea/tasks/certbot.yml @@ -0,0 +1,107 @@ +--- +- name: Route53 tasks + ansible.builtin.include_tasks: route53.yml + when: gitea_certbot_auth == 'dns_route53' + tags: certbot + +- name: Install certbot + ansible.builtin.package: + name: + - certbot + state: present + tags: certbot + +- name: Install certbot Route53 plugin + ansible.builtin.package: + name: + - python3-certbot-dns-route53 + state: present + when: gitea_certbot_auth == 'dns_route53' + tags: certbot + +- name: Install certbot Cloudflare plugin + ansible.builtin.package: + name: + - python3-certbot-dns-cloudflare + state: present + when: gitea_certbot_auth == 'dns_cloudflare' + tags: certbot + +- name: Ensure Let's Encrypt directory exists + ansible.builtin.file: + path: /etc/letsencrypt + state: directory + mode: "0700" + tags: certbot + +- name: Cloudflare credentials + ansible.builtin.include_tasks: cloudflare.yml + when: gitea_certbot_auth == 'dns_cloudflare' + tags: certbot + +- name: Create ACME webroot directory + ansible.builtin.file: + path: /var/lib/letsencrypt/.well-known/acme-challenge + state: directory + mode: "0755" + owner: root + group: root + tags: certbot + +- name: Start/enable certbot renew timer + ansible.builtin.systemd_service: + name: certbot-renew.timer + enabled: true + state: started + tags: certbot + +- name: Configure certbot deploy hook + ansible.builtin.template: + src: certbot-deploy-hook.sh.j2 + dest: /etc/letsencrypt/deploy-nginx.sh + mode: "0755" + tags: certbot + +- name: Create self-signed certificate directory + ansible.builtin.file: + path: /etc/pki/nginx/{{ gitea_hostname }} + state: directory + mode: "0700" + tags: certbot + +- name: Generate self-signed certificate for initial nginx startup # noqa no-changed-when + ansible.builtin.command: + cmd: > + openssl req -x509 -nodes -days 365 -newkey rsa:2048 + -keyout /etc/pki/nginx/{{ gitea_hostname }}/privkey.pem + -out /etc/pki/nginx/{{ gitea_hostname }}/fullchain.pem + -subj '/CN={{ gitea_hostname }}' + creates: /etc/pki/nginx/{{ gitea_hostname }}/fullchain.pem + tags: certbot + +- name: Check for existing certbot renewal config (DNS pre-fetch) + ansible.builtin.stat: + path: /etc/letsencrypt/renewal/{{ gitea_hostname }}.conf + register: gitea_certbot_dns_check + tags: certbot + +- name: Obtain DNS-auth certificate before nginx startup # noqa no-changed-when + ansible.builtin.shell: > + certbot certonly + {% if gitea_certbot_auth == 'dns_route53' %} + --dns-route53 + {% elif gitea_certbot_auth == 'dns_cloudflare' %} + --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini + --dns-cloudflare-propagation-seconds {{ gitea_cloudflare_propagation_seconds }} + {% endif %} + --cert-name {{ gitea_hostname }} + -d {{ gitea_hostname }} + {% for domain in gitea_extra_domains %} + -d {{ domain }} + {% endfor %} + -m {{ certbot_email }} --agree-tos -n + --deploy-hook /etc/letsencrypt/deploy-nginx.sh + when: + - not gitea_certbot_dns_check.stat.exists + - gitea_certbot_auth != 'http' + tags: certbot diff --git a/roles/gitea/tasks/cloudflare.yml b/roles/gitea/tasks/cloudflare.yml new file mode 100644 index 0000000..d4153fd --- /dev/null +++ b/roles/gitea/tasks/cloudflare.yml @@ -0,0 +1,8 @@ +--- +- name: Write Cloudflare credentials file + ansible.builtin.template: + src: cloudflare.ini.j2 + dest: /etc/letsencrypt/cloudflare.ini + mode: "0600" + owner: root + group: root diff --git a/roles/gitea/tasks/main.yml b/roles/gitea/tasks/main.yml index c11ff9c..a6d9b68 100644 --- a/roles/gitea/tasks/main.yml +++ b/roles/gitea/tasks/main.yml @@ -60,5 +60,11 @@ state: started enabled: true -- name: Configure caddy reverse proxy - ansible.builtin.include_tasks: caddy.yml +- name: Certbot + ansible.builtin.include_tasks: certbot.yml + +- name: Configure nginx reverse proxy + ansible.builtin.include_tasks: nginx.yml + +- name: Obtain SSL certificate + ansible.builtin.include_tasks: certbot-obtain.yml diff --git a/roles/gitea/tasks/nginx.yml b/roles/gitea/tasks/nginx.yml new file mode 100644 index 0000000..96356f3 --- /dev/null +++ b/roles/gitea/tasks/nginx.yml @@ -0,0 +1,87 @@ +--- +- name: Ensure Caddy is stopped and disabled + ansible.builtin.systemd_service: + name: caddy.service + state: stopped + enabled: false + failed_when: false + tags: + - nginx + +- name: Remove Caddy configuration + ansible.builtin.file: + path: /etc/caddy/Caddyfile + state: absent + tags: + - nginx + +- name: Install nginx and brotli module + ansible.builtin.dnf: + name: + - nginx + - nginx-mod-brotli + state: present + tags: + - nginx + +- name: Gather package facts for nginx version check + ansible.builtin.package_facts: + manager: auto + tags: + - nginx + +- name: Allow nginx to proxy to backend (SELinux) + ansible.posix.seboolean: + name: httpd_can_network_connect + state: true + persistent: true + tags: + - nginx + - selinux + +- name: Distribute /etc/nginx/nginx.conf + ansible.builtin.template: + src: nginx/nginx.conf.j2 + dest: /etc/nginx/nginx.conf + mode: "0644" + owner: root + group: root + notify: Reload nginx + tags: + - nginx + +- name: Check for Let's Encrypt certificate + ansible.builtin.stat: + path: /etc/letsencrypt/live/{{ gitea_hostname }}/fullchain.pem + register: gitea_le_cert_check + tags: + - nginx + +- name: Distribute gitea vhost configuration + ansible.builtin.template: + src: nginx/gitea.conf.j2 + dest: /etc/nginx/conf.d/{{ gitea_hostname }}.conf + mode: "0644" + owner: root + group: root + vars: + _ssl_cert_path: "{{ '/etc/letsencrypt/live/' ~ gitea_hostname if gitea_le_cert_check.stat.exists else '/etc/pki/nginx/' ~ gitea_hostname }}" + notify: Reload nginx + tags: + - nginx + +- name: Remove default nginx site configuration + ansible.builtin.file: + path: /etc/nginx/conf.d/default.conf + state: absent + notify: Reload nginx + tags: + - nginx + +- name: Start/enable nginx + ansible.builtin.systemd_service: + name: nginx.service + state: started + enabled: true + tags: + - nginx diff --git a/roles/gitea/tasks/route53.yml b/roles/gitea/tasks/route53.yml new file mode 100644 index 0000000..e4043f3 --- /dev/null +++ b/roles/gitea/tasks/route53.yml @@ -0,0 +1,15 @@ +--- +- name: Create AWS config dir + ansible.builtin.file: + path: /root/.aws + state: directory + mode: "0700" + +- name: Write AWS config file + vars: + aws_access_key_id: "{{ gitea_route53_access_key }}" + aws_secret_access_key: "{{ gitea_route53_secret_key }}" + ansible.builtin.template: + src: aws_config.j2 + dest: /root/.aws/config + mode: "0600" diff --git a/roles/gitea/templates/Caddyfile.j2 b/roles/gitea/templates/Caddyfile.j2 deleted file mode 100644 index 3291b83..0000000 --- a/roles/gitea/templates/Caddyfile.j2 +++ /dev/null @@ -1,13 +0,0 @@ -# {{ 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/aws_config.j2 b/roles/gitea/templates/aws_config.j2 new file mode 100644 index 0000000..4e7228d --- /dev/null +++ b/roles/gitea/templates/aws_config.j2 @@ -0,0 +1,5 @@ +# {{ ansible_managed }} +[default] +region = us-east-1 +aws_access_key_id = {{ aws_access_key_id }} +aws_secret_access_key = {{ aws_secret_access_key }} diff --git a/roles/gitea/templates/certbot-deploy-hook.sh.j2 b/roles/gitea/templates/certbot-deploy-hook.sh.j2 new file mode 100644 index 0000000..c596278 --- /dev/null +++ b/roles/gitea/templates/certbot-deploy-hook.sh.j2 @@ -0,0 +1,5 @@ +#!/bin/sh +# {{ ansible_managed }} + +# Only reload nginx if it is running +systemctl is-active --quiet nginx.service && systemctl reload nginx.service diff --git a/roles/gitea/templates/cloudflare.ini.j2 b/roles/gitea/templates/cloudflare.ini.j2 new file mode 100644 index 0000000..d4722b0 --- /dev/null +++ b/roles/gitea/templates/cloudflare.ini.j2 @@ -0,0 +1,2 @@ +# {{ ansible_managed }} +dns_cloudflare_api_token = {{ gitea_cloudflare_api_token }} diff --git a/roles/gitea/templates/nginx/gitea.conf.j2 b/roles/gitea/templates/nginx/gitea.conf.j2 new file mode 100644 index 0000000..8bdfb16 --- /dev/null +++ b/roles/gitea/templates/nginx/gitea.conf.j2 @@ -0,0 +1,75 @@ +# {{ ansible_managed }} +# Virtual host: {{ gitea_hostname }} + +{% set all_server_names = [gitea_hostname] + gitea_extra_domains %} +upstream gitea_backend { + server {{ gitea_listen_address }}:{{ gitea_listen_port }}; + keepalive 32; +} + +server { + listen 80; + listen [::]:80; + server_name {{ all_server_names | join(' ') }}; + + location /.well-known/acme-challenge/ { + root /var/lib/letsencrypt; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { +{% if ansible_facts.packages['nginx'][0]['version'] is version('1.25.1', '>=') %} + listen 443 ssl; + listen [::]:443 ssl; + http2 on; +{% else %} + listen 443 ssl http2; + listen [::]:443 ssl http2; +{% endif %} + server_name {{ all_server_names | join(' ') }}; + + ssl_certificate {{ _ssl_cert_path }}/fullchain.pem; + ssl_certificate_key {{ _ssl_cert_path }}/privkey.pem; + + # git push and LFS uploads are not small + client_max_body_size {{ gitea_client_max_body_size }}; + + access_log /var/log/nginx/{{ gitea_hostname }}.access.log; + + proxy_set_header Connection ""; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + + # Server-sent events used for the notification stream; buffering these + # would hold the response open until the connection times out. + location /user/events { + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 24h; + proxy_pass http://gitea_backend; + } + + # Smart HTTP git and LFS transfers: stream the body straight through + # rather than spooling large packs to disk first. + location ~ ^/[^/]+/[^/]+(\.git)?/(info/refs|git-upload-pack|git-receive-pack|info/lfs/) { + proxy_request_buffering off; + proxy_buffering off; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_pass http://gitea_backend; + } + + location / { + proxy_buffers 256 16k; + proxy_buffer_size 16k; + proxy_read_timeout 600s; + proxy_pass http://gitea_backend; + } +} diff --git a/roles/gitea/templates/nginx/nginx.conf.j2 b/roles/gitea/templates/nginx/nginx.conf.j2 new file mode 100644 index 0000000..25bcd63 --- /dev/null +++ b/roles/gitea/templates/nginx/nginx.conf.j2 @@ -0,0 +1,62 @@ +# {{ ansible_managed }} + +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; + +include /usr/share/nginx/modules/*.conf; + +events { + worker_connections 1024; +} + +http { + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 4096; + server_tokens off; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + ssl_session_tickets off; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_min_length 256; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml + application/rss+xml + image/svg+xml; + + brotli on; + brotli_comp_level 6; + brotli_min_length 256; + brotli_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml + application/rss+xml + image/svg+xml; + + include /etc/nginx/conf.d/*.conf; +}