An introduction to firewalld

About firewalld

In the past, the firewall on linux systems was in a lot of cases managed by the administrator directly using the iptables command, or by a rather static script like SuSEfirewall2. There is nothing wrong with that aproach, but in a lot of modern environments a more flexible and dynamic way of “doing your firewall” is desirable… and this is where firewalld shines.

Also, in RHEL 8 iptables has been replaced with nftables… but for anyone using firewalld, everything still works exactly the same. For more detail about this change in RHEL8, see this article.

Lastly: firewalld can be managed with ansible.
Just as an example, I have this function definition in my ~/.bashrc file:

hardban ()
    ansible servers -o -m firewalld -a "zone=drop immediate=yes permanent=yes state=enabled source=$1"

With that I can insert a permanent block for any IPv4 or IPv6 adress or network range for all my servers in only one command:

hardban 2401:1800::/31

(I also have a “softban” function that does the same but with a timeout setting of one hour, which automatically removes that rule after one hour without me haveing to remember to do it. neat, uh?)

About zones

To work with firewalld the first thing to understand is how firewald understands “zones”.

A zone in firewalld is a “group” of services, like for example saying “http, https and smtp are supposed to be available to the whole internet.”

A zone can contain as many services and/or ports as you need.

A zone can also contain as many interfaces and network ranges as you need (see my hardban example above: what it does is to add the address that I pass as argument to the “drop” zone, which does nothing other than dropping all traffic).

A network interface or address range is always a member of exacty one zone (firewalld has a default zone, which by default is the “public” zone.)

Putting it together

Let’s imagine a server, with a single network card eth0 which has a public ip address of

Let’s say this server runs httpd serving http and https, postfix serving smtp, and cyrus imap serving imap, imaps, pop3 and pop3s.

Now we want to make the webserver and the mail server available for the whole internet, and the imap server available only to our internal network which is

firewall-cmd --change-interface=eth0 --zone=public --permanent
## always specify the zone, even when you are modifying the default zone - for readability

firewall-cmd --add-source= --zone=internal --permanent
## all traffic from the network will be treated with the rules from the internal zone even though the interface is actually in public

firewall-cmd --add-service=http --zone=public --permanent
firewall-cmd --add-service=https --zone=public --permanent
firewall-cmd --add-service=smtp --zone=public --permanent
firewall-cmd --add-service=http --zone=internal --permanent
firewall-cmd --add-service=https --zone=internal --permanent
firewall-cmd --add-service=smtp --zone=internal --permanent
## allow htp, https, smtp in both public and internal zone

firewall-cmd --add-service=imap --zone=internal --permanent
firewall-cmd --add-service=imaps --zone=internal --permanent
firewall-cmd --add-service=pop3 --zone=internal --permanent
firewall-cmd --add-service=pop3s --zone=internal --permanent
## allow imap, imaps, pop3, pop3s only in the internal zone, which in this case means only from hosts within

firewall-cmd --reload
## actually make these new rules active

Pretty straightforward, isnt it?

Firewalld has many preconfigured zones, and services, and you can also just open ports or port ranges with –add-port=

Firewalld also has a lot of man pages with examples, which are quite good.

advanced firewalld: rich rules and direct rules

Direct rules allow to insert hardcoded iptables rules into the firewall. I’m not sure how much of this still applies to the new nftables based system, though.

rich rules allow to create more detailed rules for better control over what is going to happen, for example traffic rate limiting, or logging.

Just as an example, let’s open another port on our server from the previous example, but this needs to be logged:

firewall-cmd --permanent --zone=public --add-rich-rule='rule service name="ssh" log prefix="ssh " level="notice" limit value="3/m" accept'
## allow incoming ssh connections in the public zone, but log them with not more than three log entries per minute

firewall-cmd --reload

…continue to the next page…

%d bloggers like this: