BLOG POSTS
    MangoHost Blog / How to Set Up a Firewall Using Firewalld on latest CentOS
How to Set Up a Firewall Using Firewalld on latest CentOS

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.

Leave a reply

Your email address will not be published. Required fields are marked