For about a year my house had two VLANs and one rule between them that said: everything from the Wi-Fi network (VLAN20) can talk to everything in the lab network (VLAN11). Accept. Not even logged. The TVs, the dryer, the Alexa, my wife’s laptop, the work PC, all with a free pass to the Kubernetes API, the NAS, the Pi-holes.

I knew it. I had a tududi task for it for months. What stopped me was not the firewall. It was the fear of breaking the house at 8 pm and having Sandra ask why Jellyfin does not load while I am in a meeting.

So I did it in phases. This is what each phase looked like and what went wrong.

Phase A was just watching

The first change was no change at all. I put one small rule above the catch-all, Wi-Fi to the Pi-holes for DNS, and turned on logging for the catch-all. Then I did nothing for a week.

Sophos ships its logs to Loki in my cluster, so after eight days I pulled every line that hit that rule. 9,595 lines. I built a flow matrix: source, destination, port, how many times.

The result was almost boring. Exactly one device used the lab for anything other than DNS. My personal Mac. Ingress on 443, the kube-apiserver on 6443, Loki on 3100. Everything else on the Wi-Fi, every TV, every camera, every phone, only ever asked for DNS.

There was one mystery. The Echo was sending UDP to the Talos nodes. Why would an Alexa talk to a Kubernetes node? It was not. It was pinging the Pi-hole load balancer IPs, which never answer ICMP because Cilium does not, so the Talos nodes replied “host unreachable” on that connection. The log blamed the wrong side.

Phase B, allow rules only

With the matrix in hand I knew exactly what to allow. I created an object called V20_Trusted with three devices: my Mac, my Galaxy and the iPad. I left the work PC and the work phone out on purpose. Those are not mine to trust.

Then three rules above the catch-all: trusted devices can reach VLAN11, any Wi-Fi device can reach the lab ingress over HTTP and HTTPS, and any Wi-Fi device can reach the Pi-holes for DNS and ping. The catch-all stayed on Accept. Still logging.

Every write to the firewall went through the XML API as a dedicated account with the minimum rights. Get, resubmit as a no-op to prove the API would not mangle anything, Set, Get again, diff. Slow, but a Sophos Set replaces the whole entity, and the DHCP entity includes every static lease.

The same week I added a rule dropping any DNS from the Wi-Fi straight to the internet, port 53 and 853 for DNS over TLS, so nothing could bypass the Pi-holes.

That last rule is where I broke things.

The regression I caused

Two days later the work PC, the eeros, the Galaxy and the streaming boxes started hitting DNS timeouts. Intermittent. The kind where you reload and it works.

My first guess was the new DPI mode on the main rule. Wrong. My second guess was the web filter. Also wrong.

The actual cause: the VLAN20 DHCP scope was inheriting the appliance’s own DNS list, and that list had OpenDNS as the third resolver. Clients were getting three DNS servers from DHCP, the two Pi-holes and OpenDNS. My new rule dropped the third. Any client that rotated to it waited for a timeout.

The fix was one field. Turn off “use appliance DNS settings” on that DHCP scope so it only hands out the two Pi-holes. I proved the no-op resubmit was lossless first, 16 leases intact, then flipped the one field.

Lesson I wrote down: before you drop a port outbound, check what your own DHCP is handing out.

Phase C, the flip, with me at home

I scheduled the flip for a Sunday with me physically in the house. Two things blocked it that morning.

First, one unidentified device was still hitting the catch-all every day with SMB to the NAS. A randomized MAC, so the lease table was no help. I fingerprinted it from its Pi-hole query history: Apple push, CloudKit, krisp.ai, WhatsApp desktop, Avast. That is Sandra’s MacBook doing Time Machine. I pinned it with a DHCP reservation, gave it a Pi-hole host record, and wrote a narrow rule, her Mac to the NAS on port 445 only.

Second, the drops to OpenDNS never reached zero. I had been using that as my gate metric. Broken down by source it was the three eeros self-probing on 53 and 853. Infrastructure noise. That metric was never going to hit zero and I stopped waiting for it.

Then I flipped Vlan20to11 to Drop and Log. The API call timed out on the HTTP read. It applied anyway. I did not resubmit, I read the rule back and checked the action. That has bitten me twice now.

Then I walked around the house. Alexa plays music. Tapo cameras load. My Mac reaches everything it did before. Fourteen hours later, the only denials were Sandra’s Mac trying port 139 to the NAS. Legacy NetBIOS fallback. Time Machine was flowing fine on 445, 411 allowed connections against 14 denials on 139.

Phase D, cleanup, and what the 48 hours said

The 48-hour watch ended with 97 denials total, all of them that same port 139 flow. A drop log that is 100 percent one known benign flow is a log you stop reading, so I added 139 to the SMB service object instead of adding another rule.

Then the cleanup. The old rule to the management VLAN got restricted to the trusted object with an explicit deny below it. A dead rule, two stale host objects for laptops I no longer own, an old test VPN user, all gone. The default DHCP server, disabled in the UI, because the API answers 200 and silently ignores it.

What still leaks

The DNS bypass rule went from about 1,650 drops a day to 100 or 200. Almost all of what is left is the three eeros checking Google DNS and OpenDNS at night.

Two devices, a Chromecast and an LG TV, were hardcoding 8.8.8.8. I built my first DNAT rule redirecting one of them to a Pi-hole and left the other as a control. Both went quiet on the same day, so the test proved nothing. I kept the rule because it is harmless, and I was also wrong about them never reaching Pi-hole. They query it every day. 8.8.8.8 was only a fallback.

Open items: the Sophos firmware upgrade is blocked on a forgotten admin password, and the eero mesh is a daisy chain that nobody in this house will let me rewire.