Understanding Firewalld in Multi-Zone Configurations

A Simple Multi-Zoned Example

To demonstrate precedence, let's swap ssh for http in the public zone and set up the default internal zone for our favorite IP address, 1.1.1.1. The following commands accomplish this task:


# firewall-cmd --permanent --zone=public --remove-service=ssh
# firewall-cmd --permanent --zone=public --add-service=http
# firewall-cmd --permanent --zone=internal --add-source=1.1.1.1
# firewall-cmd --reload

which results in the following configuration:


# firewall-cmd --zone=public --list-all
public (default, active)
  interfaces: eno1 eno2
  sources:
  services: dhcpv6-client http
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:
# firewall-cmd --permanent --zone=public --get-target
default
# firewall-cmd --zone=internal --list-all
internal (active)
  interfaces:
  sources: 1.1.1.1
  services: dhcpv6-client mdns samba-client ssh
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:
# firewall-cmd --permanent --zone=internal --get-target
default

With the above configuration, if someone attempts to ssh in from 1.1.1.1, the request would succeed because the source zone (internal) is applied first, and it allows ssh access.

If someone attempts to ssh from somewhere else, say 2.2.2.2, there wouldn't be a source zone, because no zones match that source. Therefore, the request would pass directly to the interface zone (public), which does not explicitly handle ssh. Since public's target is default, the request passes to the firewalld default action, which is to reject it.

What if 1.1.1.1 attempts http access? The source zone (internal) doesn't allow it, but the target is default, so the request passes to the interface zone (public), which grants access.

Now let's suppose someone from 3.3.3.3 is trolling your website. To restrict access for that IP, simply add it to the preconfigured drop zone, aptly named because it drops all connections:


# firewall-cmd --permanent --zone=drop --add-source=3.3.3.3
# firewall-cmd --reload

The next time 3.3.3.3 attempts to access your website, firewalld will send the request first to the source zone (drop). Since the target is DROP, the request will be denied and won't make it to the interface zone (public) to be accepted.

A Practical Multi-Zoned Example

Suppose you are setting up a firewall for a server at your organization. You want the entire world to have http and https access, your organization (1.1.0.0/16) and workgroup (1.1.1.0/8) to have ssh access, and your workgroup to have samba access. Using zones in firewalld, you can set up this configuration in an intuitive manner.

Given the naming, it seems logical to commandeer the public zone for your world-wide purposes and the internal zone for local use. Start by replacing the dhcpv6-client and ssh services in the public zone with http and https:


# firewall-cmd --permanent --zone=public --remove-service=dhcpv6-client
# firewall-cmd --permanent --zone=public --remove-service=ssh
# firewall-cmd --permanent --zone=public --add-service=http
# firewall-cmd --permanent --zone=public --add-service=https

Then trim mdns, samba-client and dhcpv6-client out of the internal zone (leaving only ssh) and add your organization as the source:


# firewall-cmd --permanent --zone=internal --remove-service=mdns
# firewall-cmd --permanent --zone=internal --remove-service=samba-client
# firewall-cmd --permanent --zone=internal --remove-service=dhcpv6-client
# firewall-cmd --permanent --zone=internal --add-source=1.1.0.0/16

To accommodate your elevated workgroup samba privileges, add a rich rule:


# firewall-cmd --permanent --zone=internal --add-rich-rule='rule
 ↪family=ipv4 source address="1.1.1.0/8" service name="samba"
 ↪accept'

Finally, reload, pulling the changes into the active session:


# firewall-cmd --reload

Only a few more details remain. Attempting to ssh in to your server from an IP outside the internal zone results in a reject message, which is the firewalld default. It is more secure to exhibit the behavior of an inactive IP and instead drop the connection. Change the public zone's target to DROP rather than default to accomplish this:


# firewall-cmd --permanent --zone=public --set-target=DROP
# firewall-cmd --reload

But wait, you no longer can ping, even from the internal zone! And icmp (the protocol ping goes over) isn't on the list of services that firewalld can whitelist. That's because icmp is an IP layer 3 protocol and has no concept of a port, unlike services that are tied to ports. Before setting the public zone to DROP, pinging could pass through the firewall because both of your default targets passed it on to the firewalld default, which allowed it. Now it's dropped.

To restore pinging to the internal network, use a rich rule:


# firewall-cmd --permanent --zone=internal --add-rich-rule='rule
 ↪protocol value="icmp" accept'
# firewall-cmd --reload

In summary, here's the configuration for the two active zones:


# firewall-cmd --zone=public --list-all
public (default, active)
  interfaces: eno1 eno2
  sources:
  services: http https
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:
# firewall-cmd --permanent --zone=public --get-target
DROP
# firewall-cmd --zone=internal --list-all
internal (active)
  interfaces:
  sources: 1.1.0.0/16
  services: ssh
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:
        rule family=ipv4 source address="1.1.1.0/8"
         ↪service name="samba" accept
        rule protocol value="icmp" accept
# firewall-cmd --permanent --zone=internal --get-target
default

This setup demonstrates a three-layer nested firewall. The outermost layer, public, is an interface zone and spans the entire world. The next layer, internal, is a source zone and spans your organization, which is a subset of public. Finally, a rich rule adds the innermost layer spanning your workgroup, which is a subset of internal.

The take-away message here is that when a scenario can be broken into nested layers, the broadest layer should use an interface zone, the next layer should use a source zone, and additional layers should use rich rules within the source zone.

______________________

Nathan Vance is a computer science major at Hope College in Holland, Michigan. He installed Linux Mint 12 as a high school junior and now prefers Arch Linux.