deploy: give the containers readable configs and force the nginx reload
valkey.conf was owned by the deploy user with no read for the unprivileged user the valkey image drops to, and the logs htpasswd was unreadable by nginx workers, so /logs failed. Both are readable now. The nginx play forces its handlers and validates the config with nginx -t, so a failure earlier in the run can no longer leave the vhost updated but not reloaded.
Pedro Lucas Porcellis porcellis@eletrotupi.com · 13 days ago
d0ab9930b8d2efe12277e2365410dade4162f9c5 Browse filesParents
723aa5bChanged files
4
Additions
+15
Deletions
-2
ansible/cert.yml
1 1
---
2 2
# ansible/cert.yml
3 3
# Installs uacme and manages certificates as the unprivileged `acme` user
4 4
#
5 5
# Registration is interactive and must be done by hand once: see the debug
9 9
10 10
- name: Provision ACME certificates
11 11
hosts: vps
12 12
become: yes
13 13
become_method: doas
14 +
force_handlers: true
14 15
vars_files:
15 16
- vars/vhosts.yml
16 17
17 18
roles:
18 19
- uacme
ansible/deploy.yml
1 1
---
2 2
# ansible/deploy.yml
3 3
# Publishes the nginx vhost (two-phase around the ACME certificate)
4 4
#
5 5
# ansible-playbook -i inventory/hosts.yml deploy.yml --ask-become-pass
6 6
7 7
- name: Configure nginx
8 8
hosts: vps
9 9
become: yes
10 10
become_method: doas
11 +
# Reload even if an earlier task failed, so a late failure cannot leave the
12 +
# vhost changed but nginx still serving the old config
13 +
force_handlers: true
11 14
vars_files:
12 15
- vars/vhosts.yml
13 16
14 17
roles:
15 18
- nginx
ansible/roles/app/tasks/main.yml
1 1
---
2 2
# roles/app/tasks/main.yml
3 3
# Places the production app config in /srv/app/<env>/
4 4
# The env files are rendered from group_vars (secrets come from the encrypted
5 5
# vault.yml), so there is nothing to fill in by hand anymore
30 30
owner: "{{ deploy_user }}"
31 31
group: "{{ deploy_user }}"
32 32
mode: "0640"
33 33
tags: [app]
34 34
35 +
# The valkey image drops to its own unprivileged user, so this file has to be
36 +
# readable by it. valkey.conf holds no secrets (no password), so 0644 is fine
35 37
- name: Install the valkey configuration
36 38
ansible.builtin.copy:
37 39
src: valkey.conf
38 40
dest: "/srv/app/{{ app_env }}/valkey.conf"
39 41
owner: "{{ deploy_user }}"
40 42
group: "{{ deploy_user }}"
41 -
mode: "0640"
43 +
mode: "0644"
42 44
tags: [app]
43 45
44 46
- name: Install the Vector configuration
45 47
ansible.builtin.copy:
46 48
src: vector.yaml
61 63
- { src: env.j2, dest: .env }
62 64
- { src: env.production.j2, dest: .env.production }
63 65
loop_control:
64 66
label: "{{ item.dest }}"
65 67
tags: [app]
ansible/roles/nginx/tasks/main.yml
1 1
---
2 2
# roles/nginx/tasks/main.yml
3 3
# Publishes the vhost, two-phase around the ACME certificate
4 4
#
5 5
# deploy.yml (this role) will HTTP-only block, then obtain cert via the wrapper then
49 49
- name: Install the logs UI htpasswd file
50 50
ansible.builtin.copy:
51 51
dest: "{{ nginx_logs_htpasswd_file }}"
52 52
content: "{{ nginx_logs_htpasswd }}\n"
53 53
owner: root
54 -
group: root
54 +
# nginx workers run unprivileged and must be able to read this
55 +
group: nginx
55 56
mode: "0640"
56 57
notify: reload nginx
57 58
tags: [nginx]
58 59
59 60
- name: Check whether the certificate exists
96 97
src: site.https.j2
97 98
dest: "{{ nginx_conf_dir }}/{{ vhosts[0] }}.conf"
98 99
mode: "0644"
99 100
notify: reload nginx
100 101
when: nginx_cert_after.stat.exists
102 +
tags: [nginx]
103 +
104 +
- name: Validate the nginx configuration
105 +
ansible.builtin.command:
106 +
cmd: nginx -t
107 +
changed_when: false
101 108
tags: [nginx]
102 109
103 110
- name: Explain the manual step when no certificate is available yet
104 111
ansible.builtin.debug:
105 112
msg: |-
107 114
run this once by hand and then re-run deploy.yml:
108 115
109 116
doas -u acme uacme -v -h /usr/share/uacme/uacme.sh new {{ vhosts[0] }}
110 117
when: not nginx_cert_after.stat.exists
111 118
tags: [nginx]