Skip to content

Firewalld Advanced

Firewalld is the default firewall management tool for RHEL/CentOS-based distributions. This article dives deep into Rich Rules, Direct Rules, custom zones, port forwarding, IP masquerading, ipsets, and other advanced features to help you build fine-grained network security policies.

Rich Rules provide more powerful and flexible rule definitions than simple --add-port/--add-service, allowing fine-grained control based on source addresses, destination addresses, ports, and more.

rule
[family="ipv4|ipv6"]
[source address="address[/mask]" [invert="true"]]
[destination address="address[/mask]" [invert="true"]]
[service name="service" | port port="port" protocol="tcp|udp" |
protocol value="protocol" | icmp-block name="type" | masquerade |
forward-port port="port" protocol="tcp|udp" to-port="port" [to-addr="address"]]
[log [prefix="prefix"] [level="level"] [limit value="rate"]]
[audit]
[accept|reject|drop]

Allow a Specific IP to Access a Specific Port

Section titled “Allow a Specific IP to Access a Specific Port”
Terminal window
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.100" port port="3306" protocol="tcp" accept'
Terminal window
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.50" drop'
Terminal window
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="ssh" accept'
Terminal window
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" service name="http" log prefix="HTTP-BLOCKED: " level="warning" limit value="5/m" reject'

Allow a Specific IP Range to Access Multiple Ports

Section titled “Allow a Specific IP Range to Access Multiple Ports”
Terminal window
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" port port="8080-8090" protocol="tcp" accept'
Terminal window
# View Rich Rules for the current zone
sudo firewall-cmd --list-rich-rules
# Remove a Rich Rule
sudo firewall-cmd --permanent --remove-rich-rule='rule family="ipv4" source address="10.0.0.50" drop'
# Apply changes
sudo firewall-cmd --reload

Direct Rules allow you to insert rules directly into the iptables/nftables backend, suitable for complex scenarios that Rich Rules cannot handle.

Terminal window
sudo firewall-cmd --direct --add-rule ipv4 filter INPUT 0 <iptables rule parameters>
Terminal window
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 4 -j ACCEPT
Terminal window
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 99 -j LOG --log-prefix "DROPPED: " --log-level 4
Terminal window
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m mac --mac-source AA:BB:CC:DD:EE:FF -j DROP
Terminal window
# View all Direct Rules
sudo firewall-cmd --direct --get-all-rules
# Remove a Direct Rule
sudo firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 -m mac --mac-source AA:BB:CC:DD:EE:FF -j DROP
sudo firewall-cmd --reload

Firewalld comes with built-in zones like public, trusted, and dmz, but you can also create custom zones to meet specific requirements.

Terminal window
# Create a zone named webservers
sudo firewall-cmd --permanent --new-zone=webservers
sudo firewall-cmd --reload
Terminal window
# Set zone description
sudo firewall-cmd --permanent --zone=webservers --set-description="Web Server Zone"
# Add allowed services
sudo firewall-cmd --permanent --zone=webservers --add-service=http
sudo firewall-cmd --permanent --zone=webservers --add-service=https
sudo firewall-cmd --permanent --zone=webservers --add-port=8080/tcp
# Bind a network interface to the custom zone
sudo firewall-cmd --permanent --zone=webservers --change-interface=eth1
# Bind a source address to the zone
sudo firewall-cmd --permanent --zone=webservers --add-source=10.0.1.0/24
sudo firewall-cmd --reload
Terminal window
# View all zones
sudo firewall-cmd --get-zones
# View detailed configuration for a specific zone
sudo firewall-cmd --zone=webservers --list-all
# View all active zones
sudo firewall-cmd --get-active-zones
# View the default zone
sudo firewall-cmd --get-default-zone
Terminal window
sudo firewall-cmd --permanent --delete-zone=webservers
sudo firewall-cmd --reload

Port forwarding redirects traffic arriving at one port to another port or another host.

Forward local port 80 to port 8080:

Terminal window
sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080
sudo firewall-cmd --reload

Forward traffic on local port 80 to port 8080 on another host:

Terminal window
# Masquerade must be enabled first
sudo firewall-cmd --permanent --add-masquerade
# Add the port forwarding rule
sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080:toaddr=192.168.1.100
sudo firewall-cmd --reload
Terminal window
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" forward-port port="443" protocol="tcp" to-port="8443" to-addr="192.168.1.100"'
sudo firewall-cmd --reload
Terminal window
sudo firewall-cmd --list-forward-ports
Terminal window
sudo firewall-cmd --permanent --remove-forward-port=port=80:proto=tcp:toport=8080
sudo firewall-cmd --reload

Masquerading is a special form of SNAT (Source Network Address Translation), typically used to allow internal network machines to access the internet through the firewall host.

Terminal window
sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --reload
Terminal window
sudo firewall-cmd --query-masquerade
Terminal window
sudo firewall-cmd --permanent --zone=internal --add-masquerade
sudo firewall-cmd --reload

Enable NAT only for a specific source subnet:

Terminal window
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" masquerade'
sudo firewall-cmd --reload

Both masquerading and port forwarding require kernel IP forwarding:

Terminal window
# Enable temporarily
sudo sysctl -w net.ipv4.ip_forward=1
# Enable permanently
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/ip_forward.conf
sudo sysctl -p /etc/sysctl.d/ip_forward.conf

IPSet allows you to define collections of IP addresses and reference them in firewall rules, making it ideal for managing large numbers of IPs.

Terminal window
# Create an IPv4 address set
sudo firewall-cmd --permanent --new-ipset=blacklist --type=hash:ip
# Create a set that supports subnets
sudo firewall-cmd --permanent --new-ipset=trusted_nets --type=hash:net
Terminal window
sudo firewall-cmd --permanent --ipset=blacklist --add-entry=203.0.113.50
sudo firewall-cmd --permanent --ipset=blacklist --add-entry=198.51.100.0/24
sudo firewall-cmd --permanent --ipset=trusted_nets --add-entry=10.0.0.0/8
sudo firewall-cmd --reload

Create an IP list file:

Terminal window
cat <<EOF > /tmp/blacklist.txt
203.0.113.10
203.0.113.20
198.51.100.0/24
EOF

Import into the IPSet:

Terminal window
sudo firewall-cmd --permanent --ipset=blacklist --add-entries-from-file=/tmp/blacklist.txt
sudo firewall-cmd --reload
Terminal window
# Block all IPs in the blacklist
sudo firewall-cmd --permanent --add-rich-rule='rule source ipset="blacklist" drop'
# Allow IPs in the whitelist to access SSH
sudo firewall-cmd --permanent --add-rich-rule='rule source ipset="trusted_nets" service name="ssh" accept'
sudo firewall-cmd --reload
Terminal window
# View all IPSets
sudo firewall-cmd --get-ipsets
# View IPSet entries
sudo firewall-cmd --ipset=blacklist --get-entries
# Remove an entry
sudo firewall-cmd --permanent --ipset=blacklist --remove-entry=203.0.113.50
# Delete an IPSet
sudo firewall-cmd --permanent --delete-ipset=blacklist
sudo firewall-cmd --reload

Firewall logging is essential for security auditing and troubleshooting.

Terminal window
# Log rejected SSH connections
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" service name="ssh" log prefix="SSH-ACCESS: " level="notice" limit value="3/m" accept'
# Log dropped traffic
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="0.0.0.0/0" log prefix="FW-DROP: " level="warning" limit value="10/m" drop'
sudo firewall-cmd --reload
LevelDescription
emergEmergency
alertAlert
critCritical
errorError
warningWarning
noticeNotice
infoInformational
debugDebug

limit value prevents log flooding that could fill up disk space:

Rate FormatDescription
1/s1 entry per second
5/m5 entries per minute
10/h10 entries per hour
100/d100 entries per day
Terminal window
# View via journalctl
sudo journalctl -k | grep "FW-DROP"
# View via dmesg
sudo dmesg | grep "SSH-ACCESS"
# View messages log
sudo grep "FW-DROP" /var/log/messages

Firewalld can globally log all denied connections:

Terminal window
# View current setting
sudo firewall-cmd --get-log-denied
# Enable (options: all, unicast, broadcast, multicast, off)
sudo firewall-cmd --set-log-denied=all

Hands-On: Building a Web Server Firewall Policy

Section titled “Hands-On: Building a Web Server Firewall Policy”

The following example configures a complete firewall policy for a web server, including custom zones, IPSet whitelists, port forwarding, and logging.

  1. Create an admin IPSet:

    Terminal window
    sudo firewall-cmd --permanent --new-ipset=admin_ips --type=hash:ip
    sudo firewall-cmd --permanent --ipset=admin_ips --add-entry=192.168.1.10
    sudo firewall-cmd --permanent --ipset=admin_ips --add-entry=192.168.1.11
  2. Configure basic public zone rules:

    Terminal window
    # Allow HTTP and HTTPS
    sudo firewall-cmd --permanent --add-service=http
    sudo firewall-cmd --permanent --add-service=https
    # Remove default SSH service (will be restricted via Rich Rule)
    sudo firewall-cmd --permanent --remove-service=ssh
  3. Add Rich Rules:

    Terminal window
    # Allow SSH only from admin IPs
    sudo firewall-cmd --permanent --add-rich-rule='rule source ipset="admin_ips" service name="ssh" accept'
    # Log and drop other SSH attempts
    sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" service name="ssh" log prefix="SSH-DENIED: " level="warning" limit value="3/m" drop'
    # Log HTTP access (sampled)
    sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" service name="http" log prefix="HTTP: " level="info" limit value="1/m" accept'
  4. Configure port forwarding (HTTP to backend application):

    Terminal window
    sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080
  5. Apply and verify:

    Terminal window
    sudo firewall-cmd --reload
    sudo firewall-cmd --list-all
    sudo firewall-cmd --list-rich-rules

By leveraging Rich Rules, IPSets, port forwarding, and logging, you can build fine-grained firewall security policies that effectively defend against network threats while keeping services running smoothly.