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

Drop all incoming (no reply). Outgoing allowed.

block

Reject all incoming (ICMP prohibited). Outgoing allowed.

public

For public networks. Untrusted. Only selected services.

external

For external networks with masquerading (NAT).

dmz

For DMZ machines. Limited access from internal.

work

Work networks. More trust than public.

home

Home networks. More trust.

internal

Internal networks. Significant trust.

trusted

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

Assign Interfaces to Zones

# Assign interface to zone
firewall-cmd --zone=internal --change-interface=eth1 --permanent

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

# Query interface's zone
firewall-cmd --get-zone-of-interface=eth0

Assign Sources to Zones

# Add source IP/network to zone
firewall-cmd --zone=trusted --add-source=192.168.1.0/24 --permanent

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

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

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:

/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"/>
</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

Common ICMP Types

Type Description

echo-request

Ping request (block to prevent ping)

echo-reply

Ping response

destination-unreachable

Various unreachable messages

time-exceeded

TTL exceeded (traceroute)

parameter-problem

Malformed packet

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

/etc/firewalld/zones/myzone.xml
<?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

Rules Not Applied

# Check if --permanent was used without --reload
firewall-cmd --reload

# Verify permanent config
firewall-cmd --permanent --list-all

# Compare runtime vs permanent
firewall-cmd --list-all
firewall-cmd --permanent --list-all

Reset to Defaults

# Reset zone to default
firewall-cmd --load-zone-defaults=public

# Or remove custom config
rm -rf /etc/firewalld/zones/*
rm -rf /etc/firewalld/services/*
firewall-cmd --reload

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

Panic Mode

# Block ALL traffic (emergency)
firewall-cmd --panic-on

# Disable panic mode
firewall-cmd --panic-off

# Check panic status
firewall-cmd --query-panic

Common Configurations

Web Server

firewall-cmd --add-service={http,https} --permanent
firewall-cmd --reload

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

Docker Host

# Docker manages its own iptables rules
# If using firewalld zones with Docker, you may need:
firewall-cmd --zone=trusted --add-interface=docker0 --permanent
firewall-cmd --reload

Bastion/Jump Host

# Only allow SSH from specific sources
firewall-cmd --remove-service=ssh --permanent
firewall-cmd --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" service name="ssh" accept' --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

See Also