Security

Install and Configure Firewalld on Ubuntu 24.04 / 22.04

Firewalld is a dynamic firewall manager for Linux that supports IPv4, IPv6, Ethernet bridges, and IPSet configurations. It acts as a front-end to the kernel’s netfilter framework, using nftables as its default backend on modern systems. While Ubuntu ships with UFW as its default firewall, firewalld offers zone-based management, rich rules, and runtime-vs-permanent configuration that many administrators prefer – especially those managing mixed RHEL/Ubuntu environments.

Original content from computingforgeeks.com - post 3520

This guide walks through installing, configuring, and mastering firewalld on Ubuntu 24.04 and 22.04. We cover zones, services, rich rules, NAT/masquerading, IP sets, Docker integration, and troubleshooting common issues.

Firewalld vs UFW – When to Use Which

Ubuntu defaults to UFW (Uncomplicated Firewall), which is a simpler front-end to iptables/nftables. Here is how the two compare:

FeatureUFWFirewalld
Default onUbuntu / DebianRHEL / Fedora / SUSE
Zone supportNoYes – multiple zones per interface
Rich rulesNo (requires manual iptables)Yes – built-in rich rule language
Runtime vs permanentAll changes are permanentSeparate runtime and permanent configs
IP setsNo native supportBuilt-in ipset integration
D-Bus interfaceNoYes – programmatic control
Learning curveLowModerate
Best forSingle-interface servers, simple rulesMulti-NIC servers, zone segmentation, complex policies

Use firewalld when you manage multi-NIC servers, need zone-based segmentation, run mixed RHEL/Ubuntu environments, or need rich rules and IP sets. Stick with UFW for simple single-interface servers where basic allow/deny rules are sufficient.

Step 1 – Install Firewalld on Ubuntu

Firewalld is available in the default Ubuntu repositories. Install it with apt.

sudo apt update
sudo apt install firewalld -y

If UFW is currently active, disable it first to avoid conflicts between the two firewalls.

sudo ufw disable

Enable and start the firewalld service.

sudo systemctl enable --now firewalld

Verify the service is running.

sudo firewall-cmd --state

Expected output:

running

Check the installed version.

sudo firewall-cmd --version

Step 2 – Understanding Firewalld Zones

Zones are the core concept in firewalld. Each zone defines a trust level for network connections and interfaces assigned to it. Traffic entering through an interface is handled by the rules of its assigned zone.

Reference Table of Zones

Firewalld ships with these predefined zones, ordered from least to most trusted.

ZoneDefault BehaviorUse Case
dropDrop all incoming, no reply. Outgoing allowed.Stealth mode, honeypots
blockReject all incoming with icmp-host-prohibited. Outgoing allowed.Untrusted networks with rejection notice
publicOnly selected services allowed (ssh, dhcpv6-client by default)Public-facing servers (default zone)
externalMasquerading enabled, only ssh allowedNAT routers, external-facing interface
dmzOnly ssh allowedDMZ servers with limited access
workssh, dhcpv6-client allowedWork networks
homessh, mdns, samba-client, dhcpv6-client allowedHome networks
internalSame as homeInternal/LAN-facing interfaces
trustedAccept all trafficFully trusted networks (use with caution)

List all available zones.

sudo firewall-cmd --get-zones

Check your current default zone.

sudo firewall-cmd --get-default-zone

List all active zones and their assigned interfaces.

sudo firewall-cmd --get-active-zones

View the full configuration of the default zone.

sudo firewall-cmd --list-all

Step 3 – Managing Services and Ports

Firewalld uses predefined service definitions stored in /usr/lib/firewalld/services/. These map service names to their port/protocol combinations. Custom service files go in /etc/firewalld/services/.

List Available Services

See all services that firewalld knows about.

sudo firewall-cmd --get-services

Add a Service

Allow HTTP traffic permanently.

sudo firewall-cmd --add-service=http --permanent

Allow both HTTP and HTTPS in a single command.

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

After any --permanent change, reload firewalld to apply it to the runtime configuration.

sudo firewall-cmd --reload

Add a Port by Number

Open a specific TCP port.

sudo firewall-cmd --add-port=8080/tcp --permanent
sudo firewall-cmd --reload

Open a UDP port.

sudo firewall-cmd --add-port=514/udp --permanent
sudo firewall-cmd --reload

Open a range of ports.

sudo firewall-cmd --add-port=5000-5100/tcp --permanent
sudo firewall-cmd --reload

Remove a Service or Port

To remove a rule, replace --add with --remove.

sudo firewall-cmd --remove-service=http --permanent
sudo firewall-cmd --remove-port=8080/tcp --permanent
sudo firewall-cmd --reload

Verify Current Rules

List all services and ports currently allowed in the default zone.

sudo firewall-cmd --list-services
sudo firewall-cmd --list-ports

Step 4 – Zone-Based Network Segmentation

On multi-NIC servers, you can assign each interface to a different zone. This is useful for separating public traffic from internal management traffic or database connections.

Consider a server with three interfaces: eth0 (public), eth1 (internal app network), and eth2 (management). Assign each to its appropriate zone.

sudo firewall-cmd --zone=public --change-interface=eth0 --permanent
sudo firewall-cmd --zone=internal --change-interface=eth1 --permanent
sudo firewall-cmd --zone=trusted --change-interface=eth2 --permanent

Configure the public zone to allow only web traffic.

sudo firewall-cmd --zone=public --add-service={http,https} --permanent

Allow application-specific ports on the internal zone.

sudo firewall-cmd --zone=internal --add-port=5432/tcp --permanent
sudo firewall-cmd --zone=internal --add-port=6379/tcp --permanent

The trusted zone on eth2 allows all traffic, so no additional rules are needed there. Reload to apply all changes.

sudo firewall-cmd --reload

Verify the zone assignments.

sudo firewall-cmd --get-active-zones

Create a Custom Zone

You can create your own zones for specific use cases.

sudo firewall-cmd --permanent --new-zone=appservers
sudo firewall-cmd --reload
sudo firewall-cmd --zone=appservers --add-port=8080/tcp --permanent
sudo firewall-cmd --zone=appservers --add-port=8443/tcp --permanent
sudo firewall-cmd --reload

Set the default zone for interfaces not explicitly assigned.

sudo firewall-cmd --set-default-zone=public

Step 5 – Rich Rules

Rich rules provide fine-grained control over traffic matching. They support source/destination filtering, logging, rate limiting, and more – all in a readable syntax without touching raw iptables.

Allow a Service from a Specific IP or Subnet

Allow SSH access from a single IP address.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.100/32" service name="ssh" accept'

Allow SSH from an entire subnet.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" service name="ssh" accept'

Rate Limit SSH Connections

Limit incoming SSH connections to 3 per minute per source IP. This helps mitigate brute-force attacks without completely blocking legitimate users who mistype passwords.

sudo firewall-cmd --permanent --add-rich-rule='rule service name="ssh" accept limit value="3/m"'
sudo firewall-cmd --reload

Log and Drop Traffic from a Source

Log and drop all traffic from a known bad IP. The log prefix makes it easy to filter in syslog or journalctl.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.50" log prefix="BLOCKED: " level="warning" drop'
sudo firewall-cmd --reload

Check logs for blocked traffic.

sudo journalctl -k | grep "BLOCKED:"

Port Forwarding with Rich Rules

Forward incoming traffic on port 80 to an internal server on port 8080.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" forward-port port="80" protocol="tcp" to-port="8080" to-addr="10.0.0.5"'
sudo firewall-cmd --reload

Reject a Specific Port with ICMP Message

Reject connections on port 23 (telnet) with a clear rejection message instead of silently dropping.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" port port="23" protocol="tcp" reject type="icmp-port-unreachable"'
sudo firewall-cmd --reload

List and Remove Rich Rules

View all rich rules in the current zone.

sudo firewall-cmd --list-rich-rules

Remove a specific rich rule by passing the exact rule string with --remove-rich-rule.

sudo firewall-cmd --permanent --remove-rich-rule='rule service name="ssh" accept limit value="3/m"'
sudo firewall-cmd --reload

Step 6 – Direct Rules for Advanced iptables-Like Control

Direct rules give you raw access to the iptables/nftables chains through firewalld. Use these when you need something that rich rules cannot express – custom chains, specific ICMP handling, or complex matching. Direct rules are added to the runtime and optionally made permanent.

Block all incoming ICMP echo requests (ping).

sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p icmp --icmp-type echo-request -j DROP
sudo firewall-cmd --reload

Log new incoming connections on port 443 for debugging.

sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 443 -m state --state NEW -j LOG --log-prefix "HTTPS-NEW: "
sudo firewall-cmd --reload

List 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 -p icmp --icmp-type echo-request -j DROP
sudo firewall-cmd --reload

Note: Direct rules are considered legacy in firewalld 1.0+. Prefer rich rules and policies where possible. Direct rules do not integrate with zones and are processed before zone rules.

Step 7 – Masquerading and NAT (Gateway/Router Configuration)

Masquerading (NAT) allows a server to act as a gateway, forwarding traffic from an internal network to the internet. This is common on servers with one public and one private interface.

First, enable IP forwarding in the kernel.

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

Enable masquerading on the public-facing zone.

sudo firewall-cmd --zone=public --add-masquerade --permanent
sudo firewall-cmd --reload

Verify masquerading is enabled.

sudo firewall-cmd --zone=public --query-masquerade

Expected output:

yes

Port Forwarding Through the Gateway

Forward incoming traffic on port 80 of the gateway to an internal web server at 10.0.0.5.

sudo firewall-cmd --zone=public --add-forward-port=port=80:proto=tcp:toport=80:toaddr=10.0.0.5 --permanent
sudo firewall-cmd --reload

Forward port 2222 on the gateway to SSH (port 22) on an internal server.

sudo firewall-cmd --zone=public --add-forward-port=port=2222:proto=tcp:toport=22:toaddr=10.0.0.10 --permanent
sudo firewall-cmd --reload

Step 8 – IP Sets for Whitelist and Blacklist Management

IP sets let you manage groups of IP addresses as a single entity. This is far more efficient than creating individual rich rules for dozens or hundreds of IPs.

Create a Blacklist IP Set

Create an IP set for blocked addresses.

sudo firewall-cmd --permanent --new-ipset=blacklist --type=hash:ip
sudo firewall-cmd --reload

Add IPs to the blacklist.

sudo firewall-cmd --permanent --ipset=blacklist --add-entry=203.0.113.50
sudo firewall-cmd --permanent --ipset=blacklist --add-entry=198.51.100.23
sudo firewall-cmd --reload

Create a rich rule that drops all traffic from the blacklist.

sudo firewall-cmd --permanent --add-rich-rule='rule source ipset="blacklist" drop'
sudo firewall-cmd --reload

Create a Whitelist IP Set

Similarly, create a whitelist for trusted management IPs and allow SSH only from those addresses.

sudo firewall-cmd --permanent --new-ipset=management --type=hash:net
sudo firewall-cmd --reload
sudo firewall-cmd --permanent --ipset=management --add-entry=10.0.0.0/24
sudo firewall-cmd --permanent --ipset=management --add-entry=192.168.1.0/24
sudo firewall-cmd --reload
sudo firewall-cmd --permanent --add-rich-rule='rule source ipset="management" service name="ssh" accept'
sudo firewall-cmd --reload

Manage IP Sets

List all IP sets.

sudo firewall-cmd --get-ipsets

List entries in a specific IP set.

sudo firewall-cmd --ipset=blacklist --get-entries

Remove an entry from an IP set.

sudo firewall-cmd --permanent --ipset=blacklist --remove-entry=203.0.113.50
sudo firewall-cmd --reload

Step 9 – Firewalld with Docker

Docker manipulates iptables directly to expose container ports, which bypasses firewalld entirely. This means containers with published ports are accessible from any source, even if your firewalld rules say otherwise. This is a common security surprise on Ubuntu servers running both Docker and firewalld.

Option 1 – Disable Docker’s iptables Management

Tell Docker not to manipulate iptables. This gives firewalld full control but means you must manually create rules for container networking.

Create or edit the Docker daemon configuration.

sudo tee /etc/docker/daemon.json > /dev/null <<EOF
{
  "iptables": false
}
EOF

Restart Docker.

sudo systemctl restart docker

Now you need to allow container traffic through firewalld manually. For a container exposed on port 8080, add the port to the public zone.

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

If containers need to talk to each other or reach the internet, allow masquerading and trust the Docker bridge.

sudo firewall-cmd --zone=trusted --add-interface=docker0 --permanent
sudo firewall-cmd --zone=public --add-masquerade --permanent
sudo firewall-cmd --reload

Option 2 – Use a Dedicated Docker Zone

A more controlled approach is to create a dedicated zone for Docker traffic while leaving Docker’s iptables management enabled.

sudo firewall-cmd --permanent --new-zone=docker
sudo firewall-cmd --reload
sudo firewall-cmd --zone=docker --add-interface=docker0 --permanent
sudo firewall-cmd --zone=docker --add-masquerade --permanent
sudo firewall-cmd --reload

This keeps Docker traffic isolated in its own zone where you can apply specific policies without affecting your main public zone rules.

Important: After changing Docker’s iptables settings, test that your containers can still reach external services and that inter-container networking works as expected.

Step 10 – Runtime vs Permanent Configuration

Firewalld maintains two separate configurations: runtime (active, lost on restart) and permanent (saved, loaded on restart). This is a key difference from UFW where all changes are permanent.

Add a rule to runtime only (for testing) – omit --permanent.

sudo firewall-cmd --add-port=9090/tcp

This rule takes effect immediately but is lost when firewalld restarts. If the rule works correctly, make it permanent.

sudo firewall-cmd --runtime-to-permanent

Alternatively, save all current runtime rules as the permanent configuration.

sudo firewall-cmd --runtime-to-permanent

To discard runtime changes and revert to the permanent configuration.

sudo firewall-cmd --reload

Troubleshooting Common Firewalld Issues on Ubuntu

Here are problems you will likely encounter when running firewalld on Ubuntu, along with their fixes.

Conflict Between UFW and Firewalld

Running both UFW and firewalld simultaneously causes unpredictable behavior. If services are unreachable after enabling firewalld, check if UFW is still active.

sudo ufw status

If UFW shows active, disable it.

sudo ufw disable
sudo systemctl disable ufw
sudo systemctl mask ufw

Masking the service prevents it from being accidentally re-enabled.

Services Not Reachable After Enabling Firewalld

If your services stop working after enabling firewalld, it is because the default public zone only allows SSH and DHCPv6. Check what is currently allowed.

sudo firewall-cmd --list-all

Add back the services you need.

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

Permanent Rules Not Taking Effect

If you added rules with --permanent but they are not active, you forgot to reload.

sudo firewall-cmd --reload

Firewalld Fails to Start

Check the journal for error details.

sudo journalctl -u firewalld --no-pager -n 50

A common cause is a malformed XML file in /etc/firewalld/. Validate zone files.

sudo firewall-cmd --check-config

Docker Containers Bypassing Firewalld

If Docker containers are accessible from the internet despite your firewalld rules blocking those ports, Docker is inserting its own iptables rules. Refer to Step 9 above for the fix.

Check Which Backend Firewalld Uses

On Ubuntu 24.04, firewalld uses nftables by default. Verify the backend.

grep FirewallBackend /etc/firewalld/firewalld.conf

Expected output:

FirewallBackend=nftables

Useful Firewalld Command Reference

Here is a quick reference of the most commonly used firewalld commands.

CommandPurpose
firewall-cmd --stateCheck if firewalld is running
firewall-cmd --reloadReload permanent rules into runtime
firewall-cmd --list-allShow all rules for the default zone
firewall-cmd --list-all-zonesShow rules for every zone
firewall-cmd --get-active-zonesShow zones with assigned interfaces
firewall-cmd --get-default-zoneShow the default zone name
firewall-cmd --add-service=NAME --permanentAllow a service permanently
firewall-cmd --add-port=PORT/PROTO --permanentOpen a port permanently
firewall-cmd --runtime-to-permanentSave runtime config as permanent
firewall-cmd --panic-onBlock all traffic immediately (emergency)
firewall-cmd --panic-offDisable panic mode

For further reading, refer to the official Firewalld documentation. If you are configuring firewalld on RHEL-based systems, see our guide on configuring firewalld on Rocky Linux / AlmaLinux / RHEL. For the simpler UFW alternative, check UFW firewall usage commands with examples.

Related Articles

Asterisk Install Asterisk 16 LTS on Ubuntu 22.04|20.04|18.04 Security How To Generate Let’s Encrypt Wildcard SSL Certificate Monitoring How To Install Grafana on Ubuntu 24.04 Ubuntu How To Install Ralph CMDB on Ubuntu 22.04|20.04|18.04

Leave a Comment

Press ESC to close