# Firewall & Network Segmentation ## No blanket LAN-to-LAN allow The most important rule in the whole model is a negative one: there is no default "anything on the internal network can talk to anything else on it" rule. Every real service-to-service flow — DNS lookups, secrets/PKI traffic, reverse-proxy access, metrics/log shipping, security-agent reporting, NTP sync, admin SSH, file sharing, and a handful of specific app-to-app API integrations — gets its own explicit allow rule. Anything not explicitly listed hits a default-deny-and-log catch-all at the bottom of the ruleset. This catch-all is the fleet's actual lateral-movement boundary, not any single host's own firewall (see below). A few of the explicit rules are narrower than they look at first glance, on purpose — inbound access to the secrets platform's own admin API, for instance, is gated to only the two or three services that ever legitimately need it, not the whole fleet. ## Firewall objects are named groups, not raw addresses Every rule references named groups instead of hardcoded addresses — adding a host to a group is a single membership edit rather than hunting down and updating every rule that needs to reach it. Groups nest: a "the whole managed fleet" group is composed of several more specific per-role groups, so a rule written against the broad group automatically covers new members added to any of the narrower ones. One group is deliberately scoped *narrower* than its name might suggest — it only covers the fleet's actively-managed tier, and anything outside that tier has to be added on purpose if it needs the same access. ## Per-host firewalls: minimal, not blanket On top of the network-level segmentation above, each host's own local firewall opens only the specific ports that host actually needs — not a blanket "allow everything, block nothing" default. One host takes this further: instead of one open port for its most sensitive service, individual source IPs get their own explicit rule, so a compromised unrelated service on the same network still can't reach it. **A real, documented gap, not glossed over:** on Docker hosts, container-published ports bypass the host's own firewall rules entirely — the container runtime's own network rules are evaluated first, so the host firewall only actually protects the small number of *native* (non-containerized) services running there, not the dozens of containerized apps. The network-level segmentation above (and the edge firewall further out) still applies regardless — this is a defense-in-depth gap, not an open-to-the-world one. ## Edge firewall The edge firewall is the fleet's outermost boundary and does three separate jobs: - **Public ingress** — a bogon-address block, an IP-reputation blacklist feed, SSH exposed only on a nonstandard port, and HTTPS/HTTP forwarded to the fleet's public-facing reverse-proxy layer. Nothing else reaches the edge firewall's public interface at all. - **A second, independent reverse-proxy layer** — separate from the internal two-zone model covered in [DNS Naming Model](dns-naming.md), the edge firewall runs its own reverse-proxy package specifically for the small number of apps that are genuinely meant to be public. Each public app gets its own backend rule mapping its public hostname to the correct internal service, with automated wildcard certificate issuance. - **Security tooling** — an inline intrusion detection/prevention engine, the same IP-reputation service driving the WAN blacklist, and automated certificate renewal all run at this layer. **Failure impact, thought through in advance:** if the edge firewall goes down, public access and outbound internet access both go with it — but every internal service that doesn't depend on WAN reachability keeps running normally. The blast radius is deliberately bounded to "the edge," not "the fleet." ## The hypervisor's own firewall: deliberately unused The virtualization platform underneath everything has its own built-in firewall capability. It's left with an empty ruleset on purpose — the edge firewall and each host's own local firewall already cover the two layers that actually matter, and a third overlapping layer would add complexity without meaningfully improving the security posture. Recognizing when *not* to add a redundant control is as much a design decision as adding one. --- Related 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/)