
How to Set Up a Firewall Using Firewalld on latest CentOS
Setting up a firewall is one of those “day one” tasks when you spin up a new server, but it’s also something that can save your bacon (and your data) down the road. If you’re running the latest CentOS and want to get your firewall game on point, firewalld is your best friend. This guide will walk you through everything from basic setup to advanced configurations, complete with real-world examples and the occasional “what not to do” story. Whether you’re securing a simple web server or managing a complex multi-service environment, you’ll learn how to leverage firewalld’s zone-based approach to create a robust security layer that actually makes sense.
How Firewalld Works: Zones, Services, and the Magic Behind It
Think of firewalld as the smart, zone-aware evolution of the traditional iptables approach. Instead of wrestling with complex rule chains, firewalld organizes everything into zones – logical containers that define trust levels for network connections. Each zone has its own set of rules, and network interfaces get assigned to these zones based on your security requirements.
Here’s the brilliant part: firewalld operates on both a runtime and permanent configuration level. Runtime changes take effect immediately but disappear after a reboot, while permanent changes survive reboots but need to be reloaded to take effect. This dual-layer approach lets you test configurations safely before committing them.
The default zones you’ll work with include:
- public – For untrusted networks (like the internet)
- internal – For trusted internal networks
- dmz – For publicly accessible services with limited internal access
- work/home – For specific environment types
- trusted – All network traffic is accepted
- drop/block – Incoming traffic is dropped or rejected
Services in firewalld are predefined rule sets for common applications. Instead of memorizing that SSH uses port 22/tcp, you just reference the “ssh” service. Much cleaner, right?
Step-by-Step Firewalld Setup: From Zero to Hero
Let’s get your hands dirty with a complete firewalld setup. I’m assuming you’re starting with a fresh CentOS installation.
Installation and Initial Setup
First, let’s make sure firewalld is installed and running:
# Install firewalld (usually pre-installed on CentOS)
sudo dnf install firewalld -y
# Start and enable firewalld
sudo systemctl start firewalld
sudo systemctl enable firewalld
# Check the status
sudo systemctl status firewalld
sudo firewall-cmd --state
Understanding Your Current Configuration
Before making changes, let’s see what we’re working with:
# List all zones and their configurations
sudo firewall-cmd --list-all-zones
# Check the default zone
sudo firewall-cmd --get-default-zone
# See which zone your interface is using
sudo firewall-cmd --get-active-zones
# List available services
sudo firewall-cmd --get-services
Basic Web Server Configuration
Here’s a common scenario: you’re setting up a web server that needs HTTP, HTTPS, and SSH access:
# Add HTTP and HTTPS services to the public zone (runtime)
sudo firewall-cmd --zone=public --add-service=http
sudo firewall-cmd --zone=public --add-service=https
sudo firewall-cmd --zone=public --add-service=ssh
# Check what's currently allowed
sudo firewall-cmd --zone=public --list-all
# Make changes permanent
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --zone=public --add-service=https --permanent
sudo firewall-cmd --zone=public --add-service=ssh --permanent
# Reload to apply permanent changes
sudo firewall-cmd --reload
Advanced Configuration: Custom Ports and Rich Rules
Sometimes you need more granular control. Here’s how to handle custom applications and specific source restrictions:
# Open a custom port (e.g., for a Node.js app on port 3000)
sudo firewall-cmd --zone=public --add-port=3000/tcp --permanent
# Open a range of ports (useful for FTP passive mode)
sudo firewall-cmd --zone=public --add-port=21000-21100/tcp --permanent
# Allow specific IP to access SSH (rich rule)
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.100" service name="ssh" accept' --permanent
# Block specific IP
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.200" drop' --permanent
# Allow specific subnet to access database port
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" port protocol="tcp" port="3306" accept' --permanent
# Reload configuration
sudo firewall-cmd --reload
Real-World Examples and Use Cases
Scenario 1: Multi-Tier Web Application
Let’s say you’re running a typical web app stack with a load balancer, web servers, and a database server. Here’s how you might configure each tier:
Load Balancer Configuration:
# Allow HTTP/HTTPS from anywhere, SSH from management network only
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --zone=public --add-service=https --permanent
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
Web Server Configuration:
# Allow HTTP from load balancer subnet, SSH from management
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.2.0/24" service name="http" accept' --permanent
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
Database Server Configuration:
# Allow MySQL only from web server subnet
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.3.0/24" service name="mysql" accept' --permanent
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
Scenario 2: Development vs Production Environments
Here’s a comparison table showing different approaches:
Aspect | Development Environment | Production Environment |
---|---|---|
SSH Access | Allow from anywhere (convenience) | Restrict to management networks only |
Debug Ports | Open debug ports (9229 for Node.js) | No debug ports exposed |
Logging | Log denied packets for debugging | Log only critical events |
Default Policy | More permissive for testing | Strict deny-by-default |
Development Environment Example:
# More relaxed rules for development
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 --zone=public --add-port=9229/tcp --permanent # Node.js debug
sudo firewall-cmd --zone=public --add-port=3000-3010/tcp --permanent # Dev servers
sudo firewall-cmd --reload
Common Mistakes and How to Avoid Them
❌ The “Lockout Scenario”:
Here’s a nightmare scenario I’ve seen too many times: someone removes SSH access and locks themselves out of their server. Always test your rules before applying them permanently:
# WRONG: Removing SSH without testing
sudo firewall-cmd --zone=public --remove-service=ssh --permanent
# RIGHT: Test first, then make permanent
sudo firewall-cmd --zone=public --remove-service=ssh
# Test your connection from another terminal
# If you can still connect via your specific IP rule, then:
sudo firewall-cmd --zone=public --remove-service=ssh --permanent
sudo firewall-cmd --reload
❌ Forgetting to Reload:
Another classic mistake is making permanent changes but forgetting to reload:
# Changes made but not active yet
sudo firewall-cmd --zone=public --add-service=http --permanent
# Always reload after permanent changes
sudo firewall-cmd --reload
# Or use the --runtime-to-permanent flag instead
sudo firewall-cmd --zone=public --add-service=http
sudo firewall-cmd --runtime-to-permanent
Advanced Features and Automation
Creating Custom Services
Instead of always specifying ports, create custom service definitions:
# Create a custom service file
sudo cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/myapp.xml
# Edit the service definition
sudo vi /etc/firewalld/services/myapp.xml
Example custom service file:
<?xml version="1.0" encoding="utf-8"?>
<service>
<short>MyApp</short>
<description>Custom application service</description>
<port protocol="tcp" port="8080"/>
<port protocol="tcp" port="8443"/>
</service>
Then reload and use it:
sudo firewall-cmd --reload
sudo firewall-cmd --zone=public --add-service=myapp --permanent
Zone-Based Network Segmentation
Here’s where firewalld really shines. You can create custom zones for different network segments:
# Create a custom zone for database servers
sudo firewall-cmd --permanent --new-zone=database
sudo firewall-cmd --permanent --zone=database --set-description="Database server zone"
sudo firewall-cmd --permanent --zone=database --set-target=DROP
# Configure the database zone
sudo firewall-cmd --permanent --zone=database --add-service=ssh
sudo firewall-cmd --permanent --zone=database --add-service=mysql
sudo firewall-cmd --permanent --zone=database --add-rich-rule='rule family="ipv4" source address="10.0.3.0/24" accept'
# Assign interface to the zone
sudo firewall-cmd --permanent --zone=database --change-interface=eth1
sudo firewall-cmd --reload
Automation and Scripting
Firewalld plays nicely with configuration management tools. Here’s a bash script for standardized server setup:
#!/bin/bash
# Standard firewall setup script
MANAGEMENT_NETWORK="10.0.1.0/24"
WEB_SERVERS="10.0.2.0/24"
# Function to setup web server firewall
setup_web_server() {
echo "Configuring web server firewall..."
firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=public --add-service=https
firewall-cmd --permanent --zone=public --add-rich-rule="rule family='ipv4' source address='$MANAGEMENT_NETWORK' service name='ssh' accept"
firewall-cmd --reload
echo "Web server firewall configured"
}
# Function to setup database server firewall
setup_db_server() {
echo "Configuring database server firewall..."
firewall-cmd --permanent --zone=public --add-rich-rule="rule family='ipv4' source address='$WEB_SERVERS' service name='mysql' accept"
firewall-cmd --permanent --zone=public --add-rich-rule="rule family='ipv4' source address='$MANAGEMENT_NETWORK' service name='ssh' accept"
firewall-cmd --reload
echo "Database server firewall configured"
}
# Check server type and configure accordingly
case "$1" in
web)
setup_web_server
;;
db)
setup_db_server
;;
*)
echo "Usage: $0 {web|db}"
exit 1
;;
esac
Monitoring and Troubleshooting
Firewalld integrates well with system logging and provides several debugging options:
# Enable logging of denied packets
sudo firewall-cmd --set-log-denied=all
# Check recent firewall logs
sudo journalctl -f -u firewalld
# View iptables rules generated by firewalld
sudo iptables -L -n -v
# Debug specific connections
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="1.2.3.4" log prefix="FIREWALL: " level="info" accept'
Performance Considerations
Firewalld has minimal performance overhead, but here are some benchmarks compared to raw iptables:
- Memory usage: ~5-10MB additional for the firewalld daemon
- Rule processing: Nearly identical to iptables (uses iptables backend)
- Configuration time: 50-70% faster for complex rule sets due to abstraction
- Management overhead: Significantly reduced compared to manual iptables management
Integration with Other Tools
Firewalld works great with other system tools:
- fail2ban: Automatically creates firewalld rich rules for IP banning
- NetworkManager: Automatically assigns zones based on network profiles
- Ansible/Puppet: Native modules for firewalld management
- Docker: Integrates with docker’s networking (though requires careful configuration)
Example fail2ban integration:
# fail2ban automatically uses firewalld when available
# Check active bans
sudo fail2ban-client status sshd
# Banned IPs appear as rich rules
sudo firewall-cmd --zone=public --list-rich-rules
Conclusion and Recommendations
Firewalld represents a significant step forward in Linux firewall management, especially for CentOS environments. Its zone-based approach makes complex network security policies much more manageable than traditional iptables configurations.
When to use firewalld:
- Any production CentOS server deployment
- Multi-tier applications requiring network segmentation
- Environments where firewall rules change frequently
- Teams that need to manage firewall configurations without deep iptables knowledge
Best practices to remember:
- Always test runtime changes before making them permanent
- Use rich rules for complex scenarios, but don’t overdo it
- Create custom services for your applications rather than just opening ports
- Implement proper logging and monitoring from day one
- Document your zone strategy and stick to it
Whether you’re setting up a simple web server or a complex multi-tier application, firewalld gives you the tools to implement robust security without the complexity overhead of raw iptables. For hosting environments, this translates to faster deployment times and more consistent security policies across your infrastructure.
If you’re looking to put these configurations into practice, consider getting a VPS for testing and development, or a dedicated server for production workloads where you need full control over the firewall configuration.
Remember: a well-configured firewall is your first line of defense, but it’s not a silver bullet. Combine it with other security best practices like regular updates, proper authentication, and monitoring for a comprehensive security strategy.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.