feat: fix nftables boot ordering — start after tailscaled #80

Closed
opened 2026-03-15 05:07:10 +00:00 by forgejo_admin · 0 comments

Lineage

plan-pal-e-platform → Phase 8 (Network Security Hardening) → Phase 8c (Host Firewall Verification)

Repo

forgejo_admin/pal-e-platform

User Story

As a platform operator
I want nftables to load successfully at boot
So that the host firewall is active after every reboot without manual intervention

Context

The nftables config (/etc/nftables.conf) references iif "tailscale0" to accept all Tailscale traffic. nftables validates interface names at load time — if tailscale0 doesn't exist yet, nft fails hard.

Currently nftables.service starts at sysinit.target (very early), before tailscaled.service creates the tailscale0 interface. Result: nftables service is enabled but failed since March 4. The host has been running with INPUT ACCEPT (no firewall) for 10 days.

The fix is a systemd drop-in deployed via Salt that adds After=tailscaled.service + Wants=tailscaled.service to nftables.service. Salt owns all host config — this is not a manual systemd edit.

Bug reference: bug-nftables-service-running-oneshot (status: done — the service.running→service.enabled fix was already applied, but boot ordering was never addressed).

File Targets

Files the agent should modify:

  • salt/states/firewall/init.sls — add systemd drop-in state, daemon-reload state, update requisite chain

Files the agent should NOT touch:

  • salt/pillar/firewall.sls — rule definitions are correct
  • salt/states/firewall/nftables.conf.j2 — template is correct
  • terraform/ — this is Salt, not Terraform

Acceptance Criteria

  • Salt state deploys /etc/systemd/system/nftables.service.d/after-tailscale.conf with After=tailscaled.service and Wants=tailscaled.service
  • Salt state includes cmd.wait for systemctl daemon-reload triggered by drop-in file changes
  • nftables-service and nftables-reload states require nftables-daemon-reload
  • salt-call state.apply firewall shows 0 failures
  • After reboot: nft list ruleset shows rules (not empty)
  • After reboot: systemctl status nftables shows success (not failed)

Test Expectations

  • salt-call state.apply firewall succeeds with 0 failures
  • Drop-in file exists at expected path with correct content
  • systemctl show nftables.service -p After includes tailscaled.service
  • Run command: sudo salt-call state.apply firewall

Constraints

  • Salt owns all host config — no manual systemd edits
  • Do not modify firewall rules (pillar) or nftables template
  • The actual nft rule loading is a separate manual step with revert timer (not part of this PR)
  • Follow existing Salt state style in init.sls

Checklist

  • PR opened with Closes #N
  • No unrelated changes
  • Salt state syntax validated
  • plan-pal-e-platform — Platform Hardening plan
  • phase-pal-e-platform-network-security — Phase 8 parent
  • bug-nftables-service-running-oneshot — original bug (service.running fix)
### Lineage `plan-pal-e-platform` → Phase 8 (Network Security Hardening) → Phase 8c (Host Firewall Verification) ### Repo `forgejo_admin/pal-e-platform` ### User Story As a platform operator I want nftables to load successfully at boot So that the host firewall is active after every reboot without manual intervention ### Context The nftables config (`/etc/nftables.conf`) references `iif "tailscale0"` to accept all Tailscale traffic. nftables validates interface names at load time — if tailscale0 doesn't exist yet, `nft` fails hard. Currently `nftables.service` starts at `sysinit.target` (very early), before `tailscaled.service` creates the `tailscale0` interface. Result: nftables service is **enabled but failed** since March 4. The host has been running with `INPUT ACCEPT` (no firewall) for 10 days. The fix is a systemd drop-in deployed via Salt that adds `After=tailscaled.service` + `Wants=tailscaled.service` to nftables.service. Salt owns all host config — this is not a manual systemd edit. Bug reference: `bug-nftables-service-running-oneshot` (status: done — the service.running→service.enabled fix was already applied, but boot ordering was never addressed). ### File Targets Files the agent should modify: - `salt/states/firewall/init.sls` — add systemd drop-in state, daemon-reload state, update requisite chain Files the agent should NOT touch: - `salt/pillar/firewall.sls` — rule definitions are correct - `salt/states/firewall/nftables.conf.j2` — template is correct - `terraform/` — this is Salt, not Terraform ### Acceptance Criteria - [ ] Salt state deploys `/etc/systemd/system/nftables.service.d/after-tailscale.conf` with `After=tailscaled.service` and `Wants=tailscaled.service` - [ ] Salt state includes `cmd.wait` for `systemctl daemon-reload` triggered by drop-in file changes - [ ] `nftables-service` and `nftables-reload` states require `nftables-daemon-reload` - [ ] `salt-call state.apply firewall` shows 0 failures - [ ] After reboot: `nft list ruleset` shows rules (not empty) - [ ] After reboot: `systemctl status nftables` shows success (not failed) ### Test Expectations - [ ] `salt-call state.apply firewall` succeeds with 0 failures - [ ] Drop-in file exists at expected path with correct content - [ ] `systemctl show nftables.service -p After` includes `tailscaled.service` - Run command: `sudo salt-call state.apply firewall` ### Constraints - Salt owns all host config — no manual systemd edits - Do not modify firewall rules (pillar) or nftables template - The actual nft rule loading is a separate manual step with revert timer (not part of this PR) - Follow existing Salt state style in `init.sls` ### Checklist - [ ] PR opened with `Closes #N` - [ ] No unrelated changes - [ ] Salt state syntax validated ### Related - `plan-pal-e-platform` — Platform Hardening plan - `phase-pal-e-platform-network-security` — Phase 8 parent - `bug-nftables-service-running-oneshot` — original bug (service.running fix)
forgejo_admin 2026-03-15 05:09:49 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
forgejo_admin/pal-e-platform#80
No description provided.