firewalld
Quick Reference
# Status
firewall-cmd --state
firewall-cmd --list-all
# Zones
firewall-cmd --get-active-zones
firewall-cmd --get-default-zone
firewall-cmd --set-default-zone=public
# Services
firewall-cmd --add-service=http --permanent
firewall-cmd --remove-service=http --permanent
# Ports
firewall-cmd --add-port=8080/tcp --permanent
firewall-cmd --remove-port=8080/tcp --permanent
# Apply changes
firewall-cmd --reload
Understanding firewalld
Architecture
┌─────────────────────────────────────────────────────────────┐
│ firewall-cmd / GUI │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ firewalld daemon │
│ ┌───────────────┐ ┌───────────────┐ ┌────────────────┐ │
│ │ Runtime Config│ │Permanent Config│ │ D-Bus API │ │
│ └───────────────┘ └───────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ nftables (backend) │
│ (or iptables on older systems) │
└─────────────────────────────────────────────────────────────┘
Key Concepts
| Concept | Description |
|---|---|
Zone |
Trust level for network connections. Each interface/source belongs to a zone. |
Service |
Predefined set of ports/protocols (e.g., ssh = 22/tcp) |
Port |
Explicit port/protocol combination (e.g., 8080/tcp) |
Rich Rule |
Complex rules with source/destination, logging, actions |
Runtime vs Permanent |
Runtime = active now (lost on reload), Permanent = saved to config |
Interface |
Network interface bound to a zone |
Source |
IP address/range bound to a zone |
Runtime vs Permanent
# Runtime only (immediate, lost on reload/reboot)
firewall-cmd --add-service=http
# Permanent only (saved, requires reload to activate)
firewall-cmd --add-service=http --permanent
firewall-cmd --reload
# Both at once
firewall-cmd --add-service=http --permanent
firewall-cmd --add-service=http
# Or use --runtime-to-permanent after testing
firewall-cmd --add-service=http
# Test it works...
firewall-cmd --runtime-to-permanent
Service Management
Check Status
# Is firewalld running?
firewall-cmd --state
# Systemd status
systemctl status firewalld
# Start/stop/restart
systemctl start firewalld
systemctl stop firewalld
systemctl restart firewalld
# Enable at boot
systemctl enable firewalld
View Configuration
# List everything in default zone
firewall-cmd --list-all
# List all zones
firewall-cmd --list-all-zones
# List specific zone
firewall-cmd --zone=public --list-all
# List just services
firewall-cmd --list-services
# List just ports
firewall-cmd --list-ports
# List rich rules
firewall-cmd --list-rich-rules
Zones
Predefined Zones
| Zone | Description |
|---|---|
|
Drop all incoming (no reply). Outgoing allowed. |
|
Reject all incoming (ICMP prohibited). Outgoing allowed. |
|
For public networks. Untrusted. Only selected services. |
|
For external networks with masquerading (NAT). |
|
For DMZ machines. Limited access from internal. |
|
Work networks. More trust than public. |
|
Home networks. More trust. |
|
Internal networks. Significant trust. |
|
All traffic accepted. |
Zone Operations
# List all zones
firewall-cmd --get-zones
# Get default zone
firewall-cmd --get-default-zone
# Set default zone
firewall-cmd --set-default-zone=home
# Get active zones (zones with interfaces)
firewall-cmd --get-active-zones
# Output:
# public
# interfaces: eth0
# home
# interfaces: wlan0
Create Custom Zone
# Create new zone
firewall-cmd --permanent --new-zone=myzone
# Reload to make available
firewall-cmd --reload
# Configure it
firewall-cmd --zone=myzone --add-service=ssh --permanent
firewall-cmd --zone=myzone --add-port=8080/tcp --permanent
# Delete zone
firewall-cmd --permanent --delete-zone=myzone
firewall-cmd --reload
Services
List Available Services
# List all predefined services
firewall-cmd --get-services
# Get info about a service
firewall-cmd --info-service=http
# Output:
# http
# ports: 80/tcp
# protocols:
# source-ports:
# modules:
# destination:
Add/Remove Services
# Add service to default zone
firewall-cmd --add-service=http --permanent
# Add service to specific zone
firewall-cmd --zone=public --add-service=https --permanent
# Add multiple services
firewall-cmd --add-service={http,https,dns} --permanent
# Remove service
firewall-cmd --remove-service=http --permanent
# Check if service is enabled
firewall-cmd --query-service=http
Create Custom Service
# Create new service
firewall-cmd --permanent --new-service=myapp
# Configure it
firewall-cmd --permanent --service=myapp --set-description="My Application"
firewall-cmd --permanent --service=myapp --set-short="MyApp"
firewall-cmd --permanent --service=myapp --add-port=8080/tcp
firewall-cmd --permanent --service=myapp --add-port=8443/tcp
# Reload and use
firewall-cmd --reload
firewall-cmd --add-service=myapp --permanent
Service Definition Files
Services are defined in 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>
# After creating XML file
firewall-cmd --reload
Ports
Add/Remove Ports
# Add single port
firewall-cmd --add-port=8080/tcp --permanent
# Add port to specific zone
firewall-cmd --zone=public --add-port=8080/tcp --permanent
# Add port range
firewall-cmd --add-port=5000-5100/tcp --permanent
# Add UDP port
firewall-cmd --add-port=53/udp --permanent
# Add both TCP and UDP
firewall-cmd --add-port=123/tcp --add-port=123/udp --permanent
# Remove port
firewall-cmd --remove-port=8080/tcp --permanent
# Query port
firewall-cmd --query-port=8080/tcp
Forward Ports
# Forward port 80 to 8080 on same host
firewall-cmd --add-forward-port=port=80:proto=tcp:toport=8080 --permanent
# Forward port to another host
firewall-cmd --add-forward-port=port=80:proto=tcp:toport=80:toaddr=192.168.1.100 --permanent
# Enable masquerading (required for forwarding to another host)
firewall-cmd --add-masquerade --permanent
# Remove port forward
firewall-cmd --remove-forward-port=port=80:proto=tcp:toport=8080 --permanent
# List port forwards
firewall-cmd --list-forward-ports
Rich Rules
Rich rules provide complex firewall logic.
Syntax
rule [family="<rule family>"]
[ source [NOT] [address="<address>"] [mac="<mac-address>"] [ipset="<ipset>"] ]
[ destination [NOT] [address="<address>"] ]
[ <element> ]
[ log [prefix="<prefix text>"] [level="<log level>"] [limit value="<rate/duration>"] ]
[ audit ]
[ accept|reject|drop|mark set="<mark>/<mask>" ]
Common Rich Rules
# Allow specific IP to SSH
firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.100" service name="ssh" accept' --permanent
# Deny specific IP
firewall-cmd --add-rich-rule='rule family="ipv4" source address="10.0.0.5" drop' --permanent
# Allow subnet to access port
firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="8080" protocol="tcp" accept' --permanent
# Log and accept
firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="ssh" log prefix="SSH-ACCESS: " level="info" accept' --permanent
# Log and drop (useful for monitoring)
firewall-cmd --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" service name="ssh" log prefix="SSH-BLOCKED: " drop' --permanent
# Rate limit connections
firewall-cmd --add-rich-rule='rule service name="ssh" accept limit value="10/m"' --permanent
# Allow established connections
firewall-cmd --add-rich-rule='rule family="ipv4" source address="0.0.0.0/0" service name="ssh" accept' --permanent
Remove Rich Rules
# Remove by exact rule text
firewall-cmd --remove-rich-rule='rule family="ipv4" source address="192.168.1.100" service name="ssh" accept' --permanent
# List rich rules first
firewall-cmd --list-rich-rules
Rich Rule Examples
Allow HTTP from specific network
firewall-cmd --add-rich-rule='
rule family="ipv4"
source address="10.50.0.0/16"
service name="http"
accept
' --permanent
Block country (via ipset)
# Create ipset
firewall-cmd --permanent --new-ipset=blocklist --type=hash:net
# Add IPs to ipset
firewall-cmd --permanent --ipset=blocklist --add-entry=1.2.3.0/24
firewall-cmd --permanent --ipset=blocklist --add-entry=5.6.7.0/24
# Block ipset
firewall-cmd --add-rich-rule='rule source ipset="blocklist" drop' --permanent
Allow only specific IPs to service
# First deny all
firewall-cmd --remove-service=ssh --permanent
# Then allow specific IPs
firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.10" service name="ssh" accept' --permanent
firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.20" service name="ssh" accept' --permanent
Masquerading (NAT)
Enable Masquerading
# Enable on zone
firewall-cmd --zone=external --add-masquerade --permanent
# Check status
firewall-cmd --zone=external --query-masquerade
# Disable
firewall-cmd --zone=external --remove-masquerade --permanent
NAT Router Setup
# External interface in external zone
firewall-cmd --zone=external --change-interface=eth0 --permanent
# Internal interface in internal zone
firewall-cmd --zone=internal --change-interface=eth1 --permanent
# Enable masquerading on external
firewall-cmd --zone=external --add-masquerade --permanent
# Enable IP forwarding
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
sysctl -p
# Reload
firewall-cmd --reload
ICMP Types
Manage ICMP
# List ICMP types
firewall-cmd --get-icmptypes
# Block specific ICMP type
firewall-cmd --add-icmp-block=echo-request --permanent
# Allow blocked ICMP type
firewall-cmd --remove-icmp-block=echo-request --permanent
# Block all ICMP (enable inversion)
firewall-cmd --add-icmp-block-inversion --permanent
# List blocked ICMP types
firewall-cmd --list-icmp-blocks
Direct Rules
Direct rules bypass zones for low-level access (use sparingly).
# Add direct rule (iptables syntax)
firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -s 192.168.1.0/24 -j ACCEPT
# List direct rules
firewall-cmd --direct --get-all-rules
# Remove direct rule
firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -s 192.168.1.0/24 -j ACCEPT
# Permanent direct rules
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -s 192.168.1.0/24 -j ACCEPT
Lockdown Mode
Prevent applications from modifying firewall:
# Enable lockdown
firewall-cmd --lockdown-on
# Disable lockdown
firewall-cmd --lockdown-off
# Check status
firewall-cmd --query-lockdown
NetworkManager Integration
firewalld integrates with NetworkManager:
# Set zone for connection
nmcli connection modify "Wired connection 1" connection.zone internal
# Verify
nmcli connection show "Wired connection 1" | grep zone
# Or in connection file
# /etc/NetworkManager/system-connections/connection.nmconnection
# [connection]
# zone=internal
Configuration Files
Locations
# System defaults (don't modify)
/usr/lib/firewalld/
# Custom configuration
/etc/firewalld/
# Key files
/etc/firewalld/firewalld.conf # Main config
/etc/firewalld/zones/ # Zone configs
/etc/firewalld/services/ # Custom services
/etc/firewalld/ipsets/ # IP sets
Zone XML Format
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>My Zone</short>
<description>Custom zone for internal servers</description>
<service name="ssh"/>
<service name="http"/>
<port protocol="tcp" port="8080"/>
<rule family="ipv4">
<source address="192.168.1.0/24"/>
<service name="mysql"/>
<accept/>
</rule>
</zone>
Troubleshooting
Common Issues
Service Not Accessible
# 1. Check if firewalld is running
systemctl status firewalld
# 2. Check current rules
firewall-cmd --list-all
# 3. Check specific service
firewall-cmd --query-service=http
# 4. Check which zone interface is in
firewall-cmd --get-active-zones
# 5. Check if port is listening
ss -tulnp | grep :80
# 6. Temporarily disable firewall to test
systemctl stop firewalld
# Test connection
systemctl start firewalld
Debug and Logging
# Enable debug logging
firewall-cmd --set-log-denied=all
# Options: all, unicast, broadcast, multicast, off
firewall-cmd --set-log-denied=unicast
# View logs
journalctl -u firewalld
journalctl -k | grep -i "firewall\|REJECT\|DROP"
# Get log-denied setting
firewall-cmd --get-log-denied
Common Configurations
Database Server (Internal Only)
firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="mysql" accept' --permanent
firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="postgresql" accept' --permanent
firewall-cmd --reload
Mail Server
firewall-cmd --add-service={smtp,smtps,submission,imap,imaps,pop3,pop3s} --permanent
firewall-cmd --reload
Quick Command Reference
# Status
firewall-cmd --state # Is running?
firewall-cmd --list-all # Show current zone config
firewall-cmd --list-all-zones # Show all zones
# Zones
firewall-cmd --get-zones # List zones
firewall-cmd --get-default-zone # Show default
firewall-cmd --set-default-zone=home # Set default
firewall-cmd --get-active-zones # Zones with interfaces
firewall-cmd --zone=X --change-interface=eth0 # Assign interface
# Services
firewall-cmd --get-services # List available
firewall-cmd --add-service=http # Add (runtime)
firewall-cmd --add-service=http --permanent # Add (permanent)
firewall-cmd --remove-service=http # Remove
firewall-cmd --query-service=http # Check
# Ports
firewall-cmd --add-port=8080/tcp # Add port
firewall-cmd --add-port=5000-5100/tcp # Add range
firewall-cmd --remove-port=8080/tcp # Remove
# Rich Rules
firewall-cmd --add-rich-rule='...' # Add complex rule
firewall-cmd --list-rich-rules # List rules
# Maintenance
firewall-cmd --reload # Apply permanent changes
firewall-cmd --runtime-to-permanent # Save runtime to permanent
firewall-cmd --panic-on/off # Emergency block all