Skip to content

Runbook 14 — Validation (full end-to-end test matrix)

Goal: prove the firewall + identity/storage/SSO stack works end-to-end. Run the matrix from a test client on each zone (a Proxmox VM with its NIC tagged to the VLAN under test, or a switch access port set to that VLAN — it DHCPs from the firewall).

Prereqs: runbooks 01–13 applied.


CONSOLIDATED PASS — automatable matrix run from the test client (9150, WORKSTATIONS 10.20.40.101) + fleet hosts. Passed: DHCP/DNS + DNSSEC AD; IPv6 affirmatively off (no v6 addr/route, curl -6 fails); Proton egress for clients (M247) AND servers (Proton AG); inter-zone default-deny (WS→DMZ blocked, WS→SERVERS-SSH blocked, WS→MGMT-host blocked, DMZ→MGMT blocked) + allow-list (WS→SERVERS NFS:2049 open); identity id operator=640400004 consistent; hidden primary answers internally, recursion REFUSED, anonymous AXFR refused (TSIG); origin Caddy serves; edge mechanism (rb17); SSO redirect → auth.${domain}.

⚠️ FINDING — OPNsense web GUI reachable from client zones. https://10.20.10.1 returns "Login | OPNsense" from WORKSTATIONS (the GUI listens on all interfaces; the inter-zone host blocks are otherwise correct — WS→MGMT host and WS→firewall:22 are blocked). FIX: System → Settings → Administration → Listen interfaces = MGMT only CLOSED 2026-07-26: enforced instead via the automation rule seq 91WORKSTATIONS → (self):443 block (fw-automation-rules.json, like the SERVERS seq-90 rule), asserted by the conformance harness (WS canary = a workstation via admin-workstations). GUI reachable from MGMT + wg-admin only; per-zone DNS/NTP self-rules untouched. (the WG admin peer reaches MGMT); also confirm the firewall's SSH is key-only/no-root. Re-test: WS→10.20.10.1:443 should become blocked.

Still hands-on (not automatable from the build host): Proton failover + kill-switch (GUI toggle; mechanism is in the config); external port-scan of the Comcast IP = only UDP 51820 on .69 + no service NAT (the core Phase-1 proof); mDNS cast/print (rb08, physical devices); road-warrior dial-in (admin→MGMT, users→Jellyfin).

Deferred to Phase 2 (by design): real LE certs (origin is on tls internal until the edges make names publicly resolvable), real VPS edges, Authelia MFA.


How to run the matrix

  • Spin up a test client VM on Proxmox with its NIC on the LAN bridge, tagged to the VLAN under test (e.g. tag=40 for WORKSTATIONS), or use a switch access port set to that VLAN. It DHCPs from the firewall (the only DHCP server on the VLANs).
  • Repeat per zone to exercise the inter-zone matrix below.

Test matrix

Connectivity & DHCP/DNS

  • [ ] Client on VLAN 40 gets 10.20.40.10x, gw .1, DNS .1.
  • [ ] dig google.com and dig ipa.${ipa_domain} both resolve.
  • [ ] DNSSEC validating (dig +dnssec AD flag).
  • [ ] IPv6 affirmatively off: "Turn off IPv6" is checked (Interfaces → Settings); a test client has no global IPv6 address and no IPv6 default route (ip -6 route shows none); curl -6 https://google.com fails immediately (no usable v6) and plain curl succeeds — nothing needs -4.

ProtonVPN egress + failover + bypass + kill-switch

  • [ ] WORKSTATIONS client and a SERVERS host: curl ipinfo.io shows a Proton IP (clients + servers both egress via the group).
  • [ ] VPN_BYPASS works: from a client, a Google "what's my IP" check shows your real WAN IP; YouTube loads. The Roku (IOT) plays YouTube.
  • [ ] Failover (LB self-heal): disable one Proton instance → its gateway drops from the load-balance pool within the monitor interval and egress continues via the remaining members (egress IP may change); re-enable → it rejoins.
  • [ ] Kill-switch: disable all Proton instances → non-bypass internet stops (no leak); VPN_BYPASS destinations + inbound reverse proxy still work; re-enable → restored.
  • [ ] Inbound reverse proxy still serves while egress is on Proton (confirms WAN reply-to for inbound is independent of the Proton default).
  • [ ] RFC1918 bypass: WS → fileserver NFS is LAN-speed (not tunneled); states show the flow on the normal table, not a PROTON* interface.

Inter-zone policy (default deny)

  • [ ] WS → SERVERS: IPA + NFS ports OK; other ports blocked.
  • [ ] WS → MGMT: blocked. WS → DMZ-internal: blocked. WS → IOT/GUEST: blocked (except WS→IOT cast/print).
  • [ ] GUEST → any RFC1918: blocked; internet OK; client isolation on.
  • [ ] IOT → anything except DNS/NTP-to-fw, Jellyfin, and 5353-to-WORKSTATIONS: blocked.
  • [ ] DMZ → SERVERS only on the one backend port; DMZ → MGMT/clients blocked.

Discovery (mDNS)

  • [ ] WORKSTATIONS phone discovers Roku → casts Jellyfin; laptop discovers + prints.

Identity / NFS home

  • [ ] id operator consistent UID across FreeIPA, fileserver, and 2 clients.
  • [ ] Login on two workstations → same /home/operator via NFS4 (sec=sys); owner=operator.

Remote access (WireGuard)

  • [ ] From outside: admin peer → reaches MGMT + firewall GUI; user peers → Jellyfin only, MGMT blocked.

Origin services (Phase 1 — internal only; no public inbound)

  • [ ] Each site serves a valid LE cert auto-issued by the origin Caddy → backend; HTTP→HTTPS; HTTP/3 (h3) — tested internally (--resolve cloud.${domain}:443:10.20.30.10, or a temp Unbound override).
  • [ ] No service port-forwards exist (Firewall → NAT → Destination NAT has no 80/443/53/22); external scan of the Comcast IP shows ONLY UDP 51820 on .69 (WG admin) — nothing else inbound. This is the core Phase-1 proof.
  • [ ] Certs issued via DNS-01/RFC2136 (origin Caddy → hidden Bind primary).
  • [ ] Internal resolution: Unbound answers clients, forwards ${ipa_domain} → FreeIPA. Hidden primary: dig @10.20.30.40 ${domain} SOA answers internally, recursion REFUSED, anonymous AXFR refused (TSIG required).

Edge (once a VPS edge exists; see runbook 17)

  • [ ] From the public internet, https://<site> hits the edge IP and the presented cert is the home origin's (TLS passthrough — the edge holds no key).
  • [ ] dig @<edge-ip> ${domain} SOA answers from the edge secondary with the primary's serial (AXFR over the tunnel); wg show on home = live edge handshake. Home Comcast scan still shows only WG.

Identity-aware access (Authelia SSO/MFA)

  • [ ] A protected site redirects to auth.${domain}, requires FreeIPA login + MFA, then loads the app (forward-auth at the origin Caddy — unchanged by edge/origin; the edge only passes the encrypted stream through).
  • [ ] User not in the allowed group is denied; SSO is shared across protected apps.
  • [ ] Direct hits to a protected backend (bypassing Authelia) are blocked at Caddy.

Management posture

  • [ ] No zone except the WG admin peer can reach MGMT.
  • [ ] SSH to firewall: key-only, no root, MGMT-scoped.

Sign-off

When every box is checked, take a fresh config backup (System → Configuration → Backups) and snapshot the FreeIPA/fileserver state. The lab is validated end-to-end.