Skip to main content
  1. posts/

UniFi's Zone-Based Firewall Changes the IoT VLAN Rule

 Author
Author
Christian Mohn
IT veteran, podcaster, author, and blogger from Bergen, Norway.
Table of Contents

Back in 2019 I wrote a guide to isolating IoT devices on their own UniFi VLAN, using the controller’s legacy firewall rule engine: an explicit allow rule for established and related traffic back to the LAN. Here’s what the same policy looks like on UniFi’s Zone-Based Firewall (ZBF), introduced in UniFi Network 9.0.108, apparently while I wasn’t looking.

Home Assistant Still Needs Access
#

The requirement hasn’t moved at all since the original post. Home Assistant, or whatever manages your automation, needs to reach into the IoT network to control and poll devices, and needs their responses back. An IoT device itself should never get to open a new connection toward the rest of the network on its own. Internet access, cloud services, firmware updates, app connectivity, none of that changes. This is only about traffic between VLANs.

From Allow to Block
#

The legacy engine’s default was deny. The 2019 rule was an explicit allow, scoped to established and related connections, layered on top of that default so replies could get back to Home Assistant without opening the IoT network up generally.

ZBF defaults the other way. Any two networks sitting in the same firewall zone can reach each other freely unless a rule says otherwise. My IoT network and my regular LAN both sit in the Internal zone, so after the migration they could talk to each other with no rule in place at all, exactly what I’d built the original rule to stop.

The equivalent policy under ZBF is a block rule, not an allow rule:

  • Source: IoT network
  • Destination: Internal zone, any network
  • Action: Block
  • Connection state: NEW and INVALID, not ALL

The default flipped, so the rule had to flip with it.

NEW Needs INVALID Too
#

The connection-state scoping is the part worth getting right, and it’s easy to get wrong. A block rule with no state scoping at all blocks every packet matching source and destination, including reply traffic for connections something else opened. Get that wrong and you break the one thing this setup actually depends on: Home Assistant opens a connection into the IoT network, and the replies get caught by the same block rule that’s supposed to stop the IoT device from starting things on its own. From the outside, that just looks like Home Assistant having an off day.

Scoping the rule to NEW blocks connections the IoT device itself tries to start. Traffic that belongs to a session opened from the other direction isn’t new from the firewall’s point of view, it’s part of an existing connection, so it passes through untouched. INVALID closes a narrower gap: conntrack can occasionally mark a genuinely new connection attempt as invalid rather than new, say from asymmetric routing, a stale conntrack entry, or an out-of-window retransmit, and a NEW-only rule wouldn’t catch that. Blocking both states together is what makes the rule behave like the old allow rule did: the IoT device still can’t start anything on its own, and replies to sessions opened elsewhere still get through fine.

mDNS
#

mDNS reflection is still necessary, for the same reason it was in 2019. mDNS is link-local by design. Discovery protocols like Chromecast, HomeKit, and AirPlay depend on it, and none of them cross VLAN boundaries on their own. Without reflection, a device sitting on the IoT network is reachable but not discoverable from anywhere else.

What changed is more than where the switch lives. In 2019 this was a single on/off toggle. Today it’s the Gateway mDNS Proxy, one gateway-level feature under Settings -> Networks -> Gateway with three modes:

  • Auto: reflects common mDNS traffic across every VLAN automatically
  • Off: no reflection at all
  • Custom: pick specific services and specific network scopes per service

Auto and Off are just the old switch by another name. Custom is the genuinely new part, per-service, per-network granularity the 2019 version never had.

My gateway is set to Custom. In that mode, each network has to be explicitly included in the scope for mDNS to reflect to and from it, and that’s what the per-network mdns_enabled flag actually represents, not an independent setting sitting alongside the gateway one. IoT has it enabled, so it’s in scope.

New Firewall Engine, Same Result
#

Nothing changed from the outside. Home Assistant still reaches into the IoT network and gets its responses back exactly as before. IoT devices keep their internet access. The mechanism underneath is different, but the result isn’t.

If you’re running UniFi and haven’t checked which firewall engine you’re actually on, check it. A rule that worked under the old engine doesn’t necessarily mean anything under the new one, and the failure mode is easy to miss: nothing crashes, a VLAN boundary just stops doing its job without anyone noticing. That’s exactly what happened to me on my own gateway, and not just on this VLAN.

Related