FirewallD on CentOS 7 is the modern, dynamic firewall management solution that replaced raw iptables handling and simplifies network security administration. In this guide you'll learn practical, production-ready steps to install, enable, and configure FirewallD, manage zones and services, open ports, forward traffic, and create custom services. This article is written for system administrators and DevOps engineers who want clear, actionable commands and explanations to secure CentOS 7 servers. Follow the examples to implement runtime changes, persist them across reboots, and verify your firewall rules to keep your services reachable while minimizing your attack surface.
Prerequisites
Before you begin, log in as a user with sudo privileges or as root. Ensure your CentOS 7 system has network connectivity and that you understand which services (SSH, HTTP, HTTPS, application-specific ports) must be accessible. Back up any existing firewall rules if you migrated from legacy iptables. We'll demonstrate both runtime and permanent changes so you can test before making rules persistent.
Install and Start FirewallD
If FirewallD is not present, install it using yum and then check its state. Use the runtime versus permanent options carefully: runtime changes are immediate but lost at reboot; permanent changes are loaded on service start or reload.
sudo yum install firewalld -y Loaded plugins: fastestmirror Resolving Dependencies --> Running transaction check ---> Package firewalld.noarch 0:0.5.3-8.el7 will be installed ... Complete!
This command installs the FirewallD package. The -y flag automatically answers “yes” to prompts. The output shows package resolution, download, and installation completion.
sudo firewall-cmd --state running
The –state option returns the service state. “running” indicates FirewallD is active; “not running” means it is stopped and must be started.
sudo systemctl start firewalld
● firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2025-03-03 10:22:13 UTC; 2s ago
Main PID: 1234 (firewalld)
CGroup: /system.slice/firewalld.service
└─1234 /usr/bin/python2 -Es /usr/sbin/firewalld --nofork --nopid Starting via systemd shows the service is active. Use systemctl enable to start FirewallD automatically at boot if desired.
sudo systemctl enable firewalld Created symlink from /etc/systemd/system/multi-user.target.wants/firewalld.service to /usr/lib/systemd/system/firewalld.service
systemctl enable creates the necessary symlink so FirewallD will start on subsequent reboots.
Understand Zones and Active Configuration
FirewallD uses zones (predefined trust levels) and services (predefined sets of ports/protocols) to control traffic. By default, CentOS 7 assigns all interfaces to the ‘public’ zone. Always check active zones and the default zone before making changes.
sudo firewall-cmd --get-default-zone public
This prints the default zone used for interfaces without an explicit assignment; here it is “public”.
sudo firewall-cmd --get-zones block dmz drop external home internal public trusted work
The –get-zones output lists all zones available on the system. Choose the zone that best matches the trust level of the interface/network.
sudo firewall-cmd --get-active-zones public interfaces: eth0 eth1
–get-active-zones shows zones currently in use and which network interfaces are bound to them. In this example eth0 and eth1 are both in the public zone.
sudo firewall-cmd --zone=public --list-all public (active) target: default icmp-block-inversion: no interfaces: eth0 eth1 sources: services: ssh dhcpv6-client ports: protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:
The –list-all flag prints the configuration of a given zone in use. You can see allowed services, bound interfaces, and other options such as masquerading and forwarded ports.
Open a Service or Port
Prefer built-in service names where available because they make rules readable. For services not present, open specific ports or create a custom service XML definition.
sudo firewall-cmd --zone=public --add-service=http success
This command adds the HTTP service to the public zone in the runtime configuration only. “success” confirms the runtime change. Use –permanent to persist across reboots.
sudo firewall-cmd --zone=public --list-services ssh dhcpv6-client http
Verify the service is active for the zone with –list-services. The output lists allowed services for that zone.
sudo firewall-cmd --permanent --zone=public --add-service=http success
Adding the same service with –permanent changes the persistent configuration. It won't take effect in runtime until a reload, unless already added at runtime.
sudo firewall-cmd --reload success
Reload applies permanent configuration to the running firewall. Use –reload after making multiple persistent changes to avoid inconsistencies.
sudo firewall-cmd --zone=public --add-port=32400/tcp success
To allow a specific port (for example, Plex on TCP 32400), use –add-port=PORT/PROTO. This adds a runtime rule; add –permanent to persist.
sudo firewall-cmd --zone=public --list-ports 32400/tcp
–list-ports displays explicit port entries allowed in the selected zone.
Create a Custom Service Definition
When a service is not provided by default, place a custom XML file in /etc/firewalld/services. Copy an existing template, edit the port definitions, then reload FirewallD.
cat /etc/firewalld/services/plexmediaserver.xml plexmediaserver Plex Media Server: streaming and device integration
The custom service file defines short name, description, and one or more <port> elements. After creating the file, reload FirewallD so it becomes available as a service.
sudo firewall-cmd --reload success
Reloading makes newly created service files under /etc/firewalld/services available for use in zones.
Port Forwarding and Masquerading
For NAT or reverse-proxy configurations you may need to forward incoming traffic to different ports or internal addresses. Masquerading is required for outbound SNAT from internal networks.
sudo firewall-cmd --zone=external --add-masquerade success
Enable masquerading on the zone handling external traffic. This allows source NAT for forwarded/masqueraded packets originating from internal networks.
sudo firewall-cmd --zone=external --add-forward-port=port=80:proto=tcp:toport=8080 success
This example forwards incoming TCP port 80 to local port 8080. To forward to another host, include toaddr=IP in the forward-port definition.
sudo firewall-cmd --zone=external --add-forward-port=port=80:proto=tcp:toport=80:toaddr=10.10.10.2 success
Use toaddr= when forwarding traffic to a different internal host. Remember to enable masquerading if performing NAT.
Build a Minimal Web Server Ruleset (Example)
Example scenario: server with one interface eth0, hosting SSH and a public web server (HTTP/HTTPS). Use a restrictive zone (dmz) as default and only open necessary ports.
sudo firewall-cmd --set-default-zone=dmz
Setting the default zone binds unassigned interfaces to the chosen zone. Some versions of firewall-cmd may produce no output on success; verify the change with –get-default-zone.
sudo firewall-cmd --zone=dmz --add-interface=eth0 success
Assigns eth0 explicitly to the dmz zone at runtime. Add –permanent if you want this binding to persist.
sudo firewall-cmd --permanent --zone=dmz --add-service=http success
Open HTTP in the dmz permanent configuration so the web server remains reachable after reboots. Repeat for HTTPS and SSH as required.
sudo firewall-cmd --permanent --zone=dmz --add-service=https success
Adding HTTPS ensures TLS traffic is permitted. Always open only what you need; unnecessary ports increase attack surface.
sudo firewall-cmd --reload success
Reload applies the permanent changes to the runtime. Now verify the zone contents and active ports.
sudo firewall-cmd --zone=dmz --list-all dmz (active) target: default icmp-block-inversion: no interfaces: eth0 sources: services: ssh http https ports: protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:
Verify that the dmz zone is bound to eth0 and that SSH, HTTP, and HTTPS are enabled. Adjust rich rules or source restrictions for finer-grained controls.
Best Practices and Maintenance
1) Test changes in runtime first: use –add-* without –permanent to validate connectivity before persisting. 2) Use service names rather than ports when possible. 3) Keep custom service definitions under /etc/firewalld/services and version them in your configuration management. 4) Regularly export or document the active configuration (firewall-cmd –list-all-zones) and back up /etc/firewalld. 5) For complex rules, consider leveraging rich rules or direct rules only when necessary; they add complexity and reduce readability.
Conclusion
FirewallD on CentOS 7 provides a programmable, zone-based firewall model that simplifies common firewall tasks while keeping powerful features for NAT, forwarding, and custom services. By using runtime changes to validate access and then converting stable rules to permanent with proper reloads, you can maintain uptime while ensuring persistent security. Apply the principles in this guide—limit exposure to required services, prefer service definitions over raw ports, and document changes—to keep your CentOS 7 servers protected and reachable.
Really helpful walkthrough of FirewallD on CentOS 7 — could you add a quick section on enabling logging and testing zone rules safely before going to production?