Unifi Zone Based Firewall -- FAIL

The Unifi Zone Based Firewall (ZBF) is a very powerful, but simplified mechanism for managing firewall rules on Unifi Gateways. Sadly, ZBF policies are not always properly executed. ZBF policies in the same zone don’t work because networks in the same zone already have access to each other. ZBF policies that cross zones are often ignored due to iptables chains that get evaluated and drop traffic before ZBF policies can be evaluated. I regard this as a design flaw.

Existing iptables chains (UBIOS_FORWARD_JUMP, UBIOS_PREROUTING_JUMP) are UDM system chains that always get evaluated before zone based firewall rules. If your traffic is originating from a subnet behind a VLAN or virtual interface, sometimes the traffic never hits the zone policy because it’s caught in a system chain first.

BIG GLARING NOTE:

THANKS TO TOM LAWRENCE FROM LAWRENCE SYSTEMS WHO POINTED OUT THE REAL ISSUE. AS IT TURNS OUT, I WAS USING PORT 443 AS THE SOURCE PORT AND THE DESTINATION PORT. TOM INDICATED THAT MANY CLIENT APPLICATIONS (INCLUDING MESH CENTRAL) USE EPHEMERAL PORTS ON THE CLIENT/SOURCE SIDE. SO, I WAS IN ERROR. I SHOULD HAVE ONLY DESIGNATED THE DESTINATION PORT AND NOT ALSO THE SOURCE PORT. THE PROPER ZBF POLICY IS AS FOLLOWS:

I AM LEAVING THE VIDEO UP AND THE REST OF THIS NOTES FILE AS INFORMATION ONLY. DESPITE A FEW WEEKS OF TESTING, I WAS UNABLE TO FIND THE REAL ISSUE. THANKS TO TOM! NOW, JUST TO SEE THE RABBIT HOLE I WENT DOWN YOU CAN READ ON…

Once I upgraded to the ZBF, I discovered that if I had a rule that dropped all traffic from a particular VLAN back to my main LAN, I was unable to add a ZBF policy to allow selected traffic to pass. Normally with any firewall, the order of the policies controls what is blocked vs. passed. However, despite proper ordering, I experienced ZBF policy failures as seen in the tutorial.

For the purposes of this tutorial, I am going to use an example from my Remote Assistance Tools for the Home Lab - MeshCentral tutorial. If you have a MeshCentral client on a subnet, your MeshCentral server is on your main LAN, and you deny traffic from your VLANs to your Main LAN you will most likely run into the problem that I did. MeshCentral is simply a good use case to illustrate the problem with Unifi ZBF. I discovered that ZBF works great with simple allows or denies. The problem occurs when you need more granularity.

You can get around this issue by simply opening all of your VLAN traffic to your main LAN. Consider that the default behavior when you create a Unifi VLAN is that all other networks can talk to it. I am going to assume that you like using VLANs for security, so that might not be optimal.

It may be that only Unifi Gateways that have been upgraded through various versions are seeing this issue with ZBF. Perhaps new installs of Unifi Gateways do not have the ZBF problems that I encountered.

In the screenshot of my MeshCentral below, you will notice that the RocketChat client is indicating that it is online and that’s because MeshCentral on my Main LAN is able to “see” the RocketChat MeshCentral client on my Cloud-DMZ network in my DMZ zone for the ZBF. Since I moved my Cloud-DMZ VLAN-80 from the Internal Zone to the DMZ Zone, my VLAN-80 traffic is barred from contacting my Internal Zone networks including my main LAN and this is my intended design.

The problem with this is that the MeshCentral Client in RocketChat is unable to make a connection to my NPM server and thus my MeshCentral server also which are both on my Main LAN. I have a local DNS A record for my domain that points to the local LAN address of NginX Proxy Manager. I also have a CNAME record for mesh.scottibyte.com that aliases to that local DNS A record. That solves the problem with Hairpin NAT (discussed in a future video) and it allows MeshCentral client management even if my ISP connection is offline.

To resolve this, you would think that you could add a policy to the ZBF to allow port 443 from the DMZ zone to to connect to 172.16.0.15 (My NPM on my main LAN) in the Internal Zone and this would work. Not!

I am hoping that Ubiquiti will fix this in the future, however in UnifiOS 4.3.6 and Network 9.5.21, this is a very real problem. Note below that my main LAN (Scott & Kat’s LAN) is in the Internal Zone and Cloud-DMZ is in the DMZ Zone.

As it turns out, all networks in the Internal Zone have implicit access to each other and creating ZBF rules does not help to control access to them. The whole idea of ZBF is that groups of networks represent a “security” Zone.

So, the RocketChat Server is in the DMZ Zone and the MeshCentral and NginX Proxy Manager (NPM) servers are in the Internal Zone. The image below is the list of policies that control access from the DMZ Zone to the Internal Zone.

From the listing above, my intent was that the “Allow DMZ-Cloud to NPM” policy would allow any system on my DMZ-Cloud VLAN80 192.168.80.0/24 access to my NPM at port 443.

Sadly, that policy was ignored and the MeshCentral Client on the RocketChat server was unable to connect to NPM and you can see that below because the MeshCentral client was unable to begin a terminal session.

To fix this, I am starting an ssh session on my UDM Pro.

The first thing I want to do is to evaluate the the iptables entries for the ZBF.

sudo iptables -L UBIOS_FORWARD_JUMP -v -n --line-numbers

As it turns out, the iptables chain ends up completing its rules evaluation even before passing control to ZBF and that’s why our ZBF policy did not work.

To get around this, we are going to create a script. First, lets create a folder that Unifi uses for custom scrips.

mkdir -p /mnt/data/custom-scripts

Edit a script file.

vi /mnt/data/custom-scripts/allow_dmz_to_internal.sh

Press “i” to go into insert mode and Insert the following.

#!/bin/bash
# Allow DMZ -> Internal HTTPS permanently

# DMZ subnet
DMZ_SUBNET="192.168.80.0/24"

# Internal host
INTERNAL_HOST="172.16.0.15"

# Insert rules at top of UBIOS_FORWARD_JUMP chain
iptables -I UBIOS_FORWARD_JUMP 2 -s $DMZ_SUBNET -d $INTERNAL_HOST -p tcp --dport 443 -j ACCEPT

The script above is a very specific example for MeshCentral. You would want to change the DMZ subnet to be the address of the VLAN where you based your cloud services. The Internal host is the local address of your NPM on your main LAN.

Save the file with ESC :wq which saves the file and exits the vi editor. Now provide the script execute privilege.

chmod +x /mnt/data/custom-scripts/allow_dmz_to_internal.sh

You can run the script now to have it take effect.

/mnt/data/custom-scripts/allow_dmz_to_internal.sh

To make the script take effect at every reboot of your Unifi Gateway:

vi /etc/rc.local

Hit “i” to go into insert mode and Insert the following into the file.

/mnt/data/custom-scripts/allow_dmz_to_internal.sh

Save the file with ESC :wq which saves the file and exits the vi editor.

Since you you executed the script above, check the iptables entries.

sudo iptables -L UBIOS_FORWARD_JUMP -v -n --line-numbers

Note that the additional entry was added on line two above.

If I decided that I wanted to delete the entry on line 2 for any reason later on:

sudo iptables -D UBIOS_FORWARD_JUMP 2

In the tutorial, I deleted my ZBF policy that was supposed to do this since it was not working and the screen shot below reflects that. Note that if you make a ZBF modification, the firewall policies will be reloaded and you will need to run your script again.

Towards the end of the tutorial, I showed how after I deleted the ZBF Policy and so I needed to execute the script again.

Hopefully Ubiquiti will address this issue in a future release of the ZBF.