Mermaid diagrams up front, GFM alert callouts for key decisions and gotchas, tables instead of bullet walls - replaces plain prose with the visual template established for security-monitoring.md.
60 lines
3.9 KiB
Markdown
60 lines
3.9 KiB
Markdown
# 🧱 Firewall & Network Segmentation
|
|
|
|
> [!NOTE]
|
|
> No blanket "anything on the internal network can talk to anything else on it" rule, anywhere. Every real flow is explicit; everything else is logged and dropped.
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
Internet(["Internet"]) --> Edge
|
|
subgraph Edge["Edge Firewall"]
|
|
Bogon["Bogon block +<br/>IP-reputation blacklist"]
|
|
NonstdSSH["SSH — nonstandard port only"]
|
|
PubProxy["Public reverse-proxy layer"]
|
|
IDS["Inline IDS/IPS"]
|
|
end
|
|
Edge -->|explicit rules only| LAN
|
|
|
|
subgraph LAN["Internal Network"]
|
|
direction TB
|
|
Rule1["DNS · Secrets/PKI · Reverse proxy<br/>Metrics/logs · Security agents<br/>NTP · Admin SSH · File sharing"]
|
|
Catchall["Everything else:<br/>default-deny + log"]
|
|
end
|
|
```
|
|
|
|
## The one rule that matters most
|
|
|
|
Every real service-to-service flow — DNS lookups, secrets/PKI traffic, reverse-proxy access, metrics/log shipping, security-agent reporting, NTP, admin SSH, file sharing, and a handful of specific app-to-app integrations — gets its own explicit allow rule. Anything not explicitly listed hits a default-deny-and-log catch-all. **This catch-all is the fleet's actual lateral-movement boundary**, not any single host's own firewall.
|
|
|
|
> [!TIP]
|
|
> A few explicit rules are narrower than they look 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.
|
|
|
|
## 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, not a hunt through every rule that needs to reach it. Groups nest: a "whole managed fleet" group is composed of several narrower per-role groups, so a rule against the broad group automatically covers new members of any narrower one.
|
|
|
|
## Per-host firewalls: minimal, not blanket
|
|
|
|
Each host's own local firewall opens only the specific ports it actually needs. One host goes further: instead of one open port for its most sensitive service, individual source IPs get their own explicit rule.
|
|
|
|
> [!WARNING]
|
|
> **A real, documented gap**: on Docker hosts, container-published ports bypass the host's own firewall entirely — the container runtime's own network rules are evaluated first. Per-host firewall rules only actually protect the small number of *native*, non-containerized services on that host. The network-level segmentation above (and the edge firewall further out) still applies regardless — defense-in-depth gap, not an open-to-the-world one.
|
|
|
|
## Edge firewall: three jobs, one device
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
EdgeFW["Edge Firewall"] --> J1["Public ingress<br/>bogon block, IP-reputation feed,<br/>nonstandard SSH port"]
|
|
EdgeFW --> J2["Second reverse-proxy layer<br/>for genuinely public apps only,<br/>automated wildcard certs"]
|
|
EdgeFW --> J3["Security tooling<br/>inline IDS/IPS, cert renewal"]
|
|
```
|
|
|
|
> [!IMPORTANT]
|
|
> **Failure impact, thought through in advance**: if the edge firewall goes down, public access and outbound internet 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, left with an empty ruleset on purpose. The edge firewall and each host's own local firewall already cover the two layers that matter — a third overlapping layer would add complexity without meaningfully improving 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/)
|