firewalld and common equivalents in iptables

As I increasingly use distributions that rely on firewalld over the old ways of manually configuring iptables this article notes down some common patterns encountered.

First and foremost firewalld provides a stateful firewall configuration by default and puts emphasis on restricting incoming connections/ports/services while outgoing traffic is always allowed.

Note that the default configuration will allow a limited number of services including SSH in the public zone. Different interfaces can be assigned to different zones (e.g. home or trusted) also via NetworkManager configuration.

Blocking source IPs

firewall-cmd --zone=block --add-source=1.2.3.4
firewall-cmd --zone=block --add-source=1.2.3.0/24
# or
firewall-cmd --zone=drop --add-source=1.2.3.4
firewall-cmd --zone=drop --add-source=1.2.3.0/24

iptables -A INPUT -s 1.2.3.4 -j REJECT
iptables -A INPUT -s 1.2.3.0/24 -j REJECT
# or
iptables -A INPUT -s 1.2.3.4 -j DROP
iptables -A INPUT -s 1.2.3.0/24 -j DROP

Blocking a large list of IPs

This works like before with IP sets, except firewalld will also manage them for you.

However note that firewalld might choose to use an nftables set, so it won't be visible to the ipset command line tool.

firewall-cmd --permanent --new-ipset=asdf --type=hash:ip
firewall-cmd --ipset=asdf --add-entry=223.223.0.0
firewall-cmd --zone=block --add-source=ipset:asdf

ipset create asdf hash:ip
ipset add asdf 223.223.0.0
iptables -I INPUT -m set --match-set asdf src -j REJECT

Combinations with rich rules are also possible:

firewall-cmd --zone=public --add-rich-rule='rule source ipset="china" port port="22" protocol="tcp" drop'

iptables -I INPUT -p tcp -m tcp --dport 22 -m set --match-set china src -j DROP

Whitelisting source IPs

Allowing blanket access to all ports is easily done using the pre-existing trusted zone:

firewall-cmd --zone=trusted --add-source=3.4.5.6

iptables -I INPUT -s 3.4.5.6 -j ACCEPT

For fine-grained control a new zone can be created:

firewall-cmd --permanent --new-zone=mgmt-access
firewall-cmd --permanent --zone=mgmt-access --add-service=ssh
firewall-cmd --permanent --zone=mgmt-access --add-service=http
firewall-cmd --permanent --zone=mgmt-access --add-source=3.4.5.6
firewall-cmd --permanent --zone=mgmt-access --add-source=6.6.6.0/24

iptables -N mgmt_access
iptables -A mgmt_access -p tcp -m tcp --dport 22 -j ACCEPT
iptables -A mgmt_access -p tcp -m tcp --dport 80 -j ACCEPT
iptables -A mgmt_access -j RETURN
iptables -I INPUT -s 3.4.5.6 -j mgmt_access
iptables -I INPUT -s 6.6.6.0/24 -j mgmt_access

NAT (masquerade) for outgoing traffic

firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.0.0/27" masquerade'
firewall-cmd --zone=public --add-rich-rule='rule family="ipv6" source address="fd12:3456:789a::/48" masquerade'

iptables -t nat -A POSTROUTING -s 10.0.0.0/27 -j MASQUERADE
ip6tables -t nat -A POSTROUTING -s fd12:3456:789a::/48 -j MASQUERADE

Destination NAT

firewall-cmd --add-forward-port=port=2002:proto=tcp:toaddr=10.0.0.2:toport=2002

iptables -t nat -A PREROUTING -p tcp -m tcp --dport 2002 -j DNAT --to-destination 10.0.0.2:2002

Filtering outgoing traffic?

The whole design of firewalld seems to be built around not allowing you do this.

Yet, as long as iptables-legacy is installed it's still possible to use the deprecated "direct rule" functionality for this.

firewall-cmd --direct --add-rule ipv4 filter OUTPUT 0 -p tcp -m tcp --dport 25 -j REJECT
# the rule will literally show up in iptables as entered

iptables -A OUTPUT -p tcp -m tcp --dport 25 -j REJECT

Apparently policies are firewalld's replacement for direct rules but I have not figured those out yet.

By the way, direct rules are not bound to a particular zone and only show up in firewall-cmd --direct --get-all-rules.