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
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
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.
# 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.
# firewall-cmd --state running
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.
# firewall-cmd --get-active-zones public 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
ssh are defined making the
public zone a very useful zone to start with.
# firewall-cmd --list-all --zone=public public (active) target: default icmp-block-inversion: no interfaces: enp1s0 sources: services: cockpit dhcpv6-client ssh ports: protocols: forward: no masquerade: no forward-ports: source-ports: icmp-blocks: 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 firewalld.zone(5)
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.
# 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.
# firewall-cmd --get-default-zone public
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.
# firewall-cmd --get-default-zone dmz success # firewall-cmd --get-active-zones dmz interfaces: enp1s0
The default zone in all examples will be
public unless otherwise specified.
# firewall-cmd --change-interface=<nic> [--zone=<ZONE>]
# firewall-cmd --get-zone-of-interface=enp1s0 public
# firewall-cmd --zone=dmz --list-interfaces # firewall-cmd --zone=public --list-interfaces enp1s0
$ 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=192.168.1.0/24
$ 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=“10.0.0.0/8” 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>
$ firewall-cmd --direct --permanent --add-chain ipv4 raw blacklist
$ firewall-cmd --direct --permanent --add-rule ipv4 raw PREROUTING 0 -s 192.168.0.0/24 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=192.168.0.0/24 forward-port port=3000 protocol=tcp to-port=22122'
$ firewall-cmd --add-forward-port=port=3000:proto=tcp:toport=22122:toaddr=192.168.0.4