Skip to content

VLANs

Design Philosophy

Three VLANs segment traffic by trust level and use case. IoT devices get the strictest isolation since they run unknown/research firmware. Trusted devices get full internet access through Mullvad VPN. Lab devices can communicate freely within the VLAN and have controlled outbound access.

VLAN Definitions

VLAN 10 — Lab

Purpose: Infrastructure and compute devices that stay in (or near) the lab.

Property Value
Subnet 192.168.10.0/24
Gateway 192.168.10.1
DNS 192.168.10.1 (router)
Internet Direct (no VPN)
Inter-VLAN → Trusted: blocked. → IoT: blocked.

Devices: Proxmox node, Raspberry Pis, dell-mini, acer-lap, hp-lap (Debian/XFCE)


VLAN 20 — Trusted

Purpose: Personal devices — phones, laptops, daily drivers.

Property Value
Subnet 192.168.20.0/24
Gateway 192.168.20.1
DNS 192.168.20.1 (router)
Internet Switchable: Mullvad (trusted-vpn on) or university WAN (trusted-vpn off)
Inter-VLAN → Lab: blocked by default (allow specific services as needed). → IoT: blocked.

Devices: MacBook M1 Pro, Dell XPS 16, Pixel 6, Pixel 3, iPhone 7 (all on dfair_lab as of 2026-10-08)

VLAN 20 is selectable between VPN and WAN modes using trusted-vpn on Flint 2. VPN-mode routed IPv4 traffic uses Mullvad and a WAN guard; a controlled Mullvad outage test passed. Router cold-boot and alternate-egress audits are still pending. See Mullvad.


VLAN 30 — IoT

Purpose: Research IoT devices with untrusted/experimental firmware.

Property Value
Subnet 192.168.30.0/24
Gateway 192.168.30.1
DNS 192.168.30.1 (router) or dedicated resolver
Internet Blocked by router-side iot → wan forwarding removal on 2026-10-22; client verification pending
Inter-VLAN Fully isolated — no access to Lab or Trusted.

Devices: Research IoT hardware, Samsung phone (for IoT testing), TP-Link OpenWrt AP

IoT devices connect wirelessly to the OpenWrt AP on a dedicated SSID. This AP uplinks to the switch on a VLAN 30 access port.


Inter-VLAN Traffic Policy

flowchart LR
    L10[VLAN 10\nLab]
    L20[VLAN 20\nTrusted]
    L30[VLAN 30\nIoT]
    WAN[🌐 Internet]
    VPN[Mullvad VPN]

    L10 -->|Direct| WAN
    L20 -->|Tunneled| VPN --> WAN
    L30 -.->|Blocked by firewall| WAN

    L10 -. blocked .- L20
    L10 -. blocked .- L30
    L20 -. blocked .- L30
    L20 -.->|"selective allow\n(e.g. Proxmox UI)"| L10
Source Destination Policy
Lab Trusted ❌ Blocked
Lab IoT ❌ Blocked
Trusted Lab ⚠️ Selective (e.g. allow :8006 for Proxmox)
Trusted IoT ❌ Blocked
IoT Lab ❌ Blocked
IoT Trusted ❌ Blocked
Any Internet See per-VLAN policy above

VLAN 30 Internet block (2026-10-22)

Removed iot → wan forwarding on the Flint 2. The active network is br-lan.30 (192.168.30.0/24), firewall zone iot with input and forward policies DROP. fw4 check passed and firewall was reloaded. VLAN 10 and 20 WAN forwardings were preserved. Client-side IPv4/IPv6 connectivity tests remain pending. Router-side DHCP/DNS exception was not changed; see firewall details.

Rollout Plan (Phased)

Earlier attempt at doing all VLANs in one pass on the Flint 2 failed at the switch/router VLAN tagging step. Flint 2 runs stock OpenWrt (not GL.iNet's own firmware), which uses DSA + bridge-vlan-filtering rather than classic swconfig — tagging must be done via br-lan VLAN filtering + per-port tagged/untagged bridge-vlan members, not per-interface .VLANID subinterfaces. Rolling out incrementally to isolate failures to one layer at a time:

  • [x] Phase 1 — Flint 2 as plain main router (flat LAN, no VLANs). Replaces TP-Link as the WAN-facing device. Confirms WAN/NAT/DHCP works before any switch complexity is introduced. Notes: TL-SG108E needed a factory reset (leftover VLAN config from the earlier failed attempt was blocking L2 forwarding entirely — 802.1Q mode is currently Disable). Proxmox is reachable via static 192.168.1.197 (falls inside the .100–.249 DHCP pool — flag for exclusion/reservation later). Acer/HP/Dell-mini debian boxes skipped for now (no network config done on them yet, not blocking).
  • [x] Phase 2a — VLAN 30 (IoT) interface live on the Flint 2. Enabled vlan_filtering on br-lan, tagged lan1 (2.5G uplink) for VLAN 1 + 30, left lan2-lan5 untagged/PVID in VLAN 1. br-lan.1 (192.168.1.1) and br-lan.30 (192.168.30.1) both up, SSH survived the reload. Root cause of every prior lockout on this exact router (GL-MT6000): once vlan_filtering is enabled, the lan interface must bind to the VLAN 1 subinterface (option device 'br-lan.1'), not the raw bridge device (br-lan). Binding to the raw bridge while filtering is active is what caused the "changes apply, device goes unreachable" failure that led to repeated reflashes in the earlier attempt — confirmed as a known GL-MT6000-specific gotcha via the OpenWrt forum, not user error. (See: forum.openwrt.org/t/vlan-config-problems-on-gl-inet-gl-mt6000/200882 and forum.openwrt.org/t/gl-inet-flint-2-gl-mt6000-vlan-best-practices-on-openwrt-snapshot/251673)
  • [x] Phase 2b — DHCP server on iot interface (.100-.249, 12h lease), firewall zone (input/forward DROP by default, explicit allow only for DHCP/DNS-to-router; initial forwarding to wan was removed 2026-10-22). Switch VLAN 30 added via native-VLAN-1 pattern (see above) — zero disruption to existing traffic. Switch port 6 is now VLAN 30 untagged/PVID 30, ready for the IoT AP. (Its VLAN 1 untagged membership was left in place by mistake until 2026-10-08 — removed, port 6 is now VLAN 30 only.) Old TP-Link (15.05.1 "Chaos Calmer", ar71xx — its own switch/VLAN config untouched, only became a dumb AP) reconfigured: LAN static 192.168.30.2, own DHCP server disabled (dhcp.lan.ignore=1), WAN disabled (proto=none), wired into switch port 6. Verified end-to-end: phone joined its OpenWrt SSID, got a 192.168.30.x lease from the Flint 2, and had internet access at that time (prior to the 2026-10-22 WAN block). Phase 2 complete.
  • [x] Phase 3a — VLAN 10 (Lab) and VLAN 20 (Trusted) router-side infra live: br-lan.10 (192.168.10.1), br-lan.20 (192.168.20.1), DHCP scopes (.100-.200), firewall zones (ACCEPT/ACCEPT/ACCEPT, forward to wan only, no cross-zone rules yet). Hostname set to flint2 (was causing tab mix-ups with the identically-prompted TP-Link). No devices migrated yet, no switch access ports assigned — infra only, per the decision to stand up VLANs before moving working devices.
  • [x] Phase 3b — Switch: VLAN 10 + 20 added to 802.1Q table, tagged port 1 only (trunk), no access ports assigned. Verified zero disruption — VLAN 1 and VLAN 30 traffic unaffected.
  • [x] Phase 3c — Device migration done for core infra. dfair_lab SSID live on both radios, bound to VLAN 20. Idle debian boxes (Dell Micro/Acer/HP) switch-migrated to VLAN 10 (no OS network config on them yet — not blocking). Proxmox host + its VM/LXC guests migrated to VLAN 10 — key lesson: the Proxmox host's own vmbr0 IP is independent of each VM/LXC's guest-level network config; migrating the host doesn't migrate the guests. LXCs on ip=dhcp (nas) picked up new leases on reboot automatically; VMs (gha-general-01, monitor-01) needed the same via their own guest OS, reachable only through the Proxmox noVNC console once the host itself was unreachable over the network. Scoped lan/trusted → Lab firewall rules added per-host as needed (proxmox :8006/:22, monitor-01 :3000/:3001 for Grafana/Prometheus, gha-general-01 :22) — see devices/README.md for the full table. gha-general-01 given a static DHCP reservation (was floating DHCP, now pinned to its MAC). MacBook Pro reconnected to dfair_lab (VLAN 20), confirmed. All 10 current IoT devices given static DHCP host reservations (MAC-pinned) so addresses don't drift — see devices/README.md. Completed 2026-10-25: Mullvad selective egress and switchable VLAN 20 kill switch; router cold-boot validation remains outstanding.
  • [ ] Phase 3d — OS-level networking for the 3 idle debian boxes (Dell Micro/Acer/HP), tracked in issue #19. dell-mini and acer-lap done: systemd-networkd (chosen for a config method that works the same whether a box is headless or running a desktop env like HP's XFCE, instead of per-box ifupdown/NetworkManager), DHCP host reservations pinned to 192.168.10.117/.199, lan/trusted → :22 firewall rules added. acer-lap is run closed-lid and headless (rack-mounted laptop, built-in panel disconnected). The lid-switch fix was originally documented but never actually applied — it suspended and dropped off the network once the external display was unplugged. Fixed 2026-10-08 with a drop-in /etc/systemd/logind.conf.d/10-headless.conf (HandleLidSwitch/ExternalPower/Docked, HandleSuspendKey, HandleHibernateKey, IdleAction all ignore), masked sleep/suspend/ hibernate targets, and default target set to multi-user.target; verified with systemd-analyze cat-config and a refused systemctl suspend; survives a reboot. hp-lap done 2026-10-08: was on the IoT AP's WiFi (VLAN 30) — moved to wired enp0s25 via systemd-networkd (.network file matched on MAC), NetworkManager disabled, WiFi rfkill-blocked, DHCP host reservation pinned to 192.168.10.147, Allow-LAN-to-HP-Lap/Allow-Trusted-to-HP-Lap :22 rules added, same logind drop-in + masked sleep targets as acer-lap. Hostname renamed poolTab → hp-lap. Still on Debian 12 (bookworm); upgrade to 13 to match the other boxes is a follow-up.

TODO / Implementation Notes

  • [x] Configure 802.1Q VLAN tagging on TL-SG108E
  • [x] Create VLAN interfaces on GL.iNet Flint 2 (stock OpenWrt — DSA bridge-vlan-filtering via UCI, not LuCI — see Phase 2a note on the GL-MT6000 lockout bug)
  • [x] Create SSID for VLAN 20 on Flint 2 WiFi (dfair_lab, both radios). VLAN 30 uses the old TP-Link's own AP instead of a Flint 2 radio SSID; VLAN 10 is wired-only per design, no SSID needed.
  • [x] Configure DHCP server per VLAN on router (10, 20, 30 all live; static host reservations for known devices)
  • [x] Set up Mullvad WireGuard and optional Trusted VLAN routing (2026-10-25; trusted-vpn on|off)
  • [x] Configure and outage-test nftables WAN egress guard for VLAN 20 (router boot-order test pending)
  • [x] Set up OpenWrt IoT AP with VLAN 30 (simplified from the original "SSID + trunk" plan to a dumb-AP-on-a-single-access-port — no trunking needed on the AP itself since it only ever serves one VLAN)