Security

Install and Configure Firewalld on Debian 13 / Ubuntu 24.04

Firewalld is a dynamic firewall manager for Linux that uses zones to define trust levels for network connections. It sits on top of nftables (or iptables on older systems) and provides a D-Bus interface for runtime and permanent configuration changes – no restart required when you modify rules. The latest release is firewalld 2.4.0 (November 2025), which runs nftables as its default backend on all modern distributions.

Original content from computingforgeeks.com - post 22109

This guide covers installing and configuring firewalld on Debian 13 (Trixie) and Ubuntu 24.04 LTS. We walk through zones, services, ports, rich rules, port forwarding, masquerading, and network segmentation. Both distributions ship firewalld in their default repositories, making installation straightforward. For the official project documentation and source code, see the firewalld project site.

Prerequisites

Before starting, confirm you have the following in place:

  • A server running Debian 13 or Ubuntu 24.04 LTS
  • Root or sudo access
  • SSH access (if working remotely – make sure port 22 stays open throughout configuration)
  • UFW disabled if previously active (firewalld and UFW conflict)

Step 1: Install Firewalld on Debian 13 / Ubuntu 24.04

Firewalld is available in the default repositories on both Debian and Ubuntu. If you are currently using UFW, disable it first to avoid conflicts.

sudo ufw disable 2>/dev/null; sudo systemctl stop ufw 2>/dev/null

Update the package index and install firewalld:

sudo apt update
sudo apt install firewalld -y

Enable and start the firewalld service so it persists across reboots:

sudo systemctl enable --now firewalld

Verify the service is active and check the installed version:

sudo firewall-cmd --version
sudo systemctl status firewalld --no-pager

The version output shows the firewalld release, and the status should display active (running):

2.1.x
● firewalld.service - firewalld - dynamic firewall daemon
     Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; preset: enabled)
     Active: active (running) since Sat 2026-03-22 10:15:32 UTC; 5s ago
       Docs: man:firewalld(1)
   Main PID: 1542 (firewalld)
      Tasks: 2 (limit: 4652)
     Memory: 28.4M
        CPU: 512ms
     CGroup: /system.slice/firewalld.service
             └─1542 /usr/bin/python3 /usr/sbin/firewalld --nofork --nopid

Confirm the firewall backend is nftables (the default on modern systems):

sudo firewall-cmd --get-log-denied
sudo grep FirewallBackend /etc/firewalld/firewalld.conf

You should see FirewallBackend=nftables in the configuration output. This is the recommended backend.

Step 2: Understand Firewalld Zones

Zones are the central concept in firewalld. Each zone defines a trust level for network connections. Network interfaces and source addresses are assigned to zones, and each zone has its own set of allowed services, ports, and rules. Traffic that matches no specific zone falls into the default zone.

List all available zones:

sudo firewall-cmd --get-zones

The output lists every predefined zone:

block dmz drop external home internal nm-shared public trusted work

Check which zone is currently the default:

sudo firewall-cmd --get-default-zone

On a fresh install, the default zone is public. To see all active zones and which interfaces are assigned to them:

sudo firewall-cmd --get-active-zones

Typical output on a single-interface server:

public
  interfaces: eth0

To see everything configured in the active zone (services, ports, rules):

sudo firewall-cmd --list-all

This shows the complete configuration for the default zone including allowed services, ports, rich rules, and forwarding settings.

Step 3: Configure the Default Zone

The default zone applies to any interface not explicitly assigned elsewhere. For a public-facing server, public is appropriate. For an internal application server behind a firewall, internal or work may be better suited.

Change the default zone (example – switching to internal):

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

The change takes effect immediately and persists across reboots. Verify the change:

sudo firewall-cmd --get-default-zone

For most public-facing servers, keep public as the default and add only the services you need. Switch back if you changed it:

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

You can also assign a specific interface to a zone without changing the default:

sudo firewall-cmd --zone=internal --change-interface=eth1 --permanent
sudo firewall-cmd --reload

Step 4: Add Services and Ports in Firewalld

Firewalld uses service definitions to manage common protocols. A service maps a friendly name (like http) to one or more port/protocol combinations. This is the preferred method for allowing traffic since it is self-documenting and easier to audit.

Allow a Service

List all available service definitions:

sudo firewall-cmd --get-services

Allow SSH, HTTP, and HTTPS in the public zone permanently:

sudo firewall-cmd --zone=public --add-service=ssh --permanent
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --zone=public --add-service=https --permanent
sudo firewall-cmd --reload

The --permanent flag writes the rule to the persistent configuration. The --reload applies permanent rules to the running configuration. Without --permanent, rules are runtime-only and lost on reboot.

Verify the services are active:

sudo firewall-cmd --zone=public --list-services

The output confirms all three services are allowed:

dhcpv6-client http https ssh

Allow a Specific Port

For applications that do not have a predefined service definition, add ports directly. Allow TCP port 8080 and UDP port 5353:

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

Allow a range of ports (useful for services like passive FTP or RTP media):

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

List all open ports to confirm:

sudo firewall-cmd --zone=public --list-ports

Remove a Service or Port

Remove rules the same way you add them, replacing --add with --remove:

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

Step 5: Configure Rich Rules

Rich rules give you fine-grained control that simple service and port rules cannot provide. They support source/destination filtering, logging, rate limiting, and accept/reject/drop actions – all in a single rule.

Allow Access from a Specific IP

Allow SSH only from a management subnet (10.0.1.0/24):

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

Block a Specific IP Address

Drop all traffic from a known malicious IP:

sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.100" drop' --permanent
sudo firewall-cmd --reload

Rate Limit Connections

Limit SSH connections to 3 per minute per source IP to slow down brute-force attempts. This works well alongside other firewall configurations:

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

Log Dropped Packets

Log and then drop traffic on port 3306 (MySQL) from external sources. The log prefix makes it easy to filter in syslog:

sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" port port="3306" protocol="tcp" log prefix="mysql-blocked:" level="warning" drop' --permanent
sudo firewall-cmd --reload

View all configured rich rules in the current zone:

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

Step 6: Configure Port Forwarding

Port forwarding redirects incoming traffic on one port to another port or a different server. This is common for exposing internal services through a gateway host.

Forward a Local Port

Forward TCP port 8080 to local port 80 (useful when an application must listen on a non-privileged port but you want users to access it on 8080):

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

Forward to Another Server

Forward incoming traffic on port 443 to an internal web server at 10.0.1.50. This requires masquerading to be enabled (covered in Step 8):

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

IP forwarding must be enabled at the kernel level for cross-host forwarding. Enable it:

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

Verify that forwarding is active:

sysctl net.ipv4.ip_forward

The output should show net.ipv4.ip_forward = 1.

Step 7: Direct Rules (nftables Passthrough)

Direct rules let you pass raw nftables or iptables commands through firewalld. Use these sparingly – only when the standard firewalld interface cannot express what you need. Direct rules bypass the zone logic, so they are harder to audit.

Block all incoming ICMP echo requests (ping) using a direct rule:

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

List all direct rules:

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

Remove the direct rule when no longer needed:

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

Note: Direct rules are considered legacy. The firewalld project recommends using rich rules or policies instead. Direct rules may be removed in a future major version.

Step 8: Zone-Based Network Segmentation

On servers with multiple network interfaces, zones provide clean network segmentation. Assign each interface to a zone based on its trust level – public-facing interfaces go to the public or external zone, while internal network interfaces go to internal or trusted.

Assign interfaces to different zones:

sudo firewall-cmd --zone=external --change-interface=eth0 --permanent
sudo firewall-cmd --zone=internal --change-interface=eth1 --permanent
sudo firewall-cmd --reload

Verify the assignment:

sudo firewall-cmd --get-active-zones

The output confirms each interface is in its assigned zone:

external
  interfaces: eth0
internal
  interfaces: eth1

Now configure each zone independently. Allow only SSH and HTTPS on the external zone:

sudo firewall-cmd --zone=external --add-service=ssh --permanent
sudo firewall-cmd --zone=external --add-service=https --permanent
sudo firewall-cmd --reload

Allow broader access on the internal zone for backend services:

sudo firewall-cmd --zone=internal --add-service=ssh --permanent
sudo firewall-cmd --zone=internal --add-service=mysql --permanent
sudo firewall-cmd --zone=internal --add-service=nfs --permanent
sudo firewall-cmd --zone=internal --add-service=dns --permanent
sudo firewall-cmd --reload

You can also assign source IP ranges to zones. This is useful when you want all traffic from a specific subnet handled by a particular zone, regardless of which interface it arrives on:

sudo firewall-cmd --zone=trusted --add-source=10.0.1.0/24 --permanent
sudo firewall-cmd --reload

Traffic from 10.0.1.0/24 now goes through the trusted zone, which allows all connections by default.

Step 9: Masquerading and NAT

Masquerading (also called source NAT) rewrites the source address of outgoing packets to the gateway’s external IP. This is essential when you are routing traffic from an internal network to the internet through a Linux gateway. If you are configuring a firewall gateway for your network, masquerading is a core requirement.

Enable masquerading on the external zone:

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

Verify masquerading is enabled:

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

The output should return yes. With masquerading active and IP forwarding enabled (from Step 6), internal hosts on eth1 can reach the internet through the gateway.

For a complete NAT gateway setup, make sure IP forwarding is on and both zones are configured:

sysctl net.ipv4.ip_forward
sudo firewall-cmd --zone=external --list-all
sudo firewall-cmd --zone=internal --list-all

This gives you a full picture of what traffic is allowed on each side of the gateway.

Firewalld Zones Reference Table

The following table summarizes all predefined firewalld zones and their default behavior. Choose the zone that best matches the trust level of each network segment:

ZoneDefault BehaviorTypical Use Case
dropDrops all incoming, no reply sentMaximum security – stealth mode
blockRejects all incoming with icmp-host-prohibitedSimilar to drop but sends rejection notice
publicAllows selected services only (default: ssh, dhcpv6-client)Internet-facing servers (default zone)
externalLike public with masquerading enabledNAT gateways, external router interfaces
dmzAllows ssh onlyDMZ servers with limited access
workAllows ssh, dhcpv6-clientCorporate/office networks
homeAllows ssh, mdns, samba-client, dhcpv6-clientHome networks with local services
internalSame defaults as homeInternal/backend networks
trustedAllows all trafficFully trusted networks (use with caution)

Useful Firewalld Management Commands

Here are everyday commands you will use when managing firewalld in production:

Reload firewalld to apply permanent changes without dropping connections:

sudo firewall-cmd --reload

Show the complete configuration of a specific zone:

sudo firewall-cmd --zone=public --list-all

Show configuration across all zones:

sudo firewall-cmd --list-all-zones

Check if a specific service is allowed:

sudo firewall-cmd --zone=public --query-service=https

Switch firewalld to panic mode (blocks ALL traffic – use only in emergencies):

sudo firewall-cmd --panic-on

Disable panic mode to restore normal operation:

sudo firewall-cmd --panic-off

Create a custom service definition for an application that uses a non-standard port. For the cPanel firewalld configuration or any custom app, define the service in XML:

sudo firewall-cmd --permanent --new-service=myapp
sudo firewall-cmd --permanent --service=myapp --set-description="My Custom Application"
sudo firewall-cmd --permanent --service=myapp --add-port=9090/tcp
sudo firewall-cmd --permanent --service=myapp --add-port=9091/tcp
sudo firewall-cmd --reload

Then add the custom service to a zone like any built-in service:

sudo firewall-cmd --zone=public --add-service=myapp --permanent
sudo firewall-cmd --reload

Conclusion

You now have firewalld installed and configured on Debian 13 or Ubuntu 24.04 with zones, services, ports, rich rules, port forwarding, and masquerading. Firewalld replaces the complexity of raw nftables rules with a structured zone-based approach that is straightforward to manage and audit.

For production environments, review your zone assignments regularly, keep the allowed services list minimal, and use rich rules for IP-based access control on sensitive services like SSH and databases. Pair firewalld with SSH hardening (key-only auth, non-standard port) and centralized log monitoring for a strong security posture. Check the firewalld GitHub repository for release notes and updates.

Related Articles

Ubuntu Configure Postfix as Send-Only SMTP Server on Ubuntu 24.04 / 22.04 Ubuntu Installing UVdesk Ticketing System On Ubuntu 22.04|20.04|18.04 Security Automate Penetration Testing with Infection Monkey Prometheus Monitor Apache with Prometheus and Grafana in 5 minutes

Leave a Comment

Press ESC to close