Open Source Home Network
The Foundation
Your ISP gave you a router. It works for your ISP.
This is the stack that works for you instead.
Why Replace Your ISP Router?
- Your ISP router reports back to your ISP
- Connected devices, usage patterns, DNS queries — all visible upstream by design
- Firmware you didn't choose, can't audit, and can't update on your own schedule
- Remote management ports your ISP can reach — and anyone who finds them can too
- Consumer routers are built to a price point, not a security standard
- Security patches stop after 12–18 months — your router runs unpatched for years after that
- No real VLAN support, no per-device firewall rules, no traffic visibility or logging
- "Guest WiFi" that doesn't actually isolate the guest from your LAN in practice
- Open source gives you the actual controls
- You choose what runs, what updates, what phones home — the answer to the last one is nothing
- Real VLAN segmentation: IoT devices cannot reach your NAS, full stop
- Per-device firewall rules, traffic shaping, DNS control, VPN, IDS — all in one box you own
- The two-device stack: OpenWRT + OPNsense
- OpenWRT: the access layer — manages WiFi radios, SSIDs, and VLANs at the AP
- OPNsense: the routing and firewall layer — all policy, all security lives here
- Split by function: easier to reason about, easier to upgrade each independently
- Cost is lower than you think
- OpenWRT AP: GL.iNet travel router (~$40–80) or flash your existing router for free
- OPNsense box: old mini PC + dual NIC (~$50–100) or Protectli Vault (~$200)
- Less than one year of a "security" subscription router that does a fraction of this
- What you get that your ISP router cannot provide
- Full visibility into every device, every connection, every DNS query on your network
- Firewall rules your IoT lightbulbs can't bypass, no matter what firmware they run
The Two-Device Architecture
[ Devices — phones, laptops, IoT, guests ]
|
| WiFi / Ethernet
▼
┌─────────────────────────────────────┐
│ OpenWRT Access Point │
│ │
│ SSID: Home → VLAN 10 │
│ SSID: IoT → VLAN 20 │
│ SSID: Guest → VLAN 30 (OpenNDS) │
│ SSID: 🎯emoji → VLAN 40 (rotating) │
└──────────────┬──────────────────────┘
│ Tagged trunk (all VLANs on one cable)
│ "Router on a stick"
▼
┌─────────────────────────────────────┐
│ OPNsense Router │
│ │
│ ├── VLAN interfaces + DHCP pools │
│ ├── Firewall rules per VLAN │
│ ├── Unbound DNS (per-VLAN views) │
│ ├── Caddy reverse proxy │
│ ├── WireGuard VPN gateway │
│ ├── Suricata IDS/IPS │
│ └── ntopng traffic monitor │
└──────────────┬──────────────────────┘
│
▼
[ Your ISP / Internet ]
- One cable carries all VLANs between OpenWRT and OPNsense — 802.1Q tagged trunk
- OPNsense owns all routing decisions: inter-VLAN traffic must pass through it
- IoT VLAN cannot reach Home VLAN unless OPNsense explicitly allows it — it doesn't
OpenWRT — DSA VLAN Segmentation
Source: blog.holtzweb.com/posts/openwrt-dsa-networking-vlans-with-opnsense/
What DSA Is and Why It Matters
- Distributed Switch Architecture — the new OpenWRT switch management model (OpenWRT 21+)
- Replaces the old
switch section in LuCI — older guides showing a "Switch" tab are outdated
- Router on a stick: OpenWRT is the AP, OPNsense is the router — one cable carries all VLANs tagged
- OPNsense defines the VLANs, IP ranges, and DHCP pools — OpenWRT just relays them upstream
Setting It Up in OpenWRT (DSA)
- Create a bridge device — select the interface plugged into OPNsense as a member
- Enable Bridge VLAN filtering on that bridge
- Bridge VLAN filtering tab: set VLAN IDs, mark uplink port as tagged, device-facing ports as untagged
- Create a separate interface per VLAN using DHCP client mode, bound to
br-lan.VLANID
- Assign each WiFi SSID to its corresponding VLAN interface in the wireless config
On the OPNsense Side
- Interfaces → Other Types → VLAN — create one per VLAN ID on the trunk interface
- Assign each VLAN interface, enable DHCP server per VLAN with its own subnet and range
- Firewall rules: default is no inter-VLAN communication — add only what you explicitly need
- Result: IoT VLAN gets internet, cannot ping or scan anything on Home VLAN — OPNsense enforces this
OpenWRT — SSID Separation and Client Isolation
SSID Layout and Purpose
| SSID |
VLAN |
Purpose |
Internet |
Sees LAN? |
| Home |
10 |
Trusted devices — phones, laptops, desktops |
Yes |
Yes |
| IoT |
20 |
Smart TVs, bulbs, plugs, printers |
Yes |
No |
| Guest |
30 |
Visitors — hits captive portal first |
Yes |
No |
| Select |
40 |
Rotating credentials — contractors, select access |
Yes |
No |
Client Isolation — Devices on the Same SSID
- Without client isolation: two devices on the same SSID can talk to each other directly at L2
- With client isolation: each device is isolated — they can only reach the router (gateway), not each other
- OpenWRT: Wireless → Interface → Advanced Settings → Isolate Clients → checked
- Essential on Guest and IoT: you don't want your Roomba scanning your printer
- Your Roomba is fully capable of scanning your printer — IoT firmware is not to be trusted
Why This Matters for IoT Specifically
- IoT devices run old firmware with no update path — one compromised bulb is the beachhead for an attacker
- VLAN isolation: the compromised device can reach the internet (to phone home) and nothing else on LAN
- Client isolation: the compromised device can't even see other IoT devices on the same SSID
- ntopng will show you exactly what your IoT devices are calling — then decide if that's acceptable
- Band steering: same SSID on 2.4GHz and 5GHz — both tied to same VLAN, IoT devices join 2.4GHz naturally
OpenWRT — Captive Portal with OpenNDS
Source: holtzweb.com/openwrt-presentation.html
What a Captive Portal Does and Why
- Intercepts all HTTP traffic from new clients and redirects them to a splash page before internet access
- Client must accept terms, enter a code, or authenticate — same system hotels and coffee shops run
- Forces acknowledgment of acceptable use, logged with timestamp — useful for events and workshops
- Session timeouts: guest access expires automatically — you don't have to revoke passwords
- Bandwidth limits per client: your guests can't saturate your uplink during a workshop
OpenNDS Setup
- Lightweight, runs directly on OpenWRT — no external server needed:
opkg update && opkg install openNDS
- Bind OpenNDS to your guest VLAN interface only — does not affect other SSIDs
gatewayname — appears on the splash page header
maxclients — hard cap on simultaneous guest connections (useful for event management)
sessiontimeout — auto-expire sessions after N minutes (set 0 for no timeout)
downloadlimit / uploadlimit — bandwidth cap per client in kbps
Customization and Advanced Auth
- Default template lives in
/etc/openNDS/htdocs/ — edit splash.html for your logo and terms
- Add a voucher code field: guests enter today's code, OpenNDS validates before granting access
- FAS (Forward Authentication Service): hands off auth to an external server
- Use case: event access codes, time-limited vouchers, integration with booking or ticketing systems
- External server returns a token; OpenNDS grants or denies based on the response
OpenWRT — Rotating SSID and Password
Source: blog.holtzweb.com/posts/turn-network-services-off-after-business-hours/
The Concept and Use Cases
- One SSID with credentials that automatically rotate on a schedule — old credentials stop working
- New SSID name and password generated, applied, and emailed to all authorized recipients automatically
- Devices not re-added each cycle lose access — useful for contractors, short-term access, events
- No manual credential distribution: the cron job sends the email, recipients just connect
How the Script Works
- Downloads a list of candidate SSID names — most begin with an emoji character
- Extracts password candidates from a Voltaire novel — large natural-language corpus, memorable
- Filters by configured min/max character length — usable but not trivially short
- Updates OpenWRT via
uci set wireless.@wifi-iface[N].ssid and .key
- Runs
wifi reload to apply changes live — no reboot required
- Sends summary email via Brevo API: "Today's credentials: 🎯weird-ssid / correcthorsebattery"
- Swap Brevo for any transactional email API — Mailgun, SendGrid, Postmark all work identically
Why Emoji SSIDs?
- Makes the network visually distinct in any WiFi list — harder to spoof convincingly
- Slightly confusing to passive WiFi scanners — which is a feature, not a bug
- Schedule via cron: daily / weekly / monthly depending on paranoia level
- Combine with after-hours WiFi shutdown: SSID down entirely outside business hours, rotating when up
OPNsense — Advanced Firewalling
Source: blog.holtzweb.com/tags/opnsense/
Stateful Firewall Fundamentals
- Consumer router: "allow everything outbound, block unsolicited inbound" — that's the entire ruleset
- OPNsense: stateful packet inspection on every interface, every direction, full logging per rule
- Tracks connection state: new, established, related, invalid — rules match on any combination
- Default policy is configurable per interface — start with deny-all, add allow rules explicitly
- Floating rules: apply across all interfaces simultaneously — use for global policies and IP blocklists
Inter-VLAN Rules — The Critical Part
- By default in OPNsense: VLANs cannot communicate with each other — you add only what you need
- "Home VLAN can reach NAS on port 445" — one explicit rule, everything else is dropped silently
- IoT VLAN: allow rule for WAN only — it cannot initiate connections to Home, LAN, or other VLANs
- Drop, not reject: attackers get no information about whether a host exists behind the firewall
Aliases — Named Groups for Clean Rules
- Instead of one rule per IP: create alias "HomeDevices" containing all trusted IPs
- Rules reference the alias — add a device to the alias, all associated rules update automatically
- Aliases can be: IP lists, network ranges, port groups, or URLs (auto-refreshed for blocklists)
- IP blocklists: one alias pointing at Firehol or Spamhaus, one block rule — 50,000 IPs covered
- Firewall → Log Files → Live View: watch traffic hit your rules in real time alongside ntopng
OPNsense — Reverse Proxy
Source: blog.holtzweb.com/posts/opnsense-nginx-waf-failover-with-caddy-and-traefik/
What a Reverse Proxy Does in Your Home Network
- Multiple self-hosted services: Nextcloud, Jellyfin, Home Assistant, Vaultwarden, Gitea
- One external IP, one port 443 — the proxy routes each request by hostname to the right service
- Valid HTTPS certificates for all services — no browser certificate warnings, no self-signed exceptions
- No service listens on a public port directly — only the proxy does, reducing your attack surface
Caddy on OPNsense — the Current Recommendation
- First-class OPNsense plugin as of 2025 — installed and managed directly from the GUI
- Automatic HTTPS with Let's Encrypt built in — no separate ACME plugin, no manual cert wiring
- Simple declarative config:
cloud.yourdomain.com → Nextcloud, media.* → Jellyfin, done
- Feature-rich: reverse proxy, static files, ACME, basic auth, headers, rewrite rules — all in one
- Actively maintained and responsive to bug reports — support is real and ongoing
A Note on HAProxy
- HAProxy (
os-haProxy) was the go-to OPNsense reverse proxy for years — it works, but the plugin is effectively unmaintained
- Bugs accumulated with no resolution path; the plugin lags OPNsense releases and WAF integration was painful
- If you have existing HAProxy configs they will continue to function — but for new setups, use Caddy
- Full writeup on why the switch happened: blog.holtzweb.com/posts/opnsense-with-haproxy-and-lets-encrypt/
Advanced: Caddy → Nginx WAF → Traefik Failover
- Caddy sits at the front — terminates TLS, handles ACME, routes to backends
- Nginx + ModSecurity WAF inspects requests for SQLi, XSS, path traversal before they reach your services
- Traefik behind the WAF auto-discovers services via Docker labels — zero manual config per new container
- Failover: if the WAF is unreachable, traffic routes around it — service stays up, WAF failure is not an outage
- Full stack walkthrough: blog.holtzweb.com/posts/opnsense-nginx-waf-failover-with-caddy-and-traefik/
OPNsense — Traffic Flows and Unbound DNS Views
Per-VLAN Routing and Traffic Shaping
- Policy-based routing: different VLANs routed through different gateways — per-interface rules
- IoT VLAN → direct internet (no VPN overhead for your lightbulbs)
- Home VLAN → WireGuard exit for privacy on all devices automatically
- Guest VLAN capped at 20Mbps — guests can't saturate your uplink during a workshop
- IoT VLAN rate-limited — if something is exfiltrating, it's at least doing it slowly
- Priority queues: VoIP traffic gets low-latency scheduling regardless of concurrent activity
Unbound DNS and Split-Horizon Views
- OPNsense runs Unbound as DNS resolver — all devices receive it automatically via DHCP
- Local DNS records:
nas.home → your NAS IP, media.home → Jellyfin — no IP memorization needed
- DNSSEC validation: reject DNS responses without valid signatures — prevents cache poisoning
- DNS-over-TLS forwarding: encrypt queries to upstream resolver — ISP can't log them at transit
- Unbound Views (blog.holtzweb.com: Unbound Views in OPNsense to Resolve Domains by VLAN/Subnet):
- Home VLAN asks
cloud.yourdomain.com → gets internal IP (stays on LAN, never leaves router)
- IoT VLAN asks
cloud.yourdomain.com → gets NXDOMAIN (can't even see the service exists)
- Guest VLAN asks any internal hostname → blocked — guests see only external internet
- DNSCrypt (blog.holtzweb.com: OPNsense Unbound Multi-site Split-DNS with DNSCrypt):
- Encrypts and authenticates DNS queries from OPNsense to upstream — prevents ISP interception
OPNsense — WireGuard VPN Gateway
Source: blog.holtzweb.com/tags/opnsense/ — WireGuard Server + Site-to-Site VPN
Remote Access — Your Phone and Laptop Back to Home
- VPN → WireGuard → Local tab: create the server, assign an IP range for VPN clients
- Add a peer per client device: each gets its own public key and assigned IP in the VPN range
- Firewall rule: allow WireGuard interface → Home VLAN — clients on VPN reach everything Home can
- Client: install WireGuard app, scan QR code from OPNsense — done, connected in under 2 minutes
- When traveling: all traffic routes through your home OPNsense — your DNS blocklists, your firewall
Site-to-Site — Two Locations, One Network
- Both OPNsense boxes run a WireGuard instance — each is a peer of the other
- Each knows the other's public key and endpoint IP — routes configured per subnet
- "To reach 192.168.2.0/24, use the WireGuard tunnel" — transparent routing between sites
- Traffic between sites: the internet sees only WireGuard UDP packets on one port
Why WireGuard Over OpenVPN
- ~4,000 lines of code vs ~100,000 for OpenVPN — dramatically smaller attack surface to audit
- Modern cryptography: ChaCha20, Poly1305, Curve25519 — faster than OpenVPN's legacy cipher stack
- Reconnects instantly after network changes: switching WiFi, waking from sleep, no re-auth dance
- Split tunnel: only traffic destined for home network uses VPN — local internet stays local and fast
- Kill switch: OPNsense gateway group configuration — block WAN traffic if VPN drops, zero leaks
OPNsense — Suricata IDS/IPS
Deployment and Rule Sets
- IDS mode: alert and log without blocking — start here for a week before switching to IPS
- IPS mode: drops matching packets — effective once you've tuned out false positives
- Enable on WAN (stop inbound threats) and LAN (catch compromised devices calling out)
- ET Open (Emerging Threats) — free, updated daily, covers malware C2, exploit kits, scanners
- Abuse.ch Feodo / URLhaus — botnet C2 IPs and domains updated in near-real-time
- ET Pro — paid, lower false positive rate, more comprehensive coverage for production use
What Suricata Catches That Firewalls Miss
- Known malware C2 over HTTPS on port 443 — certificate is valid, JA3 fingerprint is flagged
- DNS exfiltration: data encoded in subdomain strings —
a2f3b1.exfil.bad.com — dedicated rules
- Port scanning originating from inside your LAN — a device is compromised and mapping your network
- Exploit kit traffic patterns: specific URI structures, headers, response sequencing
- Cryptocurrency mining pool connections — distinct protocol fingerprints, easy to detect and block
JA3 Fingerprinting — How It Sees Encrypted C2
- TLS handshakes expose a fingerprint: which cipher suites and extensions the client offers
- Malware families use the same compiled TLS library → same JA3 hash on every infected machine
- Suricata: "this HTTPS connection to a valid CDN has a JA3 matching known Emotet behavior"
- No decryption required — the handshake pattern alone identifies the malware family
- Tuning: suppress false positives for Steam, game launchers, CDNs — then switch categories to IPS
The Complete Open Source Home Network Stack
INTERNET
|
▼
[ OPNsense WAN ]
|
├── Suricata IDS/IPS ← inspect all inbound/outbound
├── IP Blocklists (Aliases) ← drop known bad at border
|
▼
[ OPNsense Routing & Policy ]
├── VLAN 10 — Home ──────────── Full LAN access
│ └── Unbound DNS view: internal hostnames resolve
├── VLAN 20 — IoT ───────────── Internet only · 10Mbps cap
│ └── DNS view: internal domains → NXDOMAIN
├── VLAN 30 — Guest ─────────── Internet only · captive portal
│ └── OpenNDS: splash page, bandwidth limit, session timeout
├── VLAN 40 — Select ────────── Internet only · rotating SSID
│ └── Cron: new SSID+PSK daily via Brevo email API
├── WireGuard ───────────────── Remote access from anywhere
├── Caddy ───────────────────── Self-hosted services + HTTPS
├── ntopng ─────────────────── Visibility into everything above
└── Unbound + blocklists ───── Block before resolve, all VLANs
|
▼
[ OpenWRT Access Point ]
├── SSID: Home → VLAN 10 (trusted)
├── SSID: IoT → VLAN 20 (client isolated)
├── SSID: Guest → VLAN 30 (client isolated + OpenNDS)
└── SSID: 🎯emoji → VLAN 40 (rotates daily via cron)
- No device can see another VLAN without an explicit firewall rule — the default is deny
- All DNS is filtered for every device on every VLAN simultaneously — no exceptions
- Malware behavior is detected even over encrypted connections via JA3 fingerprinting
- You can connect securely from anywhere in the world via WireGuard back to your home network
Resources
| Topic |
Link |
| DSA VLANs with OPNsense |
blog.holtzweb.com/posts/openwrt-dsa-networking-vlans-with-opnsense/ |
| OpenWRT captive portal + isolation |
holtzweb.com/openwrt-presentation.html |
| Rotating SSID script |
blog.holtzweb.com/posts/turn-network-services-off-after-business-hours/ |
| All OPNsense posts |
blog.holtzweb.com/tags/opnsense/ |
| Caddy + Nginx WAF + Traefik |
blog.holtzweb.com/posts/opnsense-nginx-waf-failover-with-caddy-and-traefik/ |
| WireGuard server + site-to-site |
blog.holtzweb.com — OPNsense as a WireGuard Server |
| Unbound DNS Views per VLAN |
blog.holtzweb.com — Unbound Views in OPNsense |
| Nginx WAF + Caddy + Traefik failover |
blog.holtzweb.com — Transparent Nginx WAF Failover |
| DNSCrypt + Split-DNS |
blog.holtzweb.com — OPNsense Unbound Multi-site Split-DNS |
| Geofiltered IP blocklists |
blog.holtzweb.com — Geofiltered IP Address Blocklists Aggregator |
| OPNsense docs |
docs.opnsense.org |
| OpenWRT hardware table |
openwrt.org/toh |
| GL.iNet routers |
gl-inet.com — OpenWRT pre-installed, travel or home AP |
| Protectli Vault FW4B/FW6 |
protectli.com — fanless x86 purpose-built OPNsense appliance |