AlmaLinux

Configure Firewalld on Rocky Linux 10 / AlmaLinux 10 / RHEL 10

Firewalld is the default firewall management tool on RHEL-based Linux distributions. It provides a dynamic, zone-based firewall that sits on top of the nftables framework, giving you a high-level interface to manage traffic rules without writing raw nftables syntax. On Rocky Linux 10, AlmaLinux 10, and RHEL 10, firewalld uses nftables as its backend – the legacy iptables backend is no longer the default.

Original content from computingforgeeks.com - post 1165

This guide covers everything you need to manage firewalld in production – from basic zone and service management to rich rules, port forwarding, source-based filtering, and Docker integration. Every command here works on Rocky Linux 10, AlmaLinux 10, and RHEL 10.

What is Firewalld

Firewalld is a firewall management daemon that controls the Linux kernel’s netfilter subsystem. Instead of writing nftables rules directly, you use the firewall-cmd command-line tool to add services, ports, and rules. Changes take effect immediately without dropping active connections – that is the “dynamic” part.

The core concept in firewalld is zones. Every network interface and source address gets assigned to a zone, and each zone defines what traffic is allowed. The default zone for new interfaces is public, which only allows SSH and DHCPv6 client traffic. You can create custom zones or use the predefined ones to match your network topology – public-facing servers, internal networks, DMZ segments, and trusted management subnets each get their own rules.

On RHEL 10 and its derivatives, firewalld uses nftables as its backend. The older iptables backend is still available but deprecated. Configuration files are stored as XML in /usr/lib/firewalld/ (defaults) and /etc/firewalld/ (custom overrides). Files in /etc/firewalld/ take priority over the defaults. For full technical details, see the official firewalld documentation.

Prerequisites

  • A server running Rocky Linux 10, AlmaLinux 10, or RHEL 10
  • Root access or a user with sudo privileges
  • SSH access to the server (make sure you do not lock yourself out while configuring rules)

Step 1: Install and Enable Firewalld

Firewalld comes pre-installed on Rocky Linux 10, AlmaLinux 10, and RHEL 10 minimal installations. If it is missing for any reason, install it with dnf.

sudo dnf install firewalld -y

Start the service and enable it to persist across reboots.

sudo systemctl enable --now firewalld

Verify that firewalld is running. The status should show active (running).

sudo systemctl status firewalld

The output confirms the service is active and loaded.

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

You can also check the firewalld state with.

sudo firewall-cmd --state

A running firewall returns a single word.

running

Step 2: Understanding Firewalld Zones

Zones are the foundation of firewalld. Each zone defines a trust level for network connections. When a packet arrives, firewalld checks in this order: source address rules first, then interface bindings, and finally the default zone. The first match wins.

List all available zones on your system.

sudo firewall-cmd --get-zones

The output lists all predefined zones.

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

Here is what each key zone does.

ZoneDefault Behavior
publicDefault zone. Rejects incoming traffic except SSH and DHCPv6 client. Use for servers facing the internet.
trustedAccepts all incoming traffic. Use only for fully trusted networks like a management VLAN.
dropDrops all incoming traffic silently – no ICMP rejection. Use when you want to make a host invisible.
blockRejects all incoming traffic with an ICMP host-prohibited message.
dmzAllows only SSH. Designed for servers in a demilitarized zone with limited internal access.
internalAllows SSH, mDNS, Samba client, IPP client, and DHCPv6 client. For internal-facing interfaces.
homeSame as internal. For home network environments.
workAllows SSH, IPP client, and DHCPv6 client. For workplace networks.
externalAllows only SSH with masquerading enabled. For router/NAT scenarios on external interfaces.

Check the current default zone.

sudo firewall-cmd --get-default-zone

On a fresh install, this returns public.

public

To change the default zone – for example, setting it to dmz for a server in a demilitarized zone.

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

This change is permanent and takes effect immediately. Verify with.

sudo firewall-cmd --get-default-zone

View all active zones and their assigned interfaces.

sudo firewall-cmd --get-active-zones

The output shows which interfaces or sources are assigned to each zone.

public
  interfaces: eth0

To see every detail of a specific zone including its services, ports, and rules.

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

The full zone configuration is displayed.

public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0
  sources:
  services: cockpit dhcpv6-client ssh
  ports:
  protocols:
  forward: yes
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules: 

Step 3: Managing Services in Firewalld

Firewalld ships with predefined service definitions that map friendly names to port numbers. Instead of remembering that HTTPS is TCP port 443, you just allow the https service. Service definitions are XML files stored in /usr/lib/firewalld/services/.

List all available predefined services.

sudo firewall-cmd --get-services

This prints a long list of services like ssh, http, https, dns, mysql, postgresql, nfs, and many more.

Runtime vs Permanent Rules

Firewalld has two configuration layers. Runtime rules are active immediately but lost on reload or reboot. Permanent rules are saved to disk but not active until you reload. In production, always add rules with --permanent and then reload.

Adding Services

Allow HTTP and HTTPS traffic permanently in the default zone.

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

You can also add multiple services at once using brace expansion.

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

To add a service to a specific zone instead of the default.

sudo firewall-cmd --permanent --zone=internal --add-service=samba
sudo firewall-cmd --reload

Listing Active Services

Check which services are currently allowed in the default zone.

sudo firewall-cmd --list-services

The output shows all permitted services in the active zone.

cockpit dhcpv6-client dns http https ssh

Removing Services

Remove a service from the allowed list permanently.

sudo firewall-cmd --permanent --remove-service=http
sudo firewall-cmd --reload

Verify the service was removed.

sudo firewall-cmd --list-services

Step 4: Managing Ports

When there is no predefined service for an application, open the port directly by number and protocol. This is common for custom applications, non-standard ports, and third-party software.

Adding Ports

Allow TCP port 8080 permanently.

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

Allow a UDP port – for example, UDP 514 for syslog.

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

Port Ranges

Open a range of ports using a dash separator. This example allows TCP ports 5000 through 5100.

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

Listing Open Ports

View all ports open in the default zone.

sudo firewall-cmd --list-ports

The output lists each open port with its protocol.

8080/tcp 514/udp 5000-5100/tcp

Removing Ports

Remove a port when it is no longer needed.

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

Step 5: Managing Rich Rules

Rich rules give you fine-grained control when basic service and port rules are not enough. They let you combine source addresses, ports, protocols, actions (accept, reject, drop), logging, and rate limiting in a single rule. If you need to manage access from specific IP addresses, consider Fail2ban for automated blocking of brute-force attempts.

Allow a Specific IP to a Port

Allow traffic from IP 10.0.1.50 to TCP port 3306 (MySQL).

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.1.50" port port="3306" protocol="tcp" accept'
sudo firewall-cmd --reload

Allow a Subnet to a Port

Allow the entire 192.168.1.0/24 subnet to access PostgreSQL on port 5432.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="5432" protocol="tcp" accept'
sudo firewall-cmd --reload

Block a Specific IP

Drop all traffic from a malicious IP address.

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

Rate Limiting Connections

Limit SSH connections to 3 per minute from any single source. This helps protect against brute-force login attempts.

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

Logging Traffic

Log and then drop traffic from a specific subnet. The log prefix helps you filter messages in /var/log/messages.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="198.51.100.0/24" log prefix="BLOCKED-NET" level="warning" drop'
sudo firewall-cmd --reload

Listing Rich Rules

View all rich rules in the default zone.

sudo firewall-cmd --list-rich-rules

Removing Rich Rules

To remove a rich rule, pass the exact same rule string with --remove-rich-rule.

sudo firewall-cmd --permanent --remove-rich-rule='rule family="ipv4" source address="203.0.113.50" drop'
sudo firewall-cmd --reload

Step 6: Source-Based Rules

Source-based rules let you route traffic from specific IP addresses or subnets to a particular zone, regardless of which interface the traffic arrives on. This is useful for whitelisting management subnets or isolating specific networks.

Add a Source to a Zone

Route all traffic from your management network to the trusted zone.

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

Verify the source was added.

sudo firewall-cmd --zone=trusted --list-sources

The command returns the subnet assigned to the zone.

10.0.0.0/24

Whitelist a Monitoring Subnet

Add your monitoring network to the internal zone so monitoring agents can reach all services.

sudo firewall-cmd --permanent --zone=internal --add-source=172.16.5.0/24
sudo firewall-cmd --reload

Remove a Source from a Zone

Remove a source when it no longer needs special zone treatment.

sudo firewall-cmd --permanent --zone=trusted --remove-source=10.0.0.0/24
sudo firewall-cmd --reload

Step 7: Port Forwarding

Port forwarding redirects traffic arriving on one port to another port or another host. This requires masquerading to be enabled on the zone for forwarding to a different IP.

Forward a Port Locally

Forward incoming traffic on TCP port 80 to port 8080 on the same machine.

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

Forward a Port to Another Host

Forward SSH traffic (port 22) to an internal server at 192.168.1.100. Masquerading must be enabled first so the destination server can route replies back.

sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --permanent --add-forward-port=port=2222:proto=tcp:toport=22:toaddr=192.168.1.100
sudo firewall-cmd --reload

Now connections to port 2222 on this host are forwarded to port 22 on 192.168.1.100.

Enable and Disable Masquerading

Masquerading performs source NAT on outgoing packets, making them appear to come from the firewall’s IP. This is required when forwarding traffic to another host.

Enable masquerading permanently.

sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --reload

Check if masquerading is enabled.

sudo firewall-cmd --query-masquerade

Disable masquerading when no longer needed.

sudo firewall-cmd --permanent --remove-masquerade
sudo firewall-cmd --reload

Step 8: Managing Interfaces and Zones

On multi-homed servers with multiple network interfaces, you can assign each interface to a different zone. For example, the public-facing interface goes to the public zone and the internal management interface goes to internal.

Assign an Interface to a Zone

Add the eth1 interface to the internal zone.

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

Change an Interface’s Zone

Move an interface from its current zone to a new one.

sudo firewall-cmd --permanent --zone=dmz --change-interface=eth0
sudo firewall-cmd --reload

Check Which Zone an Interface Belongs To

sudo firewall-cmd --get-zone-of-interface=eth0

The command returns the zone name for that interface.

Remove an Interface from a Zone

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

Step 9: Direct Rules (Advanced)

Direct rules let you inject raw iptables or nftables rules directly into the firewalld ruleset. Use these only when firewalld’s standard zones, services, ports, and rich rules cannot express what you need. Direct rules bypass the zone model and can create maintenance headaches if overused.

Important: On RHEL 10 with the nftables backend, direct rules that use iptables syntax are translated to nftables internally. For new deployments, prefer rich rules or policies over direct rules whenever possible.

Add a Direct Rule

Block all incoming ICMP echo requests (ping) at the iptables level.

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

List Direct Rules

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

The output shows all direct rules grouped by table and chain.

ipv4 filter INPUT 0 -p icmp --icmp-type echo-request -j DROP

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

Using nftables Passthrough

For direct nftables syntax, use passthrough rules. This adds an nftables rule to accept traffic on TCP port 9090.

sudo firewall-cmd --permanent --direct --add-passthrough ipv4 -t filter -A INPUT -p tcp --dport 9090 -j ACCEPT
sudo firewall-cmd --reload

Direct rules and passthrough rules are stored in /etc/firewalld/direct.xml. Review this file periodically to clean up rules you no longer need.

Step 10: Firewalld with Docker and Kubernetes

Docker and container platforms interact with firewalld in ways that catch many administrators off guard. Understanding this interaction prevents security surprises in production.

How Docker Interacts with Firewalld

Docker manipulates iptables/nftables rules directly to handle port mapping and container networking. When you publish a container port with -p 8080:80, Docker inserts its own NAT and FORWARD rules. These rules bypass firewalld zones entirely, meaning a container port can be exposed to the network even if firewalld does not have that port open.

On RHEL 10 with firewalld running, Docker creates a docker zone automatically. Check if it exists.

sudo firewall-cmd --get-zones | tr ' ' '\n' | grep docker

Restricting Docker-Published Ports

To prevent Docker from exposing ports globally, bind container ports to localhost only.

docker run -d -p 127.0.0.1:8080:80 nginx

Then use firewalld or a reverse proxy to control external access to that port.

Another approach is to disable Docker’s iptables manipulation by adding "iptables": false to /etc/docker/daemon.json. However, this breaks container-to-container networking and outbound connectivity unless you manually create the required rules.

Tips for Kubernetes

Kubernetes (through kube-proxy) also manages iptables/nftables rules for service routing. If you run firewalld on Kubernetes nodes, keep these points in mind.

  • Enable masquerading on the zone used by your pod network interface – kube-proxy needs this for NodePort and LoadBalancer services
  • Open the required Kubernetes ports: TCP 6443 (API server), TCP 10250 (kubelet), TCP 2379-2380 (etcd), and your NodePort range (default 30000-32767)
  • Allow pod and service CIDR traffic between nodes by adding those subnets to the trusted zone
  • If using Calico or Cilium CNI, check their documentation for additional firewalld requirements

Example – open required ports for a Kubernetes control plane node.

sudo firewall-cmd --permanent --add-port=6443/tcp
sudo firewall-cmd --permanent --add-port=2379-2380/tcp
sudo firewall-cmd --permanent --add-port=10250/tcp
sudo firewall-cmd --permanent --add-port=10257/tcp
sudo firewall-cmd --permanent --add-port=10259/tcp
sudo firewall-cmd --permanent --add-port=30000-32767/tcp
sudo firewall-cmd --reload

If you are setting up a Kubernetes cluster with kubeadm, make sure these ports are open before running kubeadm init.

Firewalld Command Reference

This table covers the most commonly used firewall-cmd commands. Add --permanent to any command to make changes persistent, then run firewall-cmd --reload to apply.

CommandDescription
firewall-cmd --stateCheck if firewalld is running
firewall-cmd --reloadReload permanent configuration into runtime
firewall-cmd --get-default-zoneShow the current default zone
firewall-cmd --set-default-zone=ZONEChange the default zone (immediate and permanent)
firewall-cmd --get-zonesList all available zones
firewall-cmd --get-active-zonesShow zones with bound interfaces or sources
firewall-cmd --list-allShow all rules for the default zone
firewall-cmd --list-all-zonesShow rules for every zone
firewall-cmd --zone=ZONE --list-allShow all rules for a specific zone
firewall-cmd --get-servicesList all predefined service names
firewall-cmd --add-service=SERVICEAllow a service in the default zone
firewall-cmd --remove-service=SERVICERemove a service from the default zone
firewall-cmd --list-servicesList allowed services in the default zone
firewall-cmd --add-port=PORT/PROTOOpen a port (e.g., 8080/tcp)
firewall-cmd --remove-port=PORT/PROTOClose a port
firewall-cmd --list-portsList open ports in the default zone
firewall-cmd --add-source=CIDR --zone=ZONERoute a subnet to a zone
firewall-cmd --remove-source=CIDR --zone=ZONERemove a source from a zone
firewall-cmd --add-rich-rule='RULE'Add a rich rule for fine-grained control
firewall-cmd --remove-rich-rule='RULE'Remove a rich rule
firewall-cmd --list-rich-rulesList all rich rules in the default zone
firewall-cmd --add-forward-port=...Configure port forwarding
firewall-cmd --add-masqueradeEnable source NAT (masquerading)
firewall-cmd --query-masqueradeCheck if masquerading is active
firewall-cmd --add-interface=IF --zone=ZONEBind an interface to a zone
firewall-cmd --change-interface=IF --zone=ZONEMove an interface to a different zone
firewall-cmd --runtime-to-permanentSave all runtime rules as permanent
firewall-cmd --panic-onDrop all traffic immediately (emergency use)
firewall-cmd --panic-offDisable panic mode and restore rules

Troubleshooting Firewalld

Here are the most common firewalld issues and how to fix them. If you run into SELinux denials blocking network services, check your SELinux context labels before blaming the firewall.

Rules Not Persisting After Reboot

If your rules disappear after a reboot, you added them as runtime-only. Every rule needs the --permanent flag followed by --reload. If you already have runtime rules you want to keep, save them all at once.

sudo firewall-cmd --runtime-to-permanent

Locked Out of SSH

If you changed the default zone to one that does not allow SSH (like drop or block), you lose access. To recover, you need console access (IPMI, KVM, or cloud provider console). Once on the console, fix it.

sudo firewall-cmd --set-default-zone=public
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload

If you have changed the SSH port from the default 22 to a custom port – for example by following a guide to change the SSH port on RHEL with SELinux – make sure to open that custom port instead of the ssh service.

Zone Conflicts

An interface can only belong to one zone. If you add an interface to a new zone without removing it from the old one, firewalld silently moves it. Check your active zones to confirm the assignment is correct.

sudo firewall-cmd --get-active-zones

Service Listening but Not Accessible

If a service is running and the port is open in firewalld but clients still cannot connect, check these things in order.

  1. Verify the service is listening on the expected interface (not just 127.0.0.1)
  2. Check that the port is in the correct zone – use firewall-cmd --get-active-zones and --list-all for that zone
  3. Check SELinux is not blocking the service, especially if using a non-standard port
  4. Look for Docker or other tools injecting conflicting nftables rules

To debug, check the actual nftables rules loaded by firewalld.

sudo nft list ruleset | less

Firewalld Fails to Start

If firewalld will not start, check the journal for error details.

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

Common causes include malformed XML in /etc/firewalld/, corrupted zone files, or conflicting iptables services. If a zone file is corrupted, remove the custom override to restore the default.

sudo rm /etc/firewalld/zones/public.xml
sudo systemctl restart firewalld

Conclusion

Firewalld provides a structured way to manage firewall rules on Rocky Linux 10, AlmaLinux 10, and RHEL 10. With zones, services, rich rules, and port forwarding, you can build a layered security posture that matches your network topology. The nftables backend on RHEL 10 gives better performance and a cleaner rule structure compared to the legacy iptables backend.

For production systems, always use --permanent with every rule change and verify with --list-all after reloading. Combine firewalld with Red Hat’s firewall documentation for enterprise-specific guidance. Layer your security with SELinux enforcement, SSH key-only authentication, and regular audit log reviews.

Related Articles

Security How To Setup WireGuard VPN on Amazon Linux 2 Cloud What Are Cloud Workloads and Securing them with CWPP Security Best CompTIA Security+ (SY0-701) Books for 2026 Arch Linux Install Arch Linux with LUKS Full Disk Encryption

Leave a Comment

Press ESC to close