Firewalld Configuration Hands-On: Linux Dynamic Firewall Management Guide
Firewalld configuration: zones, port and service allowlisting, rich rules, ipset bulk attack-IP bans, runtime vs permanent. Fast start for Linux dynamic firewall management.
Use Cases
Firewalld fits ops on CentOS 7/8/9, RHEL, Fedora, Rocky Linux. Unlike raw iptables chain edits, Firewalld manages by zone. Changes apply immediately and can persist. Use it on Web servers, database hosts, bastion hosts that need tight port control, and for emergency source-IP blocks in daily security ops.
Prerequisites
- firewalld installed (`yum install firewalld` or `dnf install firewalld`)
- root or sudo
- Remote login available (confirm SSH is already allowed before you change rules, or you lock yourself out)
How It Works
Firewalld binds each NIC to a zone (default: public). The zone defines allow/deny on that interface. Rules live in two stores: runtime (applies now, lost on reboot) and permanent (written to config, survives reboot). The stores are independent; run both. Rule types: service (predefined), port (single or range), rich rule (conditional, fine-grained), ipset (bulk IP sets).
Procedure
1. Start and inspect
systemctl enable --now firewalld
systemctl status firewalld --no-pager
# default zone and current rules
firewall-cmd --get-default-zone
firewall-cmd --list-all
2. Allow services and ports
# allow predefined services (HTTP/HTTPS/SSH)
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-service=ssh
# allow port and port range
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --permanent --add-port=3306/tcp
firewall-cmd --permanent --add-port=10000-20000/udp
# apply permanent rules (runtime applies immediately; no reload)
firewall-cmd --reload
firewall-cmd --list-all
3. Remove rules and runtime-only allow
# remove port (must do runtime and permanent)
firewall-cmd --remove-port=8080/tcp
firewall-cmd --permanent --remove-port=8080/tcp
# runtime-only allow (lost on reboot); short debug
firewall-cmd --add-port=9000/tcp
4. Drop attack IPs and CIDRs
# emergency drop single IP (immediate)
firewall-cmd --add-rich-rule='rule family="ipv4" source address="203.0.113.66" drop'
# persist drop and log
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" log prefix="BLOCKED: " level=info drop'
firewall-cmd --reload
# list and remove rich rule
firewall-cmd --list-rich-rules
firewall-cmd --permanent --remove-rich-rule='rule family="ipv4" source address="203.0.113.0/24" log prefix="BLOCKED: " level=info drop'
5. ipset for bulk attack-IP lists
# create ipset (maxelem = capacity cap)
firewall-cmd --permanent --new-ipset=attackips --type=hash:ip --option=maxelem=100000
# initial entries written to /etc/firewalld/ipsets/attackips.xml
# add attack IP to set
firewall-cmd --ipset=attackips --add-entry=198.51.100.23
firewall-cmd --permanent --ipset=attackips --add-entry=198.51.100.23
# use set as drop source
firewall-cmd --permanent --add-rich-rule='rule source ipset="attackips" drop'
firewall-cmd --reload
# list set entries
firewall-cmd --ipset=attackips --get-entries
6. Zones and port forwarding
# bind NIC to dmz zone
firewall-cmd --permanent --zone=dmz --add-interface=eth0
firewall-cmd --reload
# forward local 8443 to 10.0.0.5:443
firewall-cmd --permanent --zone=public --add-forward-port=port=8443:proto=tcp:toport=443:toaddr=10.0.0.5
# enable kernel forwarding
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf && sysctl -p
firewall-cmd --reload
Verification
# dump all zones
firewall-cmd --list-all-zones
# local check: port listening
ss -lntup | grep 8080
# remote check: run locally
nc -vz SERVER_IP 8080
# confirm IP drop (blocked source should timeout/refuse)
firewall-cmd --list-rich-rules | grep 203.0.113.66
# confirm permanent config
grep -r "8080" /etc/firewalld/zones/public.xml
FAQ
FAQ 1: Permanent rules not taking effect?
Usually you used `--permanent` and skipped `firewall-cmd --reload`, or you never passed `--permanent` so the runtime rule was not persisted. Runtime and permanent stores are independent. Run both. After permanent edits, reload once.
FAQ 2: IP is blocked but the attack still gets through?
Check CDN/load balancer: the source you see is often an origin-pull node, not the client. Confirm the rich rule is on the zone the NIC is actually bound to. Then `firewall-cmd --list-rich-rules` — later rules may override.
FAQ 3: firewalld vs iptables service conflict?
Do not enable both. CentOS 7+ defaults to firewalld. If iptables leftover: `systemctl stop iptables && systemctl disable iptables`. firewalld still uses nftables/iptables under the hood. Rules can convert; pick one management plane.
Summary
Core: zones, runtime/permanent dual store, rich-rule syntax. Daily allow: `--add-service/--add-port`. Emergency drop: `--add-rich-rule`. Bulk: ipset. Habit: runtime first, then permanent, always reload after permanent. That yields an auditable host perimeter. For complex NAT or packet marking, layer nftables on top.