~/thilagan-digital
← back to logs

Part 8

Hardening the Core: Implementing Strict VLAN Segmentation and Least-Privilege Firewalls

homelabnetworkingsecurityvlan

By this point in our homelab build, we have automated our Proxmox deployments, watched our SRE agents self-heal node failures, and synced physical telemetry into a local Digital Twin state engine.

But all of that software-defined magic sits on top of physical wires and switches. If the underlying local network is a flat, unsegmented subnet, our entire platform is built on sand.

In this installment, we are shifting our focus to the physical and logical network layers. We are going to walk through the exact process of dismantling our flat local network, implementing a zero-trust 802.1Q VLAN segmentation strategy, and configuring pfSense firewall rules to achieve absolute least-privilege isolation.

Like previous phases, we didn't do this alone. We leveraged a multi-agent workflow: co-designing the topology with Gemini, and drafting Claude to audit our rule matrices for security blind spots.

The Starting Point: Why "Flat" is Fatal

My home lab started like almost everyone's: a flat subnet where trust was implicit. Your personal laptop, your smart TV, your public-facing Docker containers, and your hypervisor management interfaces shared the exact same network segment.

If a single container running on your NAS got compromised, the attacker had immediate, unhindered line-of-sight to:

  • The Proxmox VE Web GUI (8006).
  • Your NAS administration portal.
  • Your local personal devices and family storage backups.

We didn't need to invent a new security paradigm. We just needed to enforce established, enterprise-grade network segmentation on our physical hardware.

Co-Designing the VLAN Map with Gemini

I sat down with Gemini to map out our segmentation topology. To do this cleanly, we had to coordinate our layer-2 physical switch tags with our layer-3 pfSense router interfaces.

We established a four-tier segmentation architecture utilizing 802.1Q VLAN tagging:

VLAN ID Subnet Name Description Access Rules
VLAN 10 MGMT Proxmox hypervisors, physical switches, pfSense Admin. STRICT. Only accessible from VLAN 99 or designated admin Tailscale tags.
VLAN 20 LAB_COMPUTE Virtual Machines, LXC containers, Next.js test beds. Can access the internet; cannot talk to MGMT or NAS directly.
VLAN 30 NAS_STORAGE Custom NAS, SMB/NFS storage arrays. Strictly limited to storage protocols (111, 2049, 445) from specific compute nodes.
VLAN 99 TRUSTED Personal workstations, trusted family devices. Unrestricted outbound access. Inter-VLAN access restricted to essential ports.

Avoiding the Double-NAT Trap

One of the hardest parts of routing this layout was avoiding a Double-NAT setup. Stacking routing tables destroys the peer-to-peer UDP connections that Tailscale relies on for rapid, direct connections.

With Gemini, we mapped out a clean hardware hand-off:

  1. Set the ISP modem to Bridge Mode to pass the raw public IP directly to our pfSense WAN port.
  2. Designate pfSense as the single core authority for DHCP, DNS resolution, and VLAN routing.
  3. Demote our consumer Wi-Fi mesh system to Access Point (AP) Mode, turning off its internal DHCP server to prevent routing loops and IP conflicts.

The DevSecOps Audit: Handing the Rules to Claude

With our VLANs defined, I turned to Claude to act as a senior auditor. I fed it our physical hardware inventory, our subnet routing paths, and our proposed pfSense rule sets.

Claude's job was to hunt down security leaks and edge cases. It flagged two critical vulnerabilities in our initial plan:

The NAS Container Blind Spot

We were running several utility Docker containers directly on the host OS of our storage NAS. Claude pointed out that because the NAS sits in VLAN 30 (the storage zone), any container running on the default Docker bridge could potentially sniff NFS traffic or attempt filesystem privilege escalation to access the root backup shares.

The Fix: We configured strict, user-defined Docker bridges per application stack on the NAS, turned off default bridge routing, and applied local iptables rules directly on the NAS host to block outbound container traffic from leaving their designated application zones.

The Inter-VLAN DNS Leak

We initially planned to let all VLANs query the main pfSense interface for DNS resolution. Claude noted that this would allow an isolated sandbox VM on VLAN 20 to query the DNS resolver cache and map out the hostnames and IPs of our entire private MGMT network.

The Fix: We configured split DNS views in pfSense, forcing non-trusted segments to use public DNS servers (like 1.1.1.1) directly, completely isolating our internal hostname resolution to VLAN 99.

Writing the Firewall Rule Matrix

Using the audited output, we constructed a strict Default Deny posture in pfSense.

Every interface block begins with a block-all rule, and we explicitly poke holes only for verified traffic:

=== pfSense Firewall Rules: LAB_COMPUTE (VLAN 20) ===
[ALLOW] Protocol: TCP | Source: VLAN20_net | Port: * | Destination: WAN_net      | Port: 80, 443  (Internet Outbound)
[ALLOW] Protocol: TCP | Source: VLAN20_net | Port: * | Destination: NAS_STORAGE  | Port: 2049     (Mount NFS Datastore)
[DENY]  Protocol: ANY | Source: VLAN20_net | Port: * | Destination: MGMT_net     | Port: *        (Isolate Hypervisors)
[DENY]  Protocol: ANY | Source: VLAN20_net | Port: * | Destination: TRUSTED_net  | Port: *        (Protect Personal LAN)

Physical Execution and Validation

The division of labor during this network hardening phase was distinct and highly efficient:

  • Gemini handled the initial network layout and routing optimization.
  • Claude audited the firewall rules and generated our step-by-step migration backlog.
  • I handled the physical implementation: running physical lines, assigning trunk ports on the managed switch, configuring pfSense interfaces, and verifying rule changes.

We validated every rule change using a suite of automated nmap and ping checks executed from within our various VM sandboxes.

# Run from a LAB_COMPUTE (VLAN 20) node to verify isolation:
ping -c 3 10.0.10.1    # Target: MGMT Gateway -> Expected: 100% Packet Loss (PASSED)
ping -c 3 1.1.1.1      # Target: Public DNS   -> Expected: 0% Packet Loss (PASSED)

By enforcing strict network boundaries, we have successfully mitigated lateral movement. If an attacker gains access to a development container or a public-facing service, they are completely locked inside their designated VLAN segment — with zero visibility into our core hypervisors or storage backbones.

What's Next?

Now that our underlying network is completely hardened, our next step is to look at long-term observability. In the next post, we will explore how we feed syslog data from pfSense, hypervisor logs from Proxmox, and agent telemetry from AgentOps into a centralized logging pipeline to monitor our secure footprint in real-time.