From fb91ea759e2983c3d1caad59cb60d5038341b2c6 Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Tue, 18 Aug 2026 13:41:23 -0700 Subject: [PATCH] docs(pfi): lesson 10 -- v6 collapses two exposure controls into one Operator's framing, and it is a better argument than the terminology correction that preceded it. Under v4, exposing a host needed two affirmative acts -- a DNAT and an accept rule -- so missing either left the host dark. There is no v4 misconfiguration that exposes an internal host by accident. NAT was load-bearing security whether or not anyone designed it that way. v6 removes the first control entirely. The path exists inherently, so the firewall is the only thing left, and the failure mode inverts from fail-closed to fail-open. Rule-ordering slips, rulesets that silently match only one address family, new VLANs added without policy, and re-delegated prefixes unmatching address-literal rules all become exposure events rather than no-ops. Records the practical consequences: key rules on interface/zone rather than address literals, treat enabling v6 on a segment as requiring policy to exist first, and verify default-deny from off-net rather than by reading the ruleset -- which is lesson 3's assert-the-effective-value discipline applied to firewall policy. Also corrects my own claim from the previous commit that the pending firewall pass was 'smaller' than I had implied. It is not smaller, it is different in kind. --- docs/pfi/ops-lessons-playbook.md | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/pfi/ops-lessons-playbook.md b/docs/pfi/ops-lessons-playbook.md index 5f4a2b3..e8e38d3 100644 --- a/docs/pfi/ops-lessons-playbook.md +++ b/docs/pfi/ops-lessons-playbook.md @@ -271,6 +271,48 @@ address-agnostic; the UniFi end is not. --- +## 10. IPv6 collapses two independent exposure controls into one, and it fails open + +**Rule:** before enabling IPv6 on any segment carrying real hosts, write explicit +default-deny inbound policy for that segment **and verify it from off-net**. +Reading the ruleset is not verification. + +**Why โ€” the asymmetry, which is the part worth internalising.** Under IPv4 with +NAT, exposing an internal host required **two** affirmative acts: a DNAT/port +forward *and* an accept rule. Miss either and the host stays dark. There is no +v4 misconfiguration that accidentally exposes an internal host, because without +the translation there is no path at all. NAT was load-bearing security whether or +not it was designed as such. + +Under IPv6 the path exists inherently โ€” the address is routable from birth. The +firewall is now the *only* control, so two independent things that both had to +succeed become one thing that must not fail. **The failure mode inverts from +fail-closed to fail-open.** + +**Concrete ways it bites:** + +| failure | v4 consequence | v6 consequence | +|---|---|---| +| permissive rule ordered above the deny | harmless, no forward exists | immediate exposure | +| ruleset silently only matches one address family | v4 covered, v6 ungoverned | whole segment on default | +| new VLAN added, firewall not updated | just a VLAN | live on the internet at first RA | +| ISP re-delegates a different prefix | n/a | address-literal rules stop matching | + +**How to apply:** +- Key rules on **interface/zone, not address literals** โ€” a re-delegated prefix + must not be able to silently unmatch a rule. +- Treat "enable v6 on a segment" as a change requiring the policy to exist + *first*, not as a networking toggle followed by cleanup. +- **Verify from outside.** Probe the segment's v6 addresses from off-net and + confirm the denies hold. This is ยง3's "assert the effective value, not the + presence of a substring" applied to firewall policy: a ruleset that *says* + deny is not evidence that packets are dropped. + +Operator position on the ESH fleet (2026-08-19): **no 1:1 inbound pass-through.** +The policy work is writing and proving default-deny, not deciding what to expose. + +--- + ## Superseded claims | date | claim | correction |