From fa0236016b86858eb0cf6c0bdc5417675f7feb66 Mon Sep 17 00:00:00 2001 From: thesabear Date: Tue, 1 Sep 2026 08:35:28 +0300 Subject: [PATCH] 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. --- docs/firewall-and-network.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/firewall-and-network.md b/docs/firewall-and-network.md index c9e5dc0..952fa39 100644 --- a/docs/firewall-and-network.md +++ b/docs/firewall-and-network.md @@ -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/)