Skip to content
Search RunEntLinux

Searches general documentation on RunEntLinux. Each distribution has its own documentation search.

    Browse this documentation

    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 and allow SSH connections (the rule ends with accept, so it records allowed connections, not rejected ones)
    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.