feat(net_config): safer ufw restart on NAT/forwarding changes

- Replace 'ufw disable && ufw --force enable' single-shot handler with a
  block that dry-runs the ruleset, disables, re-enables, then verifies
  ufw is active. No '&&' short-circuit, so failures are loud instead of
  leaving the host firewall-less.
- Rename handler to 'Restart ufw (ip-forwarding settings changed)' to
  reflect that this is a full restart (required to pick up
  /etc/default/ufw and /etc/ufw/before.rules changes per ufw(8)).
- Add NAT/masquerade tasks: enable ipv4 forwarding, set
  DEFAULT_FORWARD_POLICY=ACCEPT, and write a per-interface *nat block
  in /etc/ufw/before.rules.
- Declare requires_ansible >=2.15 in meta/runtime.yml (handler uses
  block:, supported since 2.12; 2.15 is a safe modern floor).
- README: document Ansible version requirement, port reservation
  rules, and Immich pgvector Q&A.
This commit is contained in:
Clément Désiles
2026-05-29 22:24:16 +02:00
parent 36d6baaecb
commit c9e2ff930c
4 changed files with 93 additions and 0 deletions
+30
View File
@@ -34,8 +34,24 @@ This is a good playground to learn and I encourage you to adapt these roles to y
| static-web | Static website hosting | | static-web | Static website hosting |
| vpn | WireGuard server | | vpn | WireGuard server |
## Port Reservation Rules
Reserved ports that **must not** be used as role defaults:
| Port(s) | Protocol | Reserved for |
| --- | --- | --- |
| 80 | tcp | Nginx |
| 443 | tcp | Nginx |
| 3000-3009 | tcp | Testing |
| 4430 | tcp | Testing |
| 8080 | tcp | Testing |
When adding a new role, pick a default port outside these ranges.
## Requirements ## Requirements
Ansible `>=2.15`
Base tools: Base tools:
```sh ```sh
@@ -110,3 +126,17 @@ Linting:
ansible-lint ansible-lint
npx prettier --write . npx prettier --write .
``` ```
## Q&A
### Immich crash loop: `PostgresError: must be owner of extension vector`
Immich tries to self-update the `pgvector` extension at startup, but its database user is intentionally `NOSUPERUSER`, so the `ALTER EXTENSION vector UPDATE` call fails and the microservices worker exits with code 1.
Fix it on the running host by updating the extension as the `postgres` superuser:
```sh
sudo -u postgres psql -d immich -c 'ALTER EXTENSION vector UPDATE;'
```
The Immich role also runs this automatically on subsequent playbook runs, so re-deployments after a pgvector package upgrade do not require manual intervention.
+2
View File
@@ -0,0 +1,2 @@
---
requires_ansible: ">=2.15"
+23
View File
@@ -0,0 +1,23 @@
---
# UFW must be fully restarted (disable + enable) — not just reloaded — to pick
# up changes in /etc/default/ufw (DEFAULT_FORWARD_POLICY) and the *nat block
# in /etc/ufw/before.rules. See ufw(8) "RULE SYNTAX" → IP forwarding.
- name: Restart ufw (ip-forwarding settings changed)
block:
- name: Validate ufw ruleset before restart (dry-run)
ansible.builtin.command: ufw --dry-run reload
changed_when: false
- name: Disable ufw
ansible.builtin.command: ufw disable
changed_when: true
- name: Enable ufw
ansible.builtin.command: ufw --force enable
changed_when: true
- name: Verify ufw is active after restart
ansible.builtin.command: ufw status
register: ufw_status_after
changed_when: false
failed_when: "'Status: active' not in ufw_status_after.stdout"
+38
View File
@@ -32,3 +32,41 @@
ansible.builtin.set_fact: ansible.builtin.set_fact:
network_reload_required: true network_reload_required: true
when: netdev_result is changed or network_result is changed when: netdev_result is changed or network_result is changed
## Routing & NAT (when interface has forward + masquerade enabled)
- name: Enable IPv4 forwarding
ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: "1"
state: present
sysctl_set: true
reload: true
when:
- interface.ipv4.forward | default(false)
- interface.ipv4.masquerade | default(false)
- name: Set UFW default forward policy to ACCEPT
ansible.builtin.lineinfile:
path: /etc/default/ufw
regexp: "^DEFAULT_FORWARD_POLICY="
line: 'DEFAULT_FORWARD_POLICY="ACCEPT"'
when:
- interface.ipv4.forward | default(false)
- interface.ipv4.masquerade | default(false)
notify: Restart ufw (ip-forwarding settings changed)
- name: Configure NAT masquerade in UFW before.rules for {{ interface.name }}
ansible.builtin.blockinfile:
path: /etc/ufw/before.rules
insertbefore: "^\\*filter"
marker: "# {mark} ANSIBLE MANAGED - NAT {{ interface.name }}"
block: |
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s {{ interface.ipv4.address | ansible.utils.ipaddr('network/prefix') }} -o {{ interface.ipv4.nat_out_interface }} -j MASQUERADE
COMMIT
when:
- interface.ipv4.forward | default(false)
- interface.ipv4.masquerade | default(false)
- interface.ipv4.nat_out_interface is defined
notify: Restart ufw (ip-forwarding settings changed)