When managing a CentOS Stream server or workstation, controlling network traffic is fundamental to securing your system against unauthorized access. Firewalld provides a dynamic firewall manager that organizes network rules into zones based on trust levels, allowing you to adapt security policies without disrupting active connections.
This guide demonstrates how to install and configure Firewalld on CentOS Stream, covering zone management, service and port rules, advanced rich rule configurations, and troubleshooting techniques that help maintain secure network boundaries.
These instructions apply to CentOS Stream 10 and CentOS Stream 9. Commands work identically on both releases unless noted otherwise. CentOS Stream 8 reached end-of-life in May 2024 and is no longer supported.
What Is Firewalld?
Firewalld acts as a management layer between your applications and the underlying nftables (or iptables) packet filtering system. Rather than writing complex filtering rules directly, you interact with Firewalld through the firewall-cmd utility, which translates your requests into the appropriate backend rules. This abstraction simplifies firewall administration while preserving the full power of Linux packet filtering.
The daemon maintains two configurations simultaneously: runtime (active but temporary) and permanent (saved to disk). This separation lets you test changes immediately and commit them only after verification, reducing the risk of locking yourself out of remote systems. Firewalld delivers several key advantages for CentOS Stream environments:
- Zone-Based Architecture: Network interfaces map to zones representing trust levels, from “drop” (zero trust) to “trusted” (full access), letting you apply different policies to different network segments.
- Runtime Changes Without Restart: Modify rules instantly without cycling the service or dropping established connections, essential for production servers where uptime matters.
- Predefined Services: Common applications like SSH, HTTP, and HTTPS have built-in service definitions, eliminating the need to memorize port numbers.
- Rich Rule Language: Create complex conditional rules that combine source addresses, logging, rate limiting, and packet marking for advanced traffic control.
- IPv4 and IPv6 Unified Management: Handle both protocol families through the same interface without maintaining separate rule sets.
On CentOS Stream servers, Firewalld integrates with Cockpit for web-based management and coordinates with SELinux policies to provide defense in depth. Whether you’re securing a database server, web application host, or KDE Plasma desktop, Firewalld gives you the flexibility to define precise network boundaries.
Check If Firewalld Is Installed
Before proceeding with installation, determine whether Firewalld already exists on your system. Server installations of CentOS Stream typically include Firewalld, while minimal or container images often omit it to reduce footprint. Check for an existing installation by querying the version:
sudo firewall-cmd --version
If Firewalld is installed and the daemon is accessible, the command returns the version number:
2.4.0
On CentOS Stream 9, you’ll see version 1.3.x instead. If the command fails with “command not found” or a connection error, Firewalld needs installation using the steps below.
Install Firewalld on CentOS Stream
Install Firewalld from the base CentOS Stream repositories using DNF:
sudo dnf install firewalld -y
DNF resolves dependencies automatically, pulling in nftables, Python bindings, and supporting libraries. The installation completes in seconds on most systems.
Enable and Start Firewalld
After installation, configure Firewalld to launch at boot and activate it immediately. The --now flag combines both operations:
sudo systemctl enable --now firewalld
This command creates the systemd symlink for automatic startup and brings the daemon online in a single step.
Verify Firewalld Status
Confirm the service is running correctly before configuring rules:
sudo systemctl status firewalld --no-pager
Healthy output shows “active (running)” with the daemon process details:
● firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-01-26 05:15:22 UTC; 1min ago
Docs: man:firewalld(1)
Main PID: 2847 (firewalld)
Tasks: 2 (limit: 23146)
Memory: 28.4M
CPU: 425ms
CGroup: /system.slice/firewalld.service
└─2847 /usr/bin/python3 -s /usr/sbin/firewalld --nofork --nopid
For a quick state check, use the dedicated Firewalld command:
sudo firewall-cmd --state
running
Understanding Firewalld Command Structure
Before configuring rules, understanding the command syntax helps you work efficiently and avoid mistakes. Every Firewalld command follows a consistent pattern that becomes intuitive with practice.
Firewalld requires root privileges on CentOS Stream. All examples use
sudo; omit it only if you’re already operating in a root shell. If you need to set up a sudo-enabled account, see how to create a sudo user on CentOS Stream.
Command Syntax Overview
The firewall-cmd utility accepts options that modify behavior and commands that perform actions:
firewall-cmd [options] <command>
Common options include:
--zone=name: Target a specific zone instead of the default--permanent: Write changes to disk for persistence across reboots--reload: Apply permanent changes to the runtime configuration
Runtime vs Permanent Configuration
Firewalld maintains separate runtime and permanent configurations. Commands without --permanent affect only the active session and revert after a reload or reboot. This design lets you test rules safely:
Test a rule temporarily:
sudo firewall-cmd --add-service=http
Make a rule permanent:
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
Save all runtime rules to permanent storage:
sudo firewall-cmd --runtime-to-permanent
Essential Firewalld Commands
The following commands cover everyday firewall administration tasks on CentOS Stream systems. Each example uses concrete values rather than placeholders to demonstrate real-world usage.
View the Default Zone
Identify which zone handles traffic for new interfaces:
sudo firewall-cmd --get-default-zone
public
CentOS Stream uses “public” as the default zone, which permits SSH, DHCPv6 client, and Cockpit web console by default.
List Active Zones
See which zones are currently in use and their associated interfaces:
sudo firewall-cmd --get-active-zones
public interfaces: eth0
Display Zone Configuration
Inspect all rules configured in the public zone:
sudo firewall-cmd --zone=public --list-all
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:
This output reveals the services currently allowed through the zone’s firewall rules.
Add a Service to a Zone
Allow HTTP traffic through the public zone permanently. If you’re setting up an Apache web server or a LEMP stack on CentOS Stream, you’ll need both HTTP and HTTPS:
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload
Verify the services were added:
sudo firewall-cmd --zone=public --list-services
cockpit dhcpv6-client http https ssh
Remove a Service from a Zone
Block a service by removing it from the zone’s allowed list. For example, to remove the Cockpit service from the public zone:
sudo firewall-cmd --permanent --zone=public --remove-service=cockpit
sudo firewall-cmd --reload
Open a Specific Port
When no predefined service exists for your application, open ports directly. For example, to allow TCP traffic on port 3000 for a Node.js application:
sudo firewall-cmd --permanent --zone=public --add-port=3000/tcp
sudo firewall-cmd --reload
View open ports in the zone:
sudo firewall-cmd --zone=public --list-ports
3000/tcp
Close a Port
Remove a port rule when no longer needed:
sudo firewall-cmd --permanent --zone=public --remove-port=3000/tcp
sudo firewall-cmd --reload
Change the Default Zone
Set a different zone as the default for new interfaces. For a workstation on a trusted network, the home zone offers more permissive defaults:
sudo firewall-cmd --set-default-zone=home
For servers exposed to the internet, keep “public” or use “dmz” for maximum restriction.
Firewall Zones Reference
Firewalld ships with nine predefined zones covering common trust scenarios. Understanding each zone’s purpose helps you select the appropriate security level for different network segments.
| Zone | Trust Level | Default Behavior | Best For |
|---|---|---|---|
| drop | Zero | Silently discards all incoming packets; outbound allowed | Hostile networks, isolation scenarios |
| block | Zero | Rejects incoming with ICMP prohibited messages | Untrusted networks requiring RFC compliance |
| public | Low | Only selected services (SSH, Cockpit, DHCPv6) | Cloud instances, internet-facing servers |
| external | Low | Masquerading enabled for NAT routing | Gateway systems, router configurations |
| dmz | Low | Minimal services; isolated from internal network | Public-facing web servers, mail relays |
| work | Medium | Trusts coworkers; standard office services | Corporate networks with managed systems |
| home | Medium | Relaxed rules; discovery protocols enabled | Home networks with personal devices |
| internal | High | Similar to home; for business internal networks | Backend servers, management networks |
| trusted | Full | Accepts all traffic without filtering | VPN tunnels, loopback, fully trusted links |
List all available zones with:
sudo firewall-cmd --get-zones
block dmz drop external home internal public trusted work
Advanced Firewalld Configuration
Beyond basic service and port management, Firewalld supports advanced scenarios including custom service definitions, IP blocking, and network address translation.
Create a Custom Service Definition
When managing applications that don’t have predefined service definitions, create your own. Service XML files stored in /etc/firewalld/services/ persist across updates:
sudo tee /etc/firewalld/services/myapp.xml <<'EOF'
<?xml version="1.0" encoding="utf-8"?>
<service>
<short>myapp</short>
<description>Custom application listening on TCP 8080</description>
<port protocol="tcp" port="8080"/>
</service>
EOF
sudo firewall-cmd --reload
sudo firewall-cmd --permanent --zone=public --add-service=myapp
sudo firewall-cmd --reload
The custom service now appears alongside built-in services and can be added to any zone.
Block Traffic from Specific IP Addresses
Use rich rules to block suspicious hosts while logging the attempts. This approach works well with log monitoring tools:
sudo firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="192.0.2.50" log prefix="blocked-ip: " level="info" drop'
sudo firewall-cmd --reload
Check the system journal to confirm blocked packets are being logged:
sudo journalctl -k | grep "blocked-ip:" | tail -5
Ban Network Ranges with Ipsets
For blocking large numbers of addresses efficiently, ipsets provide better performance than individual rich rules:
sudo firewall-cmd --permanent --new-ipset=blocklist --type=hash:net
sudo firewall-cmd --permanent --ipset=blocklist --add-entry=203.0.113.0/24
sudo firewall-cmd --permanent --ipset=blocklist --add-entry=198.51.100.0/24
sudo firewall-cmd --permanent --zone=public --add-rich-rule='rule source ipset=blocklist drop'
sudo firewall-cmd --reload
Add or remove entries from the ipset as threats evolve:
sudo firewall-cmd --permanent --ipset=blocklist --add-entry=192.0.2.0/24
sudo firewall-cmd --reload
Enable Masquerading for NAT
When your CentOS Stream system acts as a gateway between networks, enable masquerading to translate internal addresses:
sudo firewall-cmd --permanent --zone=external --add-masquerade
sudo firewall-cmd --reload
Verify masquerading is active:
sudo firewall-cmd --zone=external --query-masquerade
yes
Configure Port Forwarding
Redirect incoming traffic from one port to another, useful for running services on non-standard ports while accepting connections on standard ones:
sudo firewall-cmd --permanent --zone=public --add-forward-port=port=80:proto=tcp:toport=8080
sudo firewall-cmd --reload
This forwards incoming TCP connections on port 80 to port 8080 on the same host.
Troubleshooting Firewalld
When firewall issues arise, systematic troubleshooting helps identify whether the problem lies with Firewalld configuration, the application, or network infrastructure.
Service Accessible Locally But Not Remotely
When an application works on localhost but fails from other hosts, first verify the service is listening on all interfaces, not just 127.0.0.1:
sudo ss -tlnp | grep :8080
If the service binds only to localhost, reconfigure the application. If it listens on 0.0.0.0, check that the port or service is allowed through Firewalld:
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --zone=public --list-all
Confirm your interface belongs to the expected zone and the service or port appears in the allowed list.
Rules Disappear After Reboot
If rules vanish after restarting, they were added without --permanent. Compare runtime and permanent configurations:
sudo firewall-cmd --zone=public --list-all
sudo firewall-cmd --permanent --zone=public --list-all
Any rules present in the first output but missing from the second are runtime-only. Save current runtime rules with:
sudo firewall-cmd --runtime-to-permanent
Firewalld Fails to Start
When Firewalld won’t start after adding custom rules, syntax errors in zone or service XML files are often responsible. Check the journal for specific errors:
sudo journalctl -xeu firewalld --no-pager | tail -30
If a custom zone file is corrupted, temporarily move it aside and restart:
sudo mv /etc/firewalld/zones/public.xml /etc/firewalld/zones/public.xml.bak
sudo systemctl restart firewalld
This restores the default zone configuration, letting you rebuild rules incrementally.
Interface Assigned to Wrong Zone
NetworkManager controls zone assignments for connections it manages. To permanently assign an interface to a specific zone:
sudo nmcli connection show
Note the connection name, then set its zone:
sudo nmcli connection modify "Wired connection 1" connection.zone internal
sudo nmcli connection up "Wired connection 1"
Recovering from Panic Mode
Panic mode drops all network traffic immediately. If you accidentally enabled it and lost remote access, connect via console and disable it:
sudo firewall-cmd --query-panic
If the output is “yes”, restore connectivity:
sudo firewall-cmd --panic-off
Panic mode persists only until the next reload or reboot, so restarting the system also clears it.
Quick Reference: Common Commands
Use this table for quick lookups during day-to-day firewall administration:
| Task | Command |
|---|---|
| Check daemon status | sudo firewall-cmd --state |
| List all zones | sudo firewall-cmd --get-zones |
| Get default zone | sudo firewall-cmd --get-default-zone |
| List active zones | sudo firewall-cmd --get-active-zones |
| View zone configuration | sudo firewall-cmd --zone=public --list-all |
| Add service (permanent) | sudo firewall-cmd --permanent --add-service=http && sudo firewall-cmd --reload |
| Remove service | sudo firewall-cmd --permanent --remove-service=http && sudo firewall-cmd --reload |
| Open port | sudo firewall-cmd --permanent --add-port=8080/tcp && sudo firewall-cmd --reload |
| Close port | sudo firewall-cmd --permanent --remove-port=8080/tcp && sudo firewall-cmd --reload |
| Change default zone | sudo firewall-cmd --set-default-zone=home |
| Save runtime to permanent | sudo firewall-cmd --runtime-to-permanent |
| Block an IP address | sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="1.2.3.4" reject' |
| Enable masquerading | sudo firewall-cmd --permanent --zone=external --add-masquerade && sudo firewall-cmd --reload |
| Assign interface to zone | sudo nmcli connection modify "Connection Name" connection.zone home |
| Emergency block all | sudo firewall-cmd --panic-on |
| Restore from panic | sudo firewall-cmd --panic-off |
Remove Firewalld from CentOS Stream
If you prefer an alternative firewall solution or need to troubleshoot conflicts, you can remove Firewalld entirely.
Stop and Disable the Service
First, stop the running daemon and prevent it from starting at boot:
sudo systemctl disable --now firewalld
Verify the service stopped:
sudo systemctl status firewalld --no-pager
○ firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; preset: enabled)
Active: inactive (dead)
Remove the Package
Removing Firewalld leaves your system without firewall protection. If you’re connected via SSH, ensure you have alternative access or another firewall configured before proceeding.
sudo dnf remove firewalld -y
Verify removal:
rpm -q firewalld
package firewalld is not installed
Remove Configuration Files
Custom zone configurations and service definitions remain in /etc/firewalld/ after package removal. To complete the cleanup:
This permanently deletes all custom zones, services, and ipsets. Only proceed if you don’t need to restore the configuration later.
sudo rm -rf /etc/firewalld/
Common Questions
CentOS Stream 9 ships Firewalld 1.3.x while Stream 10 includes version 2.4.x. The newer version offers improved policy support, enhanced nftables integration, and better handling of complex rule sets. Fundamental commands remain compatible across both versions, so guides written for one version generally work on the other.
CentOS Stream uses nftables as the underlying packet filtering framework, not legacy iptables. The iptables-nft compatibility layer translates iptables syntax to nftables rules, so existing scripts still work. You can disable Firewalld and manage nftables directly with the nft command, or use iptables-nft for familiar syntax. However, Firewalld provides easier zone-based management for most use cases.
Conclusion
Firewalld delivers flexible, zone-based network protection for CentOS Stream servers and workstations. Through the firewall-cmd interface, you can manage services, ports, and complex traffic policies without writing raw nftables rules. The separation of runtime and permanent configurations enables safe testing before committing changes, while rich rules and ipsets provide advanced capabilities for blocking threats and managing complex network topologies.