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

94 lines
3.4 KiB
Markdown

# 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.
```mermaid
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.
```mermaid
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.