firewall-and-network.md: fresh real evidence for the Docker-bypass gap

Live-verified against the new NetBird host: firewalld genuinely locked
to 3 explicit rich-rules (zero blanket ports), yet 3 container-published
ports (netbird-proxy, netbird-server, hawser) sit completely outside
those rules and are reachable LAN-wide via Docker's own NAT - the exact
gap the doc already documented, now with a second real example on a
host provisioned this same week instead of just the one from before.
This commit is contained in:
thesabear committed 2026-09-01 08:35:28 +03:00
1 parent f69dc87460
commit fa0236016b
1 file changed
+20 -1
+20 -1
View File
@@ -64,6 +64,25 @@ rule family="ipv4" source address="[automation host]" port port="[this host's SS
![Two enforcement layers per packet — pfSense always applies, firewalld only catches native services, Docker-published ports skip straight past it](../assets/firewall-and-network-docker-bypass.svg)
A fresh, real example of exactly this — the mesh overlay's own host in [`docs/netbird-zero-trust.md`](netbird-zero-trust.md), firewalld genuinely locked down to just its explicit rules, and three container-published ports sitting completely outside them:
```
$ firewall-cmd --zone=public --list-ports
[no output — zero blanket-opened ports]
$ firewall-cmd --zone=public --list-rich-rules
rule family="ipv4" source address="[admin workstation]" port port="[this host's SSH port]" protocol="tcp" accept
rule family="ipv4" source address="[automation host]" port port="[this host's SSH port]" protocol="tcp" accept
rule family="ipv4" destination address="[vault host]" port port="8200" protocol="tcp" accept
$ docker ps --format "{{.Names}}: {{.Ports}}"
netbird-proxy: 0.0.0.0:33000->33000/tcp, [::]:33000->33000/tcp
netbird-server: 0.0.0.0:34780->34780/udp, [::]:34780->34780/udp
hawser: 0.0.0.0:2376->2376/tcp, [::]:2376->2376/tcp
```
Three ports, zero matching firewalld rules, all three genuinely reachable LAN-wide via Docker's own NAT — not a hypothetical, the exact gap above on a host provisioned this same week.
## Edge firewall: three jobs, one device
One device, three distinct responsibilities — kept together deliberately, not split into separate boxes, since none of the three is heavy enough on its own to justify another VM to patch and monitor:
@@ -84,4 +103,4 @@ Firewalls aren't the only layer that matters. On this fleet's Debian-family host
Proxmox has its own built-in firewall capability, left with an empty ruleset on purpose. pfSense and firewalld already cover the two layers that matter — public/internal segmentation and per-host minimal exposure — and a third overlapping layer at the hypervisor level would add complexity (a third ruleset to audit, a third place a rule could silently be wrong) without meaningfully improving posture. Recognizing when *not* to add a redundant control is as much a design decision as adding one, and it's the same reasoning that kept the secrets model in [`docs/secrets-and-pki.md`](secrets-and-pki.md) to one trust root instead of two.
---
Related: [`docs/dns-naming.md`](dns-naming.md), [`docs/architecture-overview.md`](architecture-overview.md), [`docs/security-monitoring.md`](security-monitoring.md) · Stacks: [`stacks/platform/caddy/`](../stacks/platform/caddy/), [`stacks/platform/caddy-internal/`](../stacks/platform/caddy-internal/), [`stacks/platform/docker-socket-proxy/`](../stacks/platform/docker-socket-proxy/)
Related: [`docs/dns-naming.md`](dns-naming.md), [`docs/architecture-overview.md`](architecture-overview.md), [`docs/security-monitoring.md`](security-monitoring.md), [`docs/netbird-zero-trust.md`](netbird-zero-trust.md) · Stacks: [`stacks/platform/caddy/`](../stacks/platform/caddy/), [`stacks/platform/caddy-internal/`](../stacks/platform/caddy-internal/), [`stacks/platform/docker-socket-proxy/`](../stacks/platform/docker-socket-proxy/)