+
django
+
_
https
+
intellij
nest
stimulus
gradle
pycharm
+
+
+
0b
+
+
+
+
+
+
rb
+
https
+
+
+
+
+
+
alpine
+
symfony
lit
+
?
+
+
+
+
jquery
marko
+
android
+
+
clj
+
bundler
+
puppet
+
+
+
dask
grpc
+
julia
+
numpy
+
parcel
+
ionic
+
+
adonis
+
+
+
gentoo
linux
+
redhat
+
nuxt
+
+
+
torch
+
ios
centos
py
+
+
+
Back to Blog
Firewall Configuration with firewalld on Rocky Linux
Rocky Linux Security Firewall

Firewall Configuration with firewalld on Rocky Linux

Published Jul 27, 2025

Master firewalld configuration on Rocky Linux. Learn zones, services, ports, rich rules, and advanced firewall management for robust network security.

20 min read
0 views
Table of Contents

Firewalld is the default firewall management tool in Rocky Linux, providing a dynamic managed firewall with support for network zones and a D-Bus interface for easy management. This comprehensive guide will teach you how to configure and manage firewalld effectively, from basic concepts to advanced configurations, ensuring your Rocky Linux system is properly protected.

Understanding firewalld

What is firewalld?

firewalld is a firewall service daemon that provides a dynamic customizable host-based firewall with a D-Bus interface. Unlike static firewall solutions, firewalld allows changes to be made without restarting the entire firewall service, preventing existing connections from being lost.

Key Features

  • Dynamic Configuration: Changes without service restart
  • Zone-Based Management: Different trust levels for network connections
  • D-Bus Interface: Allows applications to request firewall changes
  • Runtime and Permanent Configuration: Test changes before making permanent
  • Service and Port Definitions: Pre-configured service definitions
  • Rich Language: Complex firewall rules with rich rule syntax
  • IPv4 and IPv6 Support: Dual-stack firewall configuration
  • Masquerading and Port Forwarding: NAT support

firewalld vs iptables

While firewalld uses iptables/nftables as its backend, it provides several advantages:

# Traditional iptables approach
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
service iptables save
service iptables restart  # Disrupts connections

# firewalld approach
firewall-cmd --add-service=http
firewall-cmd --runtime-to-permanent  # No restart needed

firewalld Architecture and Concepts

Core Components

  1. firewalld Service: The main daemon
  2. firewall-cmd: Command-line tool
  3. firewall-config: GUI configuration tool
  4. firewall-offline-cmd: Offline configuration tool

Configuration Hierarchy

/usr/lib/firewalld/    # Default configurations
├── services/          # Service definitions
├── zones/            # Zone definitions
└── icmptypes/        # ICMP type definitions

/etc/firewalld/       # Custom configurations
├── firewalld.conf    # Main configuration
├── services/         # Custom services
├── zones/           # Custom zones
└── direct.xml       # Direct rules

Runtime vs Permanent Configuration

firewalld maintains two configurations:

  • Runtime: Active configuration (lost on reload/restart)
  • Permanent: Saved configuration (loaded on start)

Basic firewalld Commands

Service Management

# Check firewalld status
sudo systemctl status firewalld

# Start firewalld
sudo systemctl start firewalld

# Enable firewalld at boot
sudo systemctl enable firewalld

# Stop firewalld (not recommended)
sudo systemctl stop firewalld

# Check if firewalld is running
sudo firewall-cmd --state

Basic Operations

# List all active zones
sudo firewall-cmd --get-active-zones

# List all available services
sudo firewall-cmd --get-services

# List all available zones
sudo firewall-cmd --get-zones

# Get default zone
sudo firewall-cmd --get-default-zone

# List everything added to a zone
sudo firewall-cmd --zone=public --list-all

# Reload firewall without losing state
sudo firewall-cmd --reload

# Complete reload (loses state information)
sudo firewall-cmd --complete-reload

Configuration Modes

# Make runtime changes (immediate effect)
sudo firewall-cmd --add-service=http

# Make permanent changes (requires reload)
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload

# Make runtime changes permanent
sudo firewall-cmd --runtime-to-permanent

# Check configuration
sudo firewall-cmd --check-config

Working with Zones

Understanding Zones

Zones define the trust level of network connections. Rocky Linux includes several predefined zones:

  • drop: Lowest trust level, all incoming connections dropped
  • block: All incoming connections rejected with icmp-host-prohibited
  • public: Default zone for public networks
  • external: For external networks with masquerading enabled
  • dmz: For computers in DMZ with limited access
  • work: For work networks
  • home: For home networks
  • internal: For internal networks
  • trusted: All network connections accepted

Zone Management

# Set default zone
sudo firewall-cmd --set-default-zone=home

# Create custom zone
sudo firewall-cmd --permanent --new-zone=customzone
sudo firewall-cmd --reload

# Delete zone
sudo firewall-cmd --permanent --delete-zone=customzone
sudo firewall-cmd --reload

# Assign interface to zone
sudo firewall-cmd --zone=internal --add-interface=eth1

# Change interface zone
sudo firewall-cmd --zone=internal --change-interface=eth1

# Remove interface from zone
sudo firewall-cmd --zone=internal --remove-interface=eth1

Zone Configuration

# List zone configuration
sudo firewall-cmd --zone=public --list-all

# Add source to zone
sudo firewall-cmd --zone=trusted --add-source=192.168.1.0/24

# Remove source from zone
sudo firewall-cmd --zone=trusted --remove-source=192.168.1.0/24

# List sources in zone
sudo firewall-cmd --zone=trusted --list-sources

Custom Zone Example

# Create web server zone
sudo firewall-cmd --permanent --new-zone=webserver
sudo firewall-cmd --reload

# Configure zone
sudo firewall-cmd --permanent --zone=webserver --add-service=http
sudo firewall-cmd --permanent --zone=webserver --add-service=https
sudo firewall-cmd --permanent --zone=webserver --add-service=ssh
sudo firewall-cmd --permanent --zone=webserver --add-source=10.0.0.0/8

# Assign interface
sudo firewall-cmd --permanent --zone=webserver --add-interface=eth0
sudo firewall-cmd --reload

Managing Services

Predefined Services

# List available services
sudo firewall-cmd --get-services

# Show service details
sudo firewall-cmd --info-service=http

# Add service to zone
sudo firewall-cmd --zone=public --add-service=http
sudo firewall-cmd --zone=public --add-service=https

# Remove service from zone
sudo firewall-cmd --zone=public --remove-service=telnet

# Query if service is enabled
sudo firewall-cmd --zone=public --query-service=http

Creating Custom Services

# Create service file
sudo nano /etc/firewalld/services/myapp.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"/>
  <port protocol="udp" port="9000"/>
</service>
# Reload to recognize new service
sudo firewall-cmd --reload

# Use custom service
sudo firewall-cmd --zone=public --add-service=myapp

Service Management Examples

# Web server configuration
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https

# Mail server configuration
sudo firewall-cmd --permanent --zone=public --add-service=smtp
sudo firewall-cmd --permanent --zone=public --add-service=smtps
sudo firewall-cmd --permanent --zone=public --add-service=imap
sudo firewall-cmd --permanent --zone=public --add-service=imaps

# Database server (specific sources)
sudo firewall-cmd --permanent --zone=internal --add-service=mysql
sudo firewall-cmd --permanent --zone=internal --add-source=192.168.1.0/24

# Apply changes
sudo firewall-cmd --reload

Port Management

Opening Ports

# Open single port
sudo firewall-cmd --zone=public --add-port=8080/tcp

# Open port range
sudo firewall-cmd --zone=public --add-port=5000-5010/tcp

# Open UDP port
sudo firewall-cmd --zone=public --add-port=123/udp

# Make permanent
sudo firewall-cmd --permanent --zone=public --add-port=8080/tcp
sudo firewall-cmd --reload

Closing Ports

# Remove port
sudo firewall-cmd --zone=public --remove-port=8080/tcp

# Remove port range
sudo firewall-cmd --zone=public --remove-port=5000-5010/tcp

# Query port status
sudo firewall-cmd --zone=public --query-port=8080/tcp

Port Forwarding

# Enable masquerading (required for port forwarding)
sudo firewall-cmd --zone=public --add-masquerade

# Forward port 80 to 8080
sudo firewall-cmd --zone=public --add-forward-port=port=80:proto=tcp:toport=8080

# Forward to different IP
sudo firewall-cmd --zone=public --add-forward-port=port=80:proto=tcp:toport=8080:toaddr=192.168.1.100

# Remove port forwarding
sudo firewall-cmd --zone=public --remove-forward-port=port=80:proto=tcp:toport=8080

# List port forwards
sudo firewall-cmd --zone=public --list-forward-ports

Rich Rules

Rich rules provide more complex firewall rule capabilities:

Rich Rule Syntax

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

Rich Rule Examples

# Allow specific IP to access SSH
sudo firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4" 
  source address="192.168.1.100" 
  service name="ssh" 
  accept'

# Rate limit connections
sudo firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4" 
  source address="192.168.1.0/24" 
  service name="http" 
  limit value="10/m" 
  accept'

# Log and drop specific traffic
sudo firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4" 
  source address="10.0.0.0/8" 
  port port="22" protocol="tcp" 
  log prefix="SSH_ATTACK" level="warning" 
  drop'

# Time-based rule (requires firewalld >= 0.6.0)
sudo firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4" 
  source address="192.168.1.0/24" 
  service name="ssh" 
  accept 
  limit value="5/h"'

# Port forwarding with rich rule
sudo firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4" 
  forward-port port="443" protocol="tcp" 
  to-port="8443" to-addr="192.168.1.100"'

Managing Rich Rules

# List rich rules
sudo firewall-cmd --zone=public --list-rich-rules

# Remove rich rule
sudo firewall-cmd --zone=public --remove-rich-rule='
  rule family="ipv4" 
  source address="192.168.1.100" 
  service name="ssh" 
  accept'

# Query rich rule
sudo firewall-cmd --zone=public --query-rich-rule='
  rule family="ipv4" 
  source address="192.168.1.100" 
  service name="ssh" 
  accept'

Direct Rules and Custom Chains

For advanced users who need iptables-like functionality:

Direct Rules

# Add direct rule
sudo firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 9000 -j ACCEPT

# Remove direct rule
sudo firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -p tcp --dport 9000 -j ACCEPT

# List direct rules
sudo firewall-cmd --direct --get-all-rules

# Add custom chain
sudo firewall-cmd --direct --add-chain ipv4 filter CUSTOM_CHAIN

# Add rule to custom chain
sudo firewall-cmd --direct --add-rule ipv4 filter CUSTOM_CHAIN 0 -s 192.168.1.0/24 -j ACCEPT

Direct Configuration File

# Edit direct rules
sudo nano /etc/firewalld/direct.xml
<?xml version="1.0" encoding="utf-8"?>
<direct>
  <chain table="filter" ipv="ipv4" chain="CUSTOM_INPUT"/>
  <rule priority="0" table="filter" ipv="ipv4" chain="INPUT">-j CUSTOM_INPUT</rule>
  <rule priority="0" table="filter" ipv="ipv4" chain="CUSTOM_INPUT">-s 192.168.1.0/24 -j ACCEPT</rule>
</direct>

Network Address Translation (NAT)

Masquerading

# Enable masquerading on zone
sudo firewall-cmd --zone=external --add-masquerade

# Query masquerading status
sudo firewall-cmd --zone=external --query-masquerade

# Remove masquerading
sudo firewall-cmd --zone=external --remove-masquerade

SNAT Configuration

# Source NAT with rich rules
sudo firewall-cmd --zone=external --add-rich-rule='
  rule family="ipv4" 
  source address="192.168.1.0/24" 
  masquerade'

DNAT Configuration

# Destination NAT (port forwarding)
sudo firewall-cmd --zone=external --add-rich-rule='
  rule family="ipv4" 
  destination address="203.0.113.1" 
  forward-port port="80" protocol="tcp" 
  to-port="8080" to-addr="192.168.1.100"'

Complete NAT Example

# Router/Gateway configuration
# External interface: eth0 (public zone)
# Internal interface: eth1 (internal zone)

# Enable IP forwarding
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-ip-forward.conf
sudo sysctl -p /etc/sysctl.d/99-ip-forward.conf

# Configure zones
sudo firewall-cmd --permanent --zone=external --add-interface=eth0
sudo firewall-cmd --permanent --zone=internal --add-interface=eth1

# Enable masquerading on external
sudo firewall-cmd --permanent --zone=external --add-masquerade

# Allow internal network services
sudo firewall-cmd --permanent --zone=internal --add-service=dhcp
sudo firewall-cmd --permanent --zone=internal --add-service=dns

# Port forwarding example
sudo firewall-cmd --permanent --zone=external --add-forward-port=port=80:proto=tcp:toport=80:toaddr=192.168.1.100

# Apply configuration
sudo firewall-cmd --reload

Logging and Monitoring

Enable Logging

# Set log denied packets
sudo firewall-cmd --set-log-denied=all

# Options: all, unicast, broadcast, multicast, off
sudo firewall-cmd --get-log-denied

# Enable logging for specific rules
sudo firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4" 
  source address="10.0.0.0/8" 
  log prefix="DENIED_10NET" level="info" 
  drop'

Configure Logging

# Edit firewalld configuration
sudo nano /etc/firewalld/firewalld.conf

# Set logging options
# LogDenied=all
# IndividualCalls=yes
# LogFile=/var/log/firewalld

Monitoring Commands

# Watch firewall logs
sudo journalctl -u firewalld -f

# View denied packets
sudo journalctl -u firewalld | grep DENIED

# Monitor in real-time
sudo tail -f /var/log/firewalld

# Check packet counts
sudo iptables -L -n -v

# Monitor connections
sudo ss -tuln
sudo netstat -tuln

Logging Script

#!/bin/bash
# /usr/local/bin/firewall-monitor.sh

LOG_FILE="/var/log/firewall-monitor.log"
ALERT_EMAIL="[email protected]"

# Function to log messages
log_message() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

# Check for denied packets
DENIED_COUNT=$(journalctl -u firewalld --since "5 minutes ago" | grep -c "DENIED")
if [ $DENIED_COUNT -gt 100 ]; then
    log_message "WARNING: High number of denied packets: $DENIED_COUNT"
    echo "Firewall Alert: $DENIED_COUNT denied packets in last 5 minutes" | mail -s "Firewall Alert" $ALERT_EMAIL
fi

# Check for specific attacks
SSH_ATTACKS=$(journalctl -u firewalld --since "5 minutes ago" | grep -c "DPT=22")
if [ $SSH_ATTACKS -gt 50 ]; then
    log_message "WARNING: Possible SSH brute force attack: $SSH_ATTACKS attempts"
fi

# Check firewall service status
if ! systemctl is-active --quiet firewalld; then
    log_message "CRITICAL: firewalld service is not running!"
fi

Security Best Practices

1. Default Deny Policy

# Use restrictive default zone
sudo firewall-cmd --set-default-zone=drop

# Only allow specific services
sudo firewall-cmd --permanent --zone=drop --add-service=ssh
sudo firewall-cmd --permanent --zone=drop --add-source=192.168.1.0/24

2. Minimize Open Ports

# Audit open ports
for zone in $(firewall-cmd --get-zones); do
    echo "Zone: $zone"
    sudo firewall-cmd --zone=$zone --list-ports
    sudo firewall-cmd --zone=$zone --list-services
done

# Remove unnecessary services
sudo firewall-cmd --permanent --zone=public --remove-service=cockpit
sudo firewall-cmd --permanent --zone=public --remove-service=dhcpv6-client

3. Use Specific Sources

# Instead of opening SSH to all
# sudo firewall-cmd --zone=public --add-service=ssh

# Restrict to specific networks
sudo firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4" 
  source address="192.168.1.0/24" 
  service name="ssh" 
  accept'

4. Rate Limiting

# Limit SSH connections
sudo firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4" 
  service name="ssh" 
  limit value="3/m" 
  accept'

# Limit HTTP connections
sudo firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4" 
  service name="http" 
  limit value="100/s" 
  accept'

5. Implement Fail2ban

# Install fail2ban
sudo dnf install -y fail2ban

# Configure for firewalld
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

# Set banaction to firewalld
# banaction = firewallcmd-ipset

# Start fail2ban
sudo systemctl enable --now fail2ban

6. Regular Auditing

#!/bin/bash
# /usr/local/bin/firewall-audit.sh

echo "=== Firewall Audit Report ==="
echo "Date: $(date)"
echo

echo "=== Active Zones ==="
firewall-cmd --get-active-zones

echo -e "\n=== Zone Configurations ==="
for zone in $(firewall-cmd --get-zones); do
    echo -e "\nZone: $zone"
    firewall-cmd --zone=$zone --list-all
done

echo -e "\n=== Direct Rules ==="
firewall-cmd --direct --get-all-rules

echo -e "\n=== Rich Rules ==="
for zone in $(firewall-cmd --get-zones); do
    rules=$(firewall-cmd --zone=$zone --list-rich-rules)
    if [ -n "$rules" ]; then
        echo "Zone $zone:"
        echo "$rules"
    fi
done

echo -e "\n=== Potential Issues ==="
# Check for permissive zones
if firewall-cmd --get-default-zone | grep -q "trusted"; then
    echo "WARNING: Default zone is set to 'trusted'"
fi

# Check for any allow-all rules
if firewall-cmd --direct --get-all-rules | grep -q "ACCEPT"; then
    echo "WARNING: Direct ACCEPT rules found"
fi

Troubleshooting Common Issues

Service Not Accessible

# Verify firewall is running
sudo firewall-cmd --state

# Check if service/port is open
sudo firewall-cmd --list-all

# Check specific zone
sudo firewall-cmd --zone=public --list-all

# Verify runtime vs permanent
sudo firewall-cmd --list-all
sudo firewall-cmd --permanent --list-all

# Test from outside
telnet server_ip port
nc -zv server_ip port

Configuration Not Persisting

# Ensure permanent flag is used
sudo firewall-cmd --permanent --add-service=http

# Or save runtime to permanent
sudo firewall-cmd --runtime-to-permanent

# Verify permanent configuration
sudo firewall-cmd --permanent --list-all

# Check configuration files
ls -la /etc/firewalld/zones/

Zone Assignment Issues

# Check interface zone assignment
sudo firewall-cmd --get-zone-of-interface=eth0

# Force zone assignment
sudo firewall-cmd --permanent --zone=public --change-interface=eth0
sudo firewall-cmd --reload

# Check NetworkManager interference
nmcli connection show
nmcli connection modify "System eth0" connection.zone public

Debugging Steps

# Enable debug logging
sudo firewall-cmd --set-log-denied=all

# Check logs
sudo journalctl -u firewalld -f

# Test with verbose output
sudo firewall-cmd --reload --debug

# Check iptables rules directly
sudo iptables -L -n -v
sudo ip6tables -L -n -v

# Verify nftables (if backend)
sudo nft list ruleset

Recovery Procedures

# If locked out, boot in emergency mode
# Add to kernel parameters: systemd.unit=emergency.target

# Disable firewalld temporarily
systemctl stop firewalld
systemctl disable firewalld

# Reset to defaults
sudo rm -rf /etc/firewalld/zones/*
sudo rm -rf /etc/firewalld/services/*
sudo firewall-cmd --reload

# Restore from backup
sudo cp -r /root/firewalld-backup/* /etc/firewalld/
sudo firewall-cmd --reload

Advanced Configurations

High Availability Firewall

# Configure connection tracking sync
sudo dnf install -y conntrack-tools

# Master node configuration
cat > /etc/conntrackd/conntrackd.conf << EOF
Sync {
    Mode FTFW {
        ResendQueueSize 131072
        PurgeTimeout 60
    }
    Multicast {
        IPv4_address 225.0.0.50
        Group 3780
        IPv4_interface 192.168.1.1
        Interface eth1
        SndSocketBuffer 1249280
        RcvSocketBuffer 1249280
        Checksum on
    }
}
EOF

GeoIP Blocking

# Install GeoIP database
sudo dnf install -y geoipupdate

# Create update script
cat > /usr/local/bin/geoip-block.sh << 'EOF'
#!/bin/bash
# Block specific countries

COUNTRIES="CN RU KP"  # China, Russia, North Korea

for country in $COUNTRIES; do
    for ip in $(geoiplookup -f /usr/share/GeoIP/GeoIP.dat -i $country | awk '{print $2}'); do
        firewall-cmd --permanent --zone=drop --add-source=$ip
    done
done

firewall-cmd --reload
EOF

chmod +x /usr/local/bin/geoip-block.sh

DDoS Protection

# Rate limiting rules
# Limit new connections
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 \
    -p tcp -m conntrack --ctstate NEW -m limit --limit 60/second --limit-burst 20 -j ACCEPT

# Drop excessive connections
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 \
    -p tcp -m conntrack --ctstate NEW -j DROP

# SYN flood protection
sudo sysctl -w net.ipv4.tcp_syncookies=1
echo "net.ipv4.tcp_syncookies=1" >> /etc/sysctl.conf

Conclusion

firewalld provides a powerful and flexible firewall solution for Rocky Linux systems. Key takeaways:

  • Use zones to manage different trust levels
  • Leverage predefined services when possible
  • Implement rich rules for complex requirements
  • Always test runtime before making permanent
  • Regular audit firewall configurations
  • Monitor logs for security events
  • Follow the principle of least privilege
  • Document your firewall rules

Remember that a properly configured firewall is essential for system security, but it’s just one layer of defense. Combine firewalld with other security measures like SELinux, intrusion detection systems, and regular security updates for comprehensive protection.