Writeup | Networking & Security

Guest VLAN Captive Portal Enforcement

Built and validated a guest network that forces new clients through an OPNsense captive portal, gives them an isolated guest DHCP lease, allows internet access only after acceptance, and blocks internal management, storage, and service VLANs.

OPNsense Captive Portal Guest VLAN DHCP / DNS Firewall Isolation 802.1Q Trunking Validation
Public-safe guest captive portal architecture diagram

STAR Breakdown

  • Situation: The homelab needed a real guest network, not just a separate WiFi name. Guest clients should get internet access, but they should not be able to reach Proxmox, OPNsense management, NAS storage, internal apps, or other private VLANs.
  • Task: Build an OPNsense-backed captive portal for the Guest VLAN, validate that fresh clients hit the portal, and prove that DHCP, DNS, portal sessions, and firewall isolation all line up.
  • Action: Verified switch trunking and UniFi SSID tagging, fixed the captive portal zone binding from the wrong interface to the actual Guest interface, cleaned stale DHCP artifacts, aligned the DHCP lease with the portal hard timeout, and cleared old bypass sessions.
  • Result: A fresh mobile client joined the Guest SSID, received a guest lease, hit the portal, authenticated as an anonymous guest session, and retained only internet access. Stale VLAN 9 bypass sessions were removed so the portal state matches the intended design.

The Goal

The goal was not only "make guest WiFi work." The goal was to model the way a real office or school guest network should behave: visitors can get online, but the guest network is not trusted. It should be easy for a visitor to connect, easy for the admin to validate, and difficult for a guest client to accidentally or intentionally reach internal infrastructure.

The design uses a dedicated Guest VLAN carried from the firewall through the switching fabric to the access point. OPNsense owns the gateway, DHCP, DNS handling, firewall rules, and captive portal zone. The AP only tags the traffic correctly; OPNsense enforces the policy.


Architecture

The traffic path is simple on purpose:

  1. A phone or laptop joins the Guest SSID.
  2. The AP tags the client into the Guest VLAN.
  3. OPNsense gives the client a Guest DHCP lease and DNS path.
  4. Before authentication, web traffic is intercepted by the captive portal.
  5. After acceptance, the client gets internet access while private/internal destinations remain blocked.

The portal timers are aligned with the client lease model: the captive portal has a 30-minute idle timeout and an 8-hour hard timeout, while the Guest DHCP pool leases for 8 hours. That keeps a guest device from holding a long DHCP lease after the portal session policy has expired.


The Bug: Correct VLAN, Wrong Portal Interface

The hardest part was that most of the configuration looked right. The Guest VLAN existed. The AP SSID was tagged. DHCP could hand out addresses. The portal service was running. The firewall rules existed. But clients could still connect without being forced through the portal.

The root cause was the OPNsense captive portal zone binding. The portal was attached to the Network Services interface instead of the Guest interface. In the UI, labels can make this easy to miss if the interface handle is wrong underneath. The fix was to update the portal zone so Guest_Portal binds to the Guest interface, then reconfigure the captive portal service.

# Public-safe summary of the fix
Guest_Portal.before.interfaces = network-services-interface
Guest_Portal.after.interfaces  = guest-interface

reconfigure captiveportal
verify service status == running

After the binding was corrected, a fresh mobile client on the Guest SSID appeared as an anonymous portal session in the Guest zone.


DHCP and Session Cleanup

The same wrong-interface history also left behind stale DHCP and portal artifacts. There was an old Guest range/options/tag attached to the Network Services interface, plus old bypass sessions created during testing. Those were removed so future troubleshooting would not be polluted by stale state.

Item Final State Why It Matters
Portal zone Bound to Guest interface Captive portal enforcement applies to actual guest clients.
Guest DHCP lease 8 hours Matches the portal hard-timeout window.
Idle timeout 30 minutes Inactive guests age out without waiting all day.
Old bypass sessions Removed Validation reflects real portal behavior, not old test exceptions.

Validation

The validation used both live client behavior and firewall-side evidence:

For a portfolio page, the important point is not the private IP address. The important point is that the control loop is complete: SSID tagging, DHCP, DNS, portal enforcement, session tracking, and firewall isolation all agree.


What This Demonstrates

This project is small compared with a full VLAN migration, but it demonstrates a lot of practical network-administration skill. Captive portals involve multiple layers: wireless controller tagging, switch trunks, DHCP scopes, DNS behavior, firewall rules, portal sessions, and timeout policy. If any one layer points at the wrong interface, the user experience looks like "it just connects."

The fix came from not trusting surface-level labels and checking live state: interface handles, generated DHCP config, active sessions, and service status. That is the same troubleshooting pattern I use across the rest of the homelab.

Related Work

Projects connected to the guest network and firewall design.