Firewall Management#

Linux systems are often connected to the internet directly or indirectly via a firewall, but Linux itself also has a firewall built-in. This firewall is constantly being developed and upgraded, and it is not always easy to configure. For this firewalld was created to be an uniform way to manage the firewall as a whole.

Linux 2.0 came with ipfwadm and was replaced by ipchains in Linux 2.2. With Linux 2.4, ipchains was replaced by iptables. Since a couple of years development has started to replace iptables with nftables to provide a more flexible and powerful firewall ready for the next decade. With nftables, the firewall can be configured on the fly and should be able to work in environments with 10 Gbps or more network bandwidth.

A short introduction to firewalld#

Firewalld is the way to unify the iptables and nftables, and make the transition easier. Automation tools like Ansible understand the abstraction layer firewalld provides and playbooks should work as expected. Recent versions of Red Hat, CentOS and Fedora come by default with firewalld installed and enabled. Lets make sure firewalld is installed and running.

Install and enable firewalld#
# dnf install firewalld
# systemctl enable --now firewalld

As seen with systemd, also firewalld has been designed to be script friendly. Command firewall-cmd --state allows you to check if the firewall is enabled on a system.

Check if firewalld is running#
# firewall-cmd --state

In the previous example firewall-cmd was already shown and it is the command line interface to the firewalld daemon. Now that we have the firewall running, we can start configuring it. First we check all the active zones that are bound to which network interfaces.

Show the active zones and interfaces#
# firewall-cmd --get-active-zones
  interfaces: enp1s0

The example above shows that network interface enp1s0 is placed in the public zone, but what is defined in the public zone? With the option --list-all we can see the rules that are defined in the public zone. By default not a lot is defined in the public zone, but services like cockpit, dhcpv6-client and ssh are defined making the public zone a very useful zone to start with.

Show the configuration of the public zone#
# firewall-cmd --list-all --zone=public
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp1s0
  services: cockpit dhcpv6-client ssh
  forward: no
  masquerade: no
  rich rules:

What are firewall or network zones#

If we take the definition that the firewalld manpage says, a firewall zone is a collection of rules that are applied to a specific network interface. The public zone is the default zone, and it is the zone that is used when no other zone is specified.


A network or firewall zone defines the trust level of the interface used for a connection. There are several pre-defined zones provided by firewalld. Zone configuration options and generic information about zones are described in

But again, what is a zone and which one to select? The answer is simple as the zone defines the trust level of the interface used for a connection. If our server was directly connected to internet the dmz zone would be used, but if we were connected to a router, the internal zone would be used. By default a couple of zones are predefined, but you can add your own zones.

Show the available zones#
# firewall-cmd --get-zones
block dmz drop external home internal nm-shared public trusted work

All network interfaces are placed in the default network zone unless explicitly defined. With the option --get-default-zone we can see the default zone.

Show the default zone#
# firewall-cmd --get-default-zone

The default zone can also be changed and will directly change for all the interfaces unless explicitly defined. If you make the default zone block, then all unspecified interfaces will be placed in the block zone and all network traffic will direct stop directly.

Set the default zone#
# firewall-cmd --get-default-zone dmz
# firewall-cmd --get-active-zones
  interfaces: enp1s0


The default zone in all examples will be public unless otherwise specified.

# firewall-cmd --change-interface=<nic> [--zone=<ZONE>]
Show the zone for a network interface#
# firewall-cmd --get-zone-of-interface=enp1s0
Show the interfaces in a zone#
# firewall-cmd --zone=dmz --list-interfaces
# firewall-cmd --zone=public --list-interfaces


$ firewall-cmd --get-services
RH-Satellite-6 RH-Satellite-6-capsule amanda-client amanda-k5-client amqp amqps apcupsd audit bacula bacula-client bb bgp bitcoin bitcoin-rpc bitcoin-testnet bitcoin-testnet-rpc bittorrent-lsd ceph ceph-mon cfengine cockpit collectd condor-collector ctdb dhcp dhcpv6 dhcpv6-client distcc dns dns-over-tls docker-registry docker-swarm dropbox-lansync elasticsearch etcd-client etcd-server finger foreman foreman-proxy freeipa-4 freeipa-ldap freeipa-ldaps freeipa-replication freeipa-trust ftp galera ganglia-client ganglia-master git grafana gre high-availability http https imap imaps ipp ipp-client ipsec irc ircs iscsi-target isns jenkins kadmin kdeconnect kerberos kibana klogin kpasswd kprop kshell kube-api kube-apiserver kube-control-plane kube-controller-manager kube-scheduler kubelet-worker ldap ldaps libvirt libvirt-tls lightning-network llmnr managesieve matrix mdns memcache minidlna mongodb mosh mountd mqtt mqtt-tls ms-wbt mssql murmur mysql nbd netbios-ns nfs nfs3 nmea-0183 nrpe ntp nut openvpn ovirt-imageio ovirt-storageconsole ovirt-vmconsole plex pmcd pmproxy pmwebapi pmwebapis pop3 pop3s postgresql privoxy prometheus proxy-dhcp ptp pulseaudio puppetmaster quassel radius rdp redis redis-sentinel rpc-bind rquotad rsh rsyncd rtsp salt-master samba samba-client samba-dc sane sip sips slp smtp smtp-submission smtps snmp snmptrap spideroak-lansync spotify-sync squid ssdp ssh steam-streaming svdrp svn syncthing syncthing-gui synergy syslog syslog-tls telnet tentacle tftp tile38 tinc tor-socks transmission-client upnp-client vdsm vnc-server wbem-http wbem-https wireguard wsman wsmans xdmcp xmpp-bosh xmpp-client xmpp-local xmpp-server zabbix-agent zabbix-server
$ firewall-cmd --reload
$ firewall-cmd --add-service=oracle [--zone=<ZONE>]
$ firewall-cmd --add-service=oracle --zone=dmz --permanent
$ firewall-cmd --add-port=666/tcp
$ firewall-cmd --add-source=
$ firewall-cmd --list-all
$ firewall-cmd --remove-port=666/tcp [--permanent] [--zone=<ZONE>]
$ firewall-cmd --remove-service=oracle [--permanent] [--zone=<ZONE>]
$ firewall-cmd --add-rich-rule `rule family=“ipv4” source address=“” service name=“http” accept’
$ firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 9000 -j ACCEPT

Runtime vs permanent and timeouts#

$ firewall-cmd --add-service=http --add-service=https
$ firewall-cmd --add-service=http --add-service=https --permanent
$ firewall-cmd --add-service=http --add-port=https/tcp
$ firewall-cmd --reload
$ firewall-cmd --add-service=http --add-service=https --timeout=<seconds>

Rich rules#

$ firewall-cmd --direct --permanent --add-chain ipv4 raw blacklist
$ firewall-cmd --direct --permanent --add-rule ipv4 raw PREROUTING 0 -s blacklist
$ firewall-cmd --direct --permanent --add-rule ipv4 raw blacklist 0 -m limit --limit 1/min -j LOG --log-prefix "blacklist "
$ firewall-cmd --direct --permanent --add-rule ipv4 raw blacklist 1 -j DROP


$ firewall-cmd --permanent --zone=work --add-masquerade


$ firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address= forward-port port=3000 protocol=tcp to-port=22122'
$ firewall-cmd --add-forward-port=port=3000:proto=tcp:toport=22122:toaddr=