Files
homelab/assets/network-topology.md
T
2026-04-26 22:26:14 -04:00

3.4 KiB

Network Topology

Two views of the same network. The trust-tier diagram is how I reason about it. The physical-flow one is for when someone asks "but where does it actually plug in."

Trust tiers and policy

Seven VLANs grouped by how much I trust what's on them. Edges are allowed inter-tier flows; everything else is default-deny.

graph TB
  subgraph UNTRUSTED["Untrusted — internet only, no internal access"]
    GUEST[Guest WiFi]
    IOT[IoT]
    WFH[Work-from-home]
  end

  subgraph PUBLIC["Public-facing"]
    DMZ[DMZ<br/>reverse proxy + public services]
  end

  subgraph TRUSTED["Trusted"]
    LAN[LAN<br/>personal devices]
    INT[Internal services<br/>app stack]
  end

  subgraph MGMT["Management — VPN-only"]
    ADMIN[Hypervisor, firewall,<br/>backup, switches, APs]
  end

  subgraph REMOTE["Remote"]
    VPN[WireGuard clients]
  end

  INTERNET((Internet))

  UNTRUSTED -->|outbound only| INTERNET
  INTERNET -->|HTTP/HTTPS<br/>tight allowlist| DMZ
  INTERNET -->|WireGuard<br/>UDP| VPN

  DMZ -.->|narrow allowlist<br/>firewall-enforced| INT
  LAN -->|consume services| INT
  VPN -->|LAN-equivalent +<br/>admin access| INT
  VPN --> ADMIN

  classDef untrusted fill:#3a1f1f,stroke:#8b3a3a,color:#f0d0d0
  classDef public fill:#3a2f1f,stroke:#8b6b3a,color:#f0e0d0
  classDef trusted fill:#1f3a2f,stroke:#3a8b6b,color:#d0f0e0
  classDef mgmt fill:#1f2f3a,stroke:#3a6b8b,color:#d0e0f0
  classDef remote fill:#2f1f3a,stroke:#6b3a8b,color:#e0d0f0

  class GUEST,IOT,WFH untrusted
  class DMZ public
  class LAN,INT trusted
  class ADMIN mgmt
  class VPN remote

Physical flow

What plugs into what. Tier labels, not addresses.

graph LR
  ISP[ISP] --> GW[Carrier gateway<br/>passthrough mode]
  GW --> FW[pfSense firewall]
  FW --> SW[Managed switch<br/>VLAN-aware]

  SW --> T_MGMT[MGMT tier]
  SW --> T_INT[Internal services tier]
  SW --> T_LAN[LAN tier]
  SW --> T_WFH[WFH tier]
  SW --> T_IOT[IoT tier]
  SW --> T_GUEST[Guest tier]
  SW --> T_DMZ[DMZ tier]

  FW -.->|VPN concentrator| VPN[WireGuard]

Why two reverse proxies, not one

The DMZ-to-internal arrow above does a lot of work, so worth being explicit. There are two Caddy instances:

  • One in the DMZ, internet-facing, fronting a deliberately small set of public services.
  • One in the internal services tier, LAN/VPN only, fronting everything else.

The first version of this was a single Caddy cloned into the DMZ doing both jobs. It "worked," in the sense that nothing was on fire — but every internal admin surface was technically internet-reachable, gated only by app-level auth. Once I drew it out I realized I'd built exactly the thing the DMZ was supposed to prevent. Splitting them was the fix: the DMZ Caddy can only see the small handful of backends it's allowed to reach, the firewall enforces that independently of the proxy config, and VPN clients hit the internal Caddy directly without ever touching the DMZ.

This is the layered-controls principle from SECURITY.md made concrete. Both the proxy and the firewall enforce the same allowlist. Misconfiguring the proxy is way easier than misconfiguring the firewall, so they back each other up.

Notes

  • Inter-tier policy is enforced at the firewall. Intra-tier traffic between hosts on the same bridge does not — see NETWORK.md for why that matters when reasoning about blast radius.
  • Subnets, VLAN IDs, hardware models, and ISP details live in the private repo. The trust tiers are the part worth publishing; the IP plan isn't.