Firewall Configuration with firewalld (CentOS/Rocky Linux)
Firewalld is the default dynamic firewall management solution for Red Hat Enterprise Linux-based distributions, providing zone-based network security with an easy-to-use interface. This comprehensive guide walks you through firewalld configuration on CentOS, Rocky Linux, AlmaLinux, and RHEL systems, covering everything from basic setup to advanced firewall rules and security hardening.
Table of Contents
- Prerequisites
- Understanding Firewalld
- Step 1: Installing and Starting Firewalld
- Step 2: Understanding Zones
- Step 3: Managing Services
- Step 4: Managing Ports
- Step 5: Working with Rich Rules
- Step 6: Port Forwarding and NAT
- Step 7: IP Set Management
- Step 8: Direct Rules and iptables
- Step 9: Firewalld Lockdown
- Step 10: Advanced Configuration
- Verification
- Troubleshooting
- Best Practices
- Conclusion
- Additional Resources
Prerequisites
Before configuring firewalld, ensure you have:
- CentOS Stream 8/9, Rocky Linux 8/9, AlmaLinux 8/9, or RHEL 8/9
- Root access or sudo privileges
- SSH or console access to the server
- Basic understanding of networking concepts (TCP/UDP, ports, protocols)
- Understanding of your server's network interfaces
- List of services/ports that need to be accessible
- Backup access method (console) in case of firewall misconfiguration
Critical warning: Incorrect firewall configuration can lock you out of your server. Always maintain console access and test rules carefully before making them permanent.
Understanding Firewalld
What is Firewalld?
Firewalld is a dynamically managed firewall with support for network/firewall zones that define trust levels of network connections or interfaces. It provides a D-Bus interface for managing firewall rules and supports both IPv4 and IPv6.
Key features:
- Dynamic management: Changes can be made without restarting the service
- Zone-based: Network interfaces assigned to zones with different trust levels
- Runtime and permanent configurations: Test rules before making permanent
- Service definitions: Predefined configurations for common services
- Rich language: Advanced rule syntax for complex scenarios
- Integration: Works seamlessly with NetworkManager
- Backend flexibility: Uses nftables (default in RHEL 8+) or iptables
Firewalld vs iptables
Firewalld advantages:
- Higher-level, easier to manage
- Dynamic rule changes without service restart
- Zone-based conceptual model
- Integrated service definitions
- Better NetworkManager integration
When to use iptables directly:
- Legacy systems (RHEL 6 and earlier)
- Complex custom requirements
- Performance-critical scenarios
- Existing iptables scripts
Firewalld Architecture
User Commands
↓
firewall-cmd / GUI
↓
firewalld (daemon)
↓
nftables / iptables (backend)
↓
Netfilter (kernel)
Configuration files:
/etc/firewalld/- User configurations (persistent)/usr/lib/firewalld/- System defaults (don't modify)/etc/firewalld/firewalld.conf- Main configuration/etc/firewalld/zones/- Zone configurations/etc/firewalld/services/- Custom service definitions
Step 1: Installing and Starting Firewalld
Installation
Firewalld is typically pre-installed on RHEL-based distributions.
# Check if firewalld is installed
rpm -q firewalld
# Install firewalld if not present
sudo dnf install firewalld -y
# Install firewall-config GUI (optional)
sudo dnf install firewall-config -y
Starting and Enabling Firewalld
# Start firewalld service
sudo systemctl start firewalld
# Enable firewalld to start at boot
sudo systemctl enable firewalld
# Check firewalld status
sudo systemctl status firewalld
# Verify firewalld is running
sudo firewall-cmd --state
# Should output: running
Basic Status Information
# Get firewalld version
firewall-cmd --version
# Display default zone
firewall-cmd --get-default-zone
# List all active zones
firewall-cmd --get-active-zones
# Display all available zones
firewall-cmd --get-zones
# Get current configuration
firewall-cmd --list-all
# Check if firewalld is running
firewall-cmd --state
Understanding Runtime vs Permanent Configuration
Firewalld maintains two configurations:
Runtime configuration:
- Active, in-memory rules
- Lost after firewalld restart
- Use for testing before making permanent
Permanent configuration:
- Stored in configuration files
- Applied after firewalld reload
- Use
--permanentflag for persistence
# Runtime change (temporary, for testing)
sudo firewall-cmd --add-service=http
# Permanent change (survives reboot)
sudo firewall-cmd --permanent --add-service=http
# Reload to apply permanent changes
sudo firewall-cmd --reload
# Make runtime configuration permanent
sudo firewall-cmd --runtime-to-permanent
Step 2: Understanding Zones
Zones are the core concept in firewalld, defining trust levels for network connections.
Default Zones
Firewalld includes predefined zones (most to least restrictive):
1. drop
- Incoming packets dropped without notification
- Only outgoing connections allowed
- Most restrictive
2. block
- Incoming rejected with icmp-host-prohibited
- Only outgoing connections allowed
3. public (default)
- For untrusted public networks
- Selective incoming connections allowed
- Recommended for most servers
4. external
- For external networks with masquerading
- Used for router/gateway systems
5. dmz
- For DMZ (demilitarized zone) systems
- Limited public access
6. work
- For work networks
- More trust than public
7. home
- For home networks
- Trusts most systems
8. internal
- For internal networks
- High trust level
9. trusted
- All connections accepted
- Least restrictive (use carefully)
Working with Zones
# List all zones
firewall-cmd --get-zones
# View specific zone configuration
firewall-cmd --zone=public --list-all
# View all zones configuration
firewall-cmd --list-all-zones
# Get default zone
firewall-cmd --get-default-zone
# Set default zone
sudo firewall-cmd --set-default-zone=public
# Get active zones (zones with interfaces assigned)
firewall-cmd --get-active-zones
Assigning Interfaces to Zones
# List network interfaces
ip addr show
# Get zone of specific interface
firewall-cmd --get-zone-of-interface=eth0
# Assign interface to zone (runtime)
sudo firewall-cmd --zone=public --add-interface=eth0
# Assign interface to zone (permanent)
sudo firewall-cmd --permanent --zone=public --add-interface=eth0
sudo firewall-cmd --reload
# Change interface zone
sudo firewall-cmd --zone=dmz --change-interface=eth0
# Remove interface from zone
sudo firewall-cmd --zone=public --remove-interface=eth0
Creating Custom Zones
# Create new zone
sudo firewall-cmd --permanent --new-zone=custom_zone
# Set zone description
sudo firewall-cmd --permanent --zone=custom_zone --set-description="Custom security zone"
# Set zone target (default, ACCEPT, REJECT, DROP)
sudo firewall-cmd --permanent --zone=custom_zone --set-target=default
# Add services to custom zone
sudo firewall-cmd --permanent --zone=custom_zone --add-service=ssh
sudo firewall-cmd --permanent --zone=custom_zone --add-service=http
# Reload to apply
sudo firewall-cmd --reload
# Verify custom zone
firewall-cmd --zone=custom_zone --list-all
Zone Configuration Files
# View zone configuration file
cat /usr/lib/firewalld/zones/public.xml
# Custom zone configurations
ls -la /etc/firewalld/zones/
# Copy zone for customization
sudo cp /usr/lib/firewalld/zones/public.xml /etc/firewalld/zones/custom.xml
Step 3: Managing Services
Firewalld includes predefined service definitions for common applications.
Listing Services
# List all available services
firewall-cmd --get-services
# List services in default zone
firewall-cmd --list-services
# List services in specific zone
firewall-cmd --zone=public --list-services
# View service details
firewall-cmd --info-service=ssh
firewall-cmd --info-service=http
firewall-cmd --info-service=https
Adding and Removing Services
# Add service (runtime)
sudo firewall-cmd --add-service=http
sudo firewall-cmd --add-service=https
# Add service (permanent)
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
# Add service to specific zone
sudo firewall-cmd --zone=public --add-service=ssh
# Add multiple services at once
sudo firewall-cmd --permanent --add-service={http,https,ssh}
# Remove service
sudo firewall-cmd --remove-service=http
sudo firewall-cmd --permanent --remove-service=http
# Remove service from specific zone
sudo firewall-cmd --zone=public --remove-service=cockpit
Common Service Configurations
# Web server
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
# SSH (usually already enabled)
sudo firewall-cmd --permanent --add-service=ssh
# Mail server
sudo firewall-cmd --permanent --add-service=smtp
sudo firewall-cmd --permanent --add-service=smtps
sudo firewall-cmd --permanent --add-service=imap
sudo firewall-cmd --permanent --add-service=imaps
# Database servers
sudo firewall-cmd --permanent --add-service=mysql
sudo firewall-cmd --permanent --add-service=postgresql
# DNS server
sudo firewall-cmd --permanent --add-service=dns
# NFS
sudo firewall-cmd --permanent --add-service=nfs
sudo firewall-cmd --permanent --add-service=mountd
sudo firewall-cmd --permanent --add-service=rpc-bind
# FTP
sudo firewall-cmd --permanent --add-service=ftp
# Reload after changes
sudo firewall-cmd --reload
Creating Custom Services
# Create new service definition
sudo firewall-cmd --permanent --new-service=myapp
# Set service description
sudo firewall-cmd --permanent --service=myapp --set-description="My Application"
# Set short name
sudo firewall-cmd --permanent --service=myapp --set-short="MyApp"
# Add ports to service
sudo firewall-cmd --permanent --service=myapp --add-port=8080/tcp
sudo firewall-cmd --permanent --service=myapp --add-port=8443/tcp
# Add protocol
sudo firewall-cmd --permanent --service=myapp --add-protocol=tcp
# Add source port
sudo firewall-cmd --permanent --service=myapp --add-source-port=5000/tcp
# View service configuration
firewall-cmd --info-service=myapp
# Reload and use service
sudo firewall-cmd --reload
sudo firewall-cmd --add-service=myapp
Service definition file format:
# View custom service file
cat /etc/firewalld/services/myapp.xml
Example XML:
<?xml version="1.0" encoding="utf-8"?>
<service>
<short>MyApp</short>
<description>My Application Service</description>
<port protocol="tcp" port="8080"/>
<port protocol="tcp" port="8443"/>
</service>
Step 4: Managing Ports
For services without predefined definitions or custom port configurations.
Adding and Removing Ports
# Add single port (runtime)
sudo firewall-cmd --add-port=8080/tcp
# Add single port (permanent)
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
# Add multiple ports
sudo firewall-cmd --permanent --add-port={80/tcp,443/tcp,8080/tcp}
# Add port range
sudo firewall-cmd --permanent --add-port=8000-9000/tcp
# Add UDP port
sudo firewall-cmd --permanent --add-port=53/udp
# Add to specific zone
sudo firewall-cmd --zone=public --permanent --add-port=3306/tcp
# Remove port
sudo firewall-cmd --permanent --remove-port=8080/tcp
# List open ports
firewall-cmd --list-ports
# List ports in specific zone
firewall-cmd --zone=public --list-ports
Common Port Configurations
# Web applications
sudo firewall-cmd --permanent --add-port=8080/tcp # Alternative HTTP
sudo firewall-cmd --permanent --add-port=8443/tcp # Alternative HTTPS
sudo firewall-cmd --permanent --add-port=3000/tcp # Node.js default
# Databases
sudo firewall-cmd --permanent --add-port=3306/tcp # MySQL/MariaDB
sudo firewall-cmd --permanent --add-port=5432/tcp # PostgreSQL
sudo firewall-cmd --permanent --add-port=27017/tcp # MongoDB
sudo firewall-cmd --permanent --add-port=6379/tcp # Redis
# Development tools
sudo firewall-cmd --permanent --add-port=9000/tcp # PHP-FPM
sudo firewall-cmd --permanent --add-port=9200/tcp # Elasticsearch
# Monitoring
sudo firewall-cmd --permanent --add-port=9090/tcp # Prometheus
sudo firewall-cmd --permanent --add-port=3000/tcp # Grafana
# Game servers
sudo firewall-cmd --permanent --add-port=25565/tcp # Minecraft
sudo firewall-cmd --permanent --add-port=27015/udp # Source Engine
# Always reload after changes
sudo firewall-cmd --reload
Port Query and Verification
# Check if specific port is open
firewall-cmd --query-port=8080/tcp
# Check service status
firewall-cmd --query-service=http
# List all configuration
firewall-cmd --list-all
Step 5: Working with Rich Rules
Rich rules provide advanced firewall rule syntax for complex scenarios.
Rich Rule Syntax
Basic rich rule structure:
rule [family="<ipv4|ipv6>"]
[source [address="<address>"] [invert="true"]]
[destination [address="<address>"] [invert="true"]]
[service name="<service>"]
[port port="<port>" protocol="<tcp|udp>"]
[forward-port port="<port>" protocol="<tcp|udp>" to-port="<port>" to-addr="<address>"]
[log [prefix="<prefix>"] [level="<level>"]]
[audit]
[accept|reject|drop]
Common Rich Rule Examples
Allow specific IP to specific port:
# Allow specific IP to SSH
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.50" service name="ssh" accept'
# Allow specific IP to port
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.50" port port="3306" protocol="tcp" accept'
# Allow subnet to service
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="http" accept'
Block specific IP:
# Drop all traffic from IP
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.100" drop'
# Reject with message
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.100" reject'
# Block subnet
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" drop'
Rate limiting:
# Limit SSH connections (3 per minute from same IP)
sudo firewall-cmd --permanent --add-rich-rule='rule service name="ssh" limit value="3/m" accept'
# Limit HTTP connections
sudo firewall-cmd --permanent --add-rich-rule='rule service name="http" limit value="100/s" accept'
# Advanced rate limit with burst
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="0.0.0.0/0" port port="80" protocol="tcp" limit value="10/s" burst="20" accept'
Logging:
# Log dropped packets
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" log prefix="FIREWALL-BLOCKED: " level="info" drop'
# Log and accept
sudo firewall-cmd --permanent --add-rich-rule='rule service name="ssh" log prefix="SSH-ACCESS: " level="info" accept'
# Log rate limited
sudo firewall-cmd --permanent --add-rich-rule='rule service name="http" log prefix="HTTP: " level="warning" limit value="5/m" accept'
Time-based rules:
Rich rules don't support time directly, but you can use cron:
# Create script to enable/disable rule
cat << 'EOF' | sudo tee /usr/local/bin/time-firewall.sh
#!/bin/bash
# Enable rule at 9 AM
if [ "$(date +%H)" -eq 9 ]; then
firewall-cmd --add-rich-rule='rule service name="http" accept'
fi
# Disable rule at 5 PM
if [ "$(date +%H)" -eq 17 ]; then
firewall-cmd --remove-rich-rule='rule service name="http" accept'
fi
EOF
sudo chmod +x /usr/local/bin/time-firewall.sh
Managing Rich Rules
# List rich rules
firewall-cmd --list-rich-rules
# List rich rules in specific zone
firewall-cmd --zone=public --list-rich-rules
# Remove rich rule
sudo firewall-cmd --permanent --remove-rich-rule='rule family="ipv4" source address="203.0.113.50" service name="ssh" accept'
# Reload to apply permanent rules
sudo firewall-cmd --reload
# Test rule before making permanent
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="203.0.113.50" port port="8080" protocol="tcp" accept'
# If works, make permanent:
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.50" port port="8080" protocol="tcp" accept'
Complex Rich Rule Examples
# Allow SSH only from specific subnet during business hours (via cron)
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="ssh" accept'
# Allow HTTP/HTTPS from anywhere, limit connections
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" service name="http" limit value="100/s" accept'
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" service name="https" limit value="100/s" accept'
# Database access only from application servers
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.10.10" port port="5432" protocol="tcp" accept'
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.10.11" port port="5432" protocol="tcp" accept'
# Log and block suspicious activity
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" port port="23" protocol="tcp" log prefix="TELNET-ATTEMPT: " level="warning" reject'
# Reload
sudo firewall-cmd --reload
Step 6: Port Forwarding and NAT
Enabling Masquerading (NAT)
# Enable masquerading in zone
sudo firewall-cmd --zone=external --add-masquerade
# Make permanent
sudo firewall-cmd --permanent --zone=external --add-masquerade
# Check if masquerading is enabled
firewall-cmd --zone=external --query-masquerade
# Disable masquerading
sudo firewall-cmd --zone=external --remove-masquerade
Port Forwarding
Forward external port to internal server:
# Forward port 80 to 8080 on same server
sudo firewall-cmd --zone=external --add-forward-port=port=80:proto=tcp:toport=8080
# Forward to different server
sudo firewall-cmd --zone=external --add-forward-port=port=80:proto=tcp:toport=80:toaddr=192.168.1.10
# Forward HTTPS to internal web server
sudo firewall-cmd --permanent --zone=external --add-forward-port=port=443:proto=tcp:toport=443:toaddr=192.168.1.10
# Forward range of ports
sudo firewall-cmd --permanent --zone=external --add-forward-port=port=8000-8100:proto=tcp:toport=9000-9100:toaddr=192.168.1.20
Rich rule port forwarding:
# Forward with source restriction
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" forward-port port="3389" protocol="tcp" to-port="3389" to-addr="192.168.1.100"'
# Reload
sudo firewall-cmd --reload
Removing Port Forwarding
# Remove forward rule
sudo firewall-cmd --zone=external --remove-forward-port=port=80:proto=tcp:toport=8080
# Remove permanent rule
sudo firewall-cmd --permanent --zone=external --remove-forward-port=port=80:proto=tcp:toport=8080:toaddr=192.168.1.10
# List forward ports
firewall-cmd --zone=external --list-forward-ports
Step 7: IP Set Management
IP sets allow efficient management of large IP address lists.
Creating and Managing IP Sets
# Create IP set
sudo firewall-cmd --permanent --new-ipset=allowed_ips --type=hash:ip
# Add IPs to set
sudo firewall-cmd --permanent --ipset=allowed_ips --add-entry=203.0.113.10
sudo firewall-cmd --permanent --ipset=allowed_ips --add-entry=203.0.113.20
sudo firewall-cmd --permanent --ipset=allowed_ips --add-entry=203.0.113.30
# Add subnet to IP set
sudo firewall-cmd --permanent --ipset=allowed_ips --add-entry=192.168.1.0/24
# List IP sets
firewall-cmd --get-ipsets
# View IP set entries
firewall-cmd --ipset=allowed_ips --get-entries
# Remove entry from IP set
sudo firewall-cmd --permanent --ipset=allowed_ips --remove-entry=203.0.113.30
# Delete IP set
sudo firewall-cmd --permanent --delete-ipset=allowed_ips
Using IP Sets in Rules
# Allow SSH from IP set
sudo firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source ipset="allowed_ips" service name="ssh" accept'
# Block IP set
sudo firewall-cmd --permanent --new-ipset=blocked_ips --type=hash:ip
sudo firewall-cmd --permanent --ipset=blocked_ips --add-entry=10.0.0.100
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source ipset="blocked_ips" drop'
# Reload
sudo firewall-cmd --reload
IP Set from File
# Create IP list file
cat << EOF | sudo tee /etc/firewalld/ipsets/whitelist.xml
<?xml version="1.0" encoding="utf-8"?>
<ipset type="hash:ip">
<entry>203.0.113.10</entry>
<entry>203.0.113.20</entry>
<entry>192.168.1.0/24</entry>
</ipset>
EOF
# Reload to load IP set
sudo firewall-cmd --reload
Step 8: Direct Rules and iptables
For advanced users who need direct iptables/nftables access.
Adding Direct Rules
# Add direct rule (iptables syntax)
sudo firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 9999 -j ACCEPT
# Make permanent
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 9999 -j ACCEPT
# List direct rules
firewall-cmd --direct --get-all-rules
# Remove direct rule
sudo firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -p tcp --dport 9999 -j ACCEPT
Direct Chains
# Add custom chain
sudo firewall-cmd --permanent --direct --add-chain ipv4 filter CUSTOM_CHAIN
# Add rule to custom chain
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter CUSTOM_CHAIN 0 -j ACCEPT
# Pass traffic to custom chain
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -j CUSTOM_CHAIN
Warning: Direct rules bypass firewalld's zone management. Use only when necessary.
Step 9: Firewalld Lockdown
Lockdown mode prevents unauthorized applications from changing firewall rules.
Enabling Lockdown
# Enable lockdown mode
sudo firewall-cmd --lockdown-on
# Check lockdown status
firewall-cmd --query-lockdown
# Disable lockdown
sudo firewall-cmd --lockdown-off
Configuring Lockdown Whitelist
# Edit lockdown whitelist
sudo nano /etc/firewalld/lockdown-whitelist.xml
Example configuration:
<?xml version="1.0" encoding="utf-8"?>
<whitelist>
<command name="/usr/bin/python3 -s /usr/bin/firewall-config"/>
<command name="/usr/libexec/platform-python -s /usr/sbin/firewall-config"/>
<selinux context="system_u:system_r:NetworkManager_t:s0"/>
<user id="0"/>
<user name="root"/>
</whitelist>
Step 10: Advanced Configuration
Panic Mode
Disable all network traffic instantly (emergency use):
# Enable panic mode (drops all traffic)
sudo firewall-cmd --panic-on
# Check panic mode status
firewall-cmd --query-panic
# Disable panic mode
sudo firewall-cmd --panic-off
ICMP Management
# List available ICMP types
firewall-cmd --get-icmptypes
# Block ICMP ping
sudo firewall-cmd --permanent --add-icmp-block=echo-request
# Allow specific ICMP
sudo firewall-cmd --permanent --remove-icmp-block=echo-request
# Block all ICMP except specific types
sudo firewall-cmd --permanent --add-icmp-block-inversion
sudo firewall-cmd --permanent --add-icmp-block=echo-reply
# List blocked ICMP
firewall-cmd --list-icmp-blocks
Protocols
# Add protocol
sudo firewall-cmd --permanent --add-protocol=igmp
sudo firewall-cmd --permanent --add-protocol=gre
# Remove protocol
sudo firewall-cmd --permanent --remove-protocol=igmp
# List protocols
firewall-cmd --list-protocols
Source Ports
# Add source port
sudo firewall-cmd --permanent --add-source-port=5000/tcp
# Remove source port
sudo firewall-cmd --permanent --remove-source-port=5000/tcp
# List source ports
firewall-cmd --list-source-ports
Helper Modules
# List available helpers
firewall-cmd --get-helpers
# Add helper
sudo firewall-cmd --permanent --add-helper=ftp
# Remove helper
sudo firewall-cmd --permanent --remove-helper=ftp
# List active helpers
firewall-cmd --list-helpers
Verification
Comprehensive Firewall Status Check
# Full firewall status
sudo firewall-cmd --list-all
# Check all zones
sudo firewall-cmd --list-all-zones
# Verify service is running
sudo systemctl status firewalld
# Check if specific service is allowed
firewall-cmd --query-service=ssh
# Check if specific port is open
firewall-cmd --query-port=8080/tcp
# View runtime configuration
firewall-cmd --list-all
# View permanent configuration
firewall-cmd --permanent --list-all
Testing Firewall Rules
# From external machine, test port access
nc -zv server_ip 80
telnet server_ip 80
nmap server_ip
# Check listening services on server
sudo ss -tlnp
sudo netstat -tlnp
# View active connections
sudo ss -tan
# Monitor firewall logs
sudo journalctl -u firewalld -f
sudo tail -f /var/log/messages | grep -i firewall
Firewall Verification Script
# Create verification script
cat << 'EOF' | sudo tee /usr/local/bin/firewall-status.sh
#!/bin/bash
echo "=== Firewalld Status ==="
echo ""
echo "Service Status:"
systemctl is-active firewalld
echo ""
echo "Default Zone:"
firewall-cmd --get-default-zone
echo ""
echo "Active Zones:"
firewall-cmd --get-active-zones
echo ""
echo "Public Zone Configuration:"
firewall-cmd --zone=public --list-all
echo ""
echo "Open Ports:"
sudo ss -tlnp | grep LISTEN
echo ""
echo "Recent Firewall Logs:"
sudo journalctl -u firewalld -n 20 --no-pager
EOF
sudo chmod +x /usr/local/bin/firewall-status.sh
sudo /usr/local/bin/firewall-status.sh
Troubleshooting
Service Not Accessible After Firewall Configuration
Problem: Cannot access service despite firewall rules.
Solution:
# 1. Verify service is running
sudo systemctl status service_name
# 2. Check if service is listening
sudo ss -tlnp | grep :80
# 3. Verify firewall allows service
firewall-cmd --list-services
firewall-cmd --list-ports
# 4. Check if rule is in correct zone
firewall-cmd --get-active-zones
firewall-cmd --zone=public --list-all
# 5. Test from localhost
curl http://localhost
# 6. Check for SELinux denials
sudo ausearch -m AVC -ts recent
# 7. Temporarily disable firewall for testing
sudo systemctl stop firewalld
# Test service
# Re-enable firewall
sudo systemctl start firewalld
Firewalld Won't Start
Problem: Firewalld service fails to start.
Solution:
# Check service status
sudo systemctl status firewalld -l
# View detailed logs
sudo journalctl -u firewalld -n 50 --no-pager
# Check for configuration errors
sudo firewall-cmd --check-config
# Verify no conflicting services
sudo systemctl status iptables
sudo systemctl status nftables
# Reset to default configuration
sudo cp -r /usr/lib/firewalld/* /etc/firewalld/
sudo systemctl restart firewalld
Rules Not Taking Effect
Problem: Added rules but they don't work.
Solution:
# 1. Check if rules are in runtime or permanent
firewall-cmd --list-all # Runtime
firewall-cmd --permanent --list-all # Permanent
# 2. Reload to apply permanent rules
sudo firewall-cmd --reload
# 3. Make runtime rules permanent
sudo firewall-cmd --runtime-to-permanent
# 4. Verify rule syntax for rich rules
firewall-cmd --check-config
# 5. Check zone assignment
firewall-cmd --get-active-zones
firewall-cmd --get-zone-of-interface=eth0
Port Forwarding Not Working
Problem: Port forwarding rules don't forward traffic.
Solution:
# 1. Verify masquerading is enabled
firewall-cmd --zone=external --query-masquerade
# 2. Enable masquerading if needed
sudo firewall-cmd --permanent --zone=external --add-masquerade
# 3. Check forward rules
firewall-cmd --zone=external --list-forward-ports
# 4. Enable IP forwarding in kernel
sudo sysctl -w net.ipv4.ip_forward=1
# Make permanent
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# 5. Verify target machine is reachable
ping target_ip
High CPU Usage
Problem: Firewalld consuming excessive CPU.
Solution:
# Check for excessive rules
firewall-cmd --list-all-zones | wc -l
# Review rich rules (can be CPU intensive)
firewall-cmd --list-rich-rules
# Consider using IP sets for large IP lists
# Consolidate similar rules
# Check for rule loops or conflicts
sudo firewall-cmd --check-config
# Monitor firewalld process
top -p $(pgrep firewalld)
Best Practices
Security Best Practices
- Default deny principle: Start with restrictive rules, open only what's needed
# Set default zone to drop
sudo firewall-cmd --set-default-zone=drop
# Or use public zone with minimal services
sudo firewall-cmd --set-default-zone=public
- Limit SSH access:
# Allow SSH only from specific IPs
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" service name="ssh" accept'
- Rate limiting:
# Limit connection attempts
sudo firewall-cmd --permanent --add-rich-rule='rule service name="ssh" limit value="3/m" accept'
- Log suspicious activity:
# Log dropped packets
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" log prefix="FIREWALL-DROP: " level="warning" drop'
- Regular audits:
# Create audit script
cat << 'EOF' | sudo tee /usr/local/bin/firewall-audit.sh
#!/bin/bash
echo "Firewall Audit - $(date)"
echo "===================="
firewall-cmd --list-all-zones
firewall-cmd --list-rich-rules
firewall-cmd --get-active-zones
EOF
sudo chmod +x /usr/local/bin/firewall-audit.sh
Operational Best Practices
- Test before making permanent:
# Add rule to runtime first
sudo firewall-cmd --add-service=http
# Test thoroughly
# Then make permanent
sudo firewall-cmd --runtime-to-permanent
- Document changes:
# Add comments to rules (via rich rules or external docs)
# Maintain change log
cat >> /root/firewall-changes.log << EOF
$(date): Added HTTP service to public zone - Ticket #12345
EOF
- Backup configuration:
# Backup firewall config
sudo tar -czf /root/firewall-backup-$(date +%Y%m%d).tar.gz /etc/firewalld/
# Automate backups
echo "0 0 * * 0 tar -czf /root/firewall-backup-\$(date +\%Y\%m\%d).tar.gz /etc/firewalld/" | sudo crontab -
- Use zones appropriately:
# Internal network
sudo firewall-cmd --zone=internal --add-interface=eth1
# Public network
sudo firewall-cmd --zone=public --add-interface=eth0
# DMZ for web servers
sudo firewall-cmd --zone=dmz --add-interface=eth2
- Monitor logs:
# Set up log monitoring
sudo journalctl -u firewalld -f
# Create alert script
cat << 'EOF' | sudo tee /usr/local/bin/firewall-monitor.sh
#!/bin/bash
DROPS=$(journalctl -u firewalld -S today | grep -c DROP)
if [ $DROPS -gt 1000 ]; then
echo "High number of firewall drops: $DROPS" | mail -s "Firewall Alert" [email protected]
fi
EOF
sudo chmod +x /usr/local/bin/firewall-monitor.sh
# Add to cron
Performance Optimization
- Use IP sets for large lists
- Consolidate similar rules
- Order rules by frequency (most-used first)
- Avoid excessive logging
- Use services instead of individual ports
Configuration Management
# Export configuration
sudo firewall-cmd --runtime-to-permanent
sudo tar -czf firewall-config.tar.gz /etc/firewalld/
# Import on new server
sudo tar -xzf firewall-config.tar.gz -C /
sudo firewall-cmd --reload
Conclusion
Firewalld provides a powerful, flexible, and user-friendly interface for managing firewall rules on RHEL-based distributions. Through zone-based configuration, service definitions, and rich rule syntax, you can implement comprehensive network security policies.
Key achievements:
- Firewalld installed and configured
- Understanding of zone-based security model
- Service and port management configured
- Advanced rich rules implemented
- Port forwarding and NAT configured
- IP sets for efficient IP management
- Monitoring and troubleshooting procedures established
Security highlights:
- Default deny with explicit allow rules
- Zone-based trust levels
- Rate limiting for brute-force protection
- Logging for security monitoring
- Source-based access restrictions
Next steps:
- Integrate with fail2ban for enhanced protection
- Set up centralized logging
- Implement automated backup procedures
- Regular security audits
- Documentation and team training
Firewalld's dynamic nature, combined with proper configuration and monitoring, provides robust network security for your RHEL-based infrastructure.
Additional Resources
- Firewalld Official Documentation
- RHEL 9 Firewalld Guide
- Firewalld Rich Language
- Firewalld Service Configuration
- Nftables Backend Documentation
Related Guides
- Initial Security Configuration on CentOS/Rocky Linux
- How to Change the Default SSH Port
- iptables Configuration: Complete Guide
- Network Troubleshooting with Commands
- Linux Server Hardening: Complete Guide
- SELinux: Basic Configuration and Modes


