OPNsense at Home, Part 2: Securing the Network


This is part 2 of a 4-part series. Part 1: The Migration covers the hardware and initial setup. Part 3: Connectivity covers dual WAN, WireGuard, and bufferbloat. Part 4: Observability covers the logging and monitoring stack.

DNS security

I run Unbound as the resolver, forwarding upstream to Quad9 (9.9.9.9) via DNS over TLS on port 853. Config is under Services > Unbound DNS > DNS over TLS. Add the upstream IP 9.9.9.9 with hostname dns.quad9.net.

I started with Cloudflare's security resolver (1.1.1.2) for the same purpose, but dropped it after testing. Independent comparisons of resolver threat feed coverage consistently find Cloudflare's filtering underperforms Quad9 in blocking actual malware domains. Cloudflare processes a massive query volume and is optimized for speed; threat intelligence filtering is not their core product. Quad9 is purpose-built for it: they aggregate feeds from over 30 threat intelligence partners including Abuse.ch and IBM X-Force, and the query data is not sold or used for ad targeting. For a home setup where the filtering resolver is the last line of defense, Quad9 blocks more of the things you actually care about.

To prevent clients from bypassing this, two firewall rules on the LAN interface:

  • Block TCP/UDP to any destination except the OPNsense LAN IP on port 53. This stops clients from using their own DNS servers.
  • Block TCP to any destination on port 853. This stops clients from running their own DNS over TLS.

After this, directly querying 9.9.9.9 from a LAN client will hang, which is correct. All DNS goes through OPNsense.

Ad blocking

On the Flint 2, AdGuard Home handled this. It ships as an installable package in the OpenWrt ecosystem, runs as a local DNS server, and supports blocklists out of the box. The setup was straightforward: install, point clients at it, add a few community-maintained lists, done.

OPNsense does not have an official AdGuard Home package. There are community-maintained ports that work, but my rule is to stick to official packages only. Community ports tend to lag on updates, and a stale package sitting between your clients and the internet is exactly the kind of thing that gets exploited and forgotten.

The official alternative is Unbound's built-in blocklist support. Under Services > Unbound DNS > Blocklists, you can add DNSBL sources directly. The coverage is not quite the breadth of a dedicated AdGuard setup with a curated list mix, but it is maintained, it updates automatically, and it requires no additional packages. For most home use it is more than enough.

If you want more aggressive blocking and are willing to manage it yourself, Pi-hole is a reasonable choice on isolated hardware. It gives you per-client statistics and a clean UI for managing lists. The key is to keep it off the firewall itself so a Pi-hole update cannot affect your routing.

IDS/IPS with Suricata

This was the original motivation for the whole migration. Suricata in IPS mode (blocking rather than just alerting) requires disabling hardware offloading, enabling promiscuous mode for VLANs, and giving it enough CPU to actually process traffic. The i5-1235U handles this without visible load.

Rulesets I run: ET Open (Emerging Threats free baseline, daily updates) as the foundation, plus the Abuse.ch Feodo Tracker feed for botnet C2 coverage. The SSLBL Suricata ruleset from Abuse.ch was deprecated in January 2025 and is no longer maintained.

Interface strategy: Suricata on the WAN interface catches inbound and outbound threats against your public-facing traffic. Adding it on LAN catches internal lateral movement. A common combination is Suricata on WAN and Zenarmor on LAN for application-layer visibility, though I have not gone there yet.

EVE JSON logging: enable it in Services > Intrusion Detection > Settings, output to syslog. This flows into the observability stack covered in Part 4.

Security hardening

UPnP. UPnP is the mechanism by which LAN clients can autonomously open inbound port forwards on your firewall. It is the main vector for software on your network to poke holes without you knowing. Check whether it is even installed: System > Firmware > Plugins, search "upnp". If os-miniupnpd is not there, it is not running. If it is, audit the port forwards it has created under Firewall > NAT > Port Forward and look for rules with "UPnP" in the description. My recommendation: do not enable it unless you have a specific need.

Admin interface binding. By default, the OPNsense web UI listens on all interfaces. Bind it to a specific management IP or VLAN under System > Settings > Administration.

SSH. Leave it off. OPNsense has a toggle under System > Settings > Administration to enable SSH, and the right default is disabled. The web UI and console cover everything you need for normal operation. If you need shell access for something specific, enable it, do what you need, and turn it back off. An SSH daemon running 24/7 on your firewall is an unnecessary attack surface for something you use once a month.

On hole punching. There is some confusion about what "hole punching" means. NAT hole punching (used by WebRTC, Discord, Tailscale, and similar) is normal outbound UDP behavior. Your firewall temporarily remembers an outbound connection and lets the response back in. This cannot be blocked without deep packet inspection and it is not a vulnerability. The actual risk to audit is UPnP, which allows inbound connections to be created without any corresponding outbound traffic.

TLS inspection for power users

This one is for people who do things on their machines. You know who you are: running scripts, pulling random packages, testing tools that phone home. You are fully aware of what you are doing, you are not a threat, but you are also the person most likely to accidentally invite something unwanted in while poking around. The threat model is not an external attacker or an untrusted user. It is you, being a power user, and wanting a net under the trapeze.

The approach is the same one your employer uses: a local CA sits between clients and the internet, terminates HTTPS connections, inspects the payload, and re-encrypts outbound. You deliberately install the root CA cert on your own machine. You know it is there. The point is that even if you accidentally run something that calls home or pulls in malware, Suricata with TLS visibility can see what it is doing and kill it. Without this, your IDS is blind to anything inside an HTTPS connection, which is almost everything.

The same setup is possible at home with OPNsense and Zenarmor (or Squid, if you want to go old school).

What TLS inspection gives you at home:

  • Visibility into what domains are being contacted, even over HTTPS
  • The ability to block specific categories of traffic (malware callbacks, ad networks, specific services) at the payload level, not just by IP
  • Alerting when a device starts behaving differently: a script running overnight making outbound connections it did not make before

The main cost is that certificate pinning will break for some apps. Banking apps and some corporate software pin their certificates and will refuse to work through an intercepting proxy. The practical approach is to set up inspection selectively: apply it to the segments where you care about visibility, exempt specific endpoints that are known to pin, and leave trusted devices uninspected.

I have not deployed this fully yet. It is in the backlog behind finishing the observability stack. The architecture is clear enough to write down.

Next: Part 3: Connectivity covers dual WAN failover, WireGuard VPN, and fixing bufferbloat with FQ_CoDel.