314fa715fd
Two issues caused TLS to break on photos.carabosse.cloud over IPv6
(GrapheneOS + Immich app via Orange 5G NAT64):
1. Per-service vhosts only listened on IPv4 (listen 443 ssl). On IPv6,
nginx fell back to the first vhost loaded alphabetically and served
its certificate, breaking hostname verification on every other vhost.
2. /etc/letsencrypt/{live,archive} were 0700 root:root after certbot
created them, so the nginx worker (user http on Arch) could not read
the chained intermediates and served the leaf-only chain.
Changes:
- Add catch-all 00-default.conf default_server on :80 and :443 (v4+v6)
with a self-signed cert and 'return 444'. ACME challenges still
answered on :80.
- Add IPv6 listeners ([::]:80 and [::]:443 ssl) to immich, gitea, ntfy,
uptime_kuma vhosts and to the temporary ACME provisioning vhost.
- Apply 0755 on /etc/letsencrypt/live and /etc/letsencrypt/archive on
every run, not only at initial cert provisioning.
210 lines
5.4 KiB
YAML
210 lines
5.4 KiB
YAML
---
|
|
- name: Load OS-specific variables
|
|
ansible.builtin.include_vars: "{{ item }}"
|
|
with_first_found:
|
|
- "{{ ansible_facts['os_family'] }}.yml"
|
|
- debian.yml
|
|
|
|
- name: Set nginx_user if not already set
|
|
ansible.builtin.set_fact:
|
|
nginx_user: "{{ nginx_user | default('www-data') }}"
|
|
|
|
- name: Add Nginx official APT signing key (Debian/Ubuntu)
|
|
ansible.builtin.get_url:
|
|
url: https://nginx.org/keys/nginx_signing.key
|
|
dest: /etc/apt/keyrings/nginx-archive-keyring.asc
|
|
mode: "0644"
|
|
when:
|
|
- ansible_facts['os_family'] == 'Debian'
|
|
|
|
- name: Add Nginx official repository (Debian/Ubuntu)
|
|
ansible.builtin.deb822_repository:
|
|
name: nginx-official
|
|
types: deb
|
|
uris: http://nginx.org/packages/mainline/debian/
|
|
suites: "{{ ansible_facts['distribution_release'] }}"
|
|
components: nginx
|
|
signed_by: /etc/apt/keyrings/nginx-archive-keyring.asc
|
|
state: present
|
|
when:
|
|
- ansible_facts['os_family'] == 'Debian'
|
|
|
|
- name: Install nginx
|
|
ansible.builtin.package:
|
|
name: nginx
|
|
state: present
|
|
|
|
- name: Install nginx stream module (Debian)
|
|
ansible.builtin.package:
|
|
name: libnginx-mod-stream
|
|
state: present
|
|
when:
|
|
- ansible_facts['os_family'] == 'Debian'
|
|
- nginx_forwarder is defined
|
|
- nginx_forwarder | length > 0
|
|
|
|
- name: Install Certbot
|
|
ansible.builtin.package:
|
|
name: certbot
|
|
state: present
|
|
when: acme_email is defined
|
|
|
|
- name: Enable Certbot renewal timer
|
|
ansible.builtin.systemd:
|
|
name: "{{ certbot_timer }}"
|
|
enabled: true
|
|
state: started
|
|
when: acme_email is defined
|
|
|
|
- name: Remove default nginx vhost (Arch ships one that conflicts)
|
|
ansible.builtin.file:
|
|
path: "{{ nginx_conf_dir }}/default.conf"
|
|
state: absent
|
|
notify: Reload nginx
|
|
|
|
- name: Ensure nginx conf.d directory exists
|
|
ansible.builtin.file:
|
|
path: "{{ nginx_conf_dir }}"
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Ensure nginx streams.d directory exists
|
|
ansible.builtin.file:
|
|
path: "{{ nginx_streams_dir }}"
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Configure catch-all default_server
|
|
when: nginx_default_server_enabled
|
|
block:
|
|
- name: Ensure nginx ssl directory exists
|
|
ansible.builtin.file:
|
|
path: "{{ nginx_default_ssl_cert | dirname }}"
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Generate self-signed cert for default_server
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
openssl req -x509 -nodes -newkey rsa:2048
|
|
-keyout {{ nginx_default_ssl_key }}
|
|
-out {{ nginx_default_ssl_cert }}
|
|
-days 3650 -subj "/CN=default"
|
|
creates: "{{ nginx_default_ssl_cert }}"
|
|
|
|
- name: Restrict permissions on default_server key
|
|
ansible.builtin.file:
|
|
path: "{{ nginx_default_ssl_key }}"
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
|
|
- name: Deploy default_server vhost
|
|
ansible.builtin.template:
|
|
src: default-server.conf.j2
|
|
dest: "{{ nginx_conf_dir }}/00-default.conf"
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
- name: Remove default_server vhost when disabled
|
|
ansible.builtin.file:
|
|
path: "{{ nginx_conf_dir }}/00-default.conf"
|
|
state: absent
|
|
when: not nginx_default_server_enabled
|
|
notify: Reload nginx
|
|
|
|
- name: Ensure Certbot webroot directory exists
|
|
ansible.builtin.file:
|
|
path: /var/www/certbot
|
|
state: directory
|
|
owner: "{{ nginx_user }}"
|
|
group: "{{ nginx_user }}"
|
|
mode: "0755"
|
|
when: acme_email is defined
|
|
|
|
- name: Deploy nginx main configuration
|
|
ansible.builtin.template:
|
|
src: nginx.conf.j2
|
|
dest: /etc/nginx/nginx.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
validate: nginx -t -c %s
|
|
notify: Reload nginx
|
|
|
|
- name: Deploy stream forwarder configurations
|
|
ansible.builtin.template:
|
|
src: forwarder.conf.j2
|
|
dest: "{{ nginx_streams_dir }}/forwarder-{{ domain | replace('.', '_') }}.conf"
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
loop: "{{ nginx_forwarder | dict2items }}"
|
|
loop_control:
|
|
loop_var: item
|
|
vars:
|
|
domain: "{{ item.key }}"
|
|
config: "{{ item.value }}"
|
|
when:
|
|
- nginx_forwarder is defined
|
|
- nginx_forwarder | length > 0
|
|
notify: Reload nginx
|
|
|
|
- name: Validate nginx configuration after stream forwarder deployment
|
|
ansible.builtin.command: nginx -t
|
|
changed_when: false
|
|
when:
|
|
- nginx_forwarder is defined
|
|
- nginx_forwarder | length > 0
|
|
|
|
- name: Deploy logrotate configuration for nginx
|
|
ansible.builtin.template:
|
|
src: logrotate-nginx.j2
|
|
dest: /etc/logrotate.d/nginx
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
when: nginx_log_backend == 'file'
|
|
|
|
- name: Remove logrotate configuration when using journald
|
|
ansible.builtin.file:
|
|
path: /etc/logrotate.d/nginx
|
|
state: absent
|
|
when: nginx_log_backend == 'journald'
|
|
|
|
- name: Allow HTTP traffic through firewall
|
|
community.general.ufw:
|
|
rule: allow
|
|
port: "80"
|
|
proto: tcp
|
|
comment: Nginx HTTP
|
|
retries: 5
|
|
delay: 2
|
|
register: ufw_result
|
|
until: ufw_result is succeeded
|
|
|
|
- name: Allow HTTPS traffic through firewall
|
|
community.general.ufw:
|
|
rule: allow
|
|
port: "443"
|
|
proto: tcp
|
|
comment: Nginx HTTPS
|
|
retries: 5
|
|
delay: 2
|
|
register: ufw_result
|
|
until: ufw_result is succeeded
|
|
|
|
- name: Enable and start nginx service
|
|
ansible.builtin.systemd:
|
|
name: nginx
|
|
enabled: true
|
|
state: started
|