Andrew Mercer
on this page

What it is

firewalld is the dynamic firewall manager on RHEL, CentOS, Fedora, and their relatives. Instead of a flat list of rules, it groups network interfaces and source addresses into zones, each with its own trust level and its own set of allowed services and ports. It drives nftables underneath on current releases (iptables on older ones) — see the iptables and nftables guides for the engine layer, and Netfilter for the hook points both use.

Project site: firewalld.org.

Default zones

Zone Default behaviour for incoming traffic
trusted Allow everything
home Reject unless related to outgoing traffic or one of: ssh, mdns, ipp-client, samba-client, dhcpv6-client
internal Same as home to start with
work Reject unless related to outgoing traffic or one of: ssh, ipp-client, dhcpv6-client
public Reject unless related to outgoing traffic or ssh/dhcpv6-client. Default zone for new interfaces
external Reject unless related or ssh. IPv4 traffic forwarded out through this zone is masqueraded
dmz Reject unless related or ssh
block Reject everything not related to outgoing traffic (with an ICMP error)
drop Silently drop everything not related to outgoing traffic

Zone management

sudo firewall-cmd --get-active-zones                      # zones with interfaces/sources bound
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --zone=public --list-all                # everything configured in a zone
sudo firewall-cmd --permanent --zone=internal --change-interface=eth1

Assigning an interface via the legacy network-scripts file (RHEL 7 style, NM_CONTROLLED=no) means adding a ZONE= line:

DEVICE="eth0"
BOOTPROTO="static"
ONBOOT="yes"
IPADDR="192.168.0.253"
NETMASK="255.255.255.0"
GATEWAY="192.168.0.254"
ZONE="external"

On NetworkManager-managed interfaces set it there instead, or firewalld and NM will disagree: nmcli connection modify "<conn>" connection.zone external (see nmcli).

Services and ports

# Open/close a predefined service
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --permanent --zone=public --remove-service=dhcpv6-client

# Open/close an arbitrary port
sudo firewall-cmd --permanent --zone=public --add-port=8080/tcp
sudo firewall-cmd --permanent --zone=public --remove-port=8080/tcp

sudo firewall-cmd --reload          # apply permanent config to the running firewall

--permanent writes config without changing the running state, and running-state changes (no --permanent) vanish on reload. The usual workflow is permanent change + --reload. --complete-reload also drops existing connection state, so it interrupts live sessions — use it only when you must.

Port forwarding

sudo firewall-cmd --permanent --zone=external \
  --add-forward-port=port=8443:proto=tcp:toport=443:toaddr=192.168.1.20
sudo firewall-cmd --permanent --zone=external \
  --remove-forward-port=port=8443:proto=tcp:toport=443:toaddr=192.168.1.20

Forwarding to another host needs masquerading on the zone that faces the destination (below) and IP forwarding enabled.

Rich rules

Rich rules express conditions the plain service/port model cannot: source addresses, logging, rejects, rate limits.

# Allow a port only from one subnet
sudo firewall-cmd --permanent --zone=public --add-rich-rule=\
'rule family="ipv4" source address="203.0.113.0/24" port protocol="tcp" port="5432" accept'

# Same, with logging
sudo firewall-cmd --permanent --zone=public --add-rich-rule=\
'rule family="ipv4" source address="203.0.113.0/24" service name="ssh" log prefix="ssh-allowed " level="info" accept'

# Block one address
sudo firewall-cmd --permanent --zone=public --add-rich-rule=\
'rule family="ipv4" source address="198.51.100.7" reject'

sudo firewall-cmd --zone=public --list-rich-rules
sudo firewall-cmd --permanent --zone=public --remove-rich-rule='<exact rule text>'

To remove a rich rule you must pass its text exactly as it was added. Copy it from --list-rich-rules.

Masquerading (NAT)

sudo firewall-cmd --zone=external --query-masquerade
sudo firewall-cmd --permanent --zone=external --add-masquerade
sudo firewall-cmd --reload

This is what turns a two-NIC box into a NAT gateway. The complete walk-through, including the interface-to-zone assignments, is in the Linux router guide.

Restarting

sudo systemctl restart firewalld     # full restart
sudo firewall-cmd --reload           # re-read permanent config, keep connections

Cheat sheet

firewall-cmd --get-active-zones
firewall-cmd --zone=<zone> --list-all
firewall-cmd --permanent --zone=<zone> --add-service=<svc>
firewall-cmd --permanent --zone=<zone> --add-port=<port>/tcp
firewall-cmd --permanent --zone=<zone> --add-masquerade
firewall-cmd --reload