FirewalldLesson 1 of 2
Zones, services and ports
Understand firewalld's model, open a service or port, and make the change permanent without breaking your own access.
firewall-cmd--add-service--add-port--permanent--reload--runtime-to-permanent--list-all--get-active-zones--set-default-zone
After this lesson you can
- Read the active zone and its rules, and tell which are runtime and which are permanent.
- Allow and remove a service or a port, runtime-only and permanently, and apply them with --reload.
- Change the default zone and move an interface between zones.
The idea
firewalld manages the kernel’s packet filter (nftables on RHEL 10) through a simpler model. You do not write packet rules; you decide what is allowed in for each part of the network.
Zones
A zone is a trust level with its own list of allowed services and ports. Network interfaces and source addresses are assigned to zones; a packet is judged by the rules of its zone.
| Zone | Meaning (default behaviour) |
|---|---|
drop | Silently discard everything incoming |
block | Reject everything incoming with a message |
public | Untrusted network; only selected services (ssh, cockpit, dhcpv6-client). The default zone |
internal, work, home | More trusted networks; a few more services |
trusted | Allow everything |
Each interface belongs to exactly one zone; an interface not otherwise assigned uses the default zone.
Services and ports
- A service is a named bundle of ports and protocols:
sshis TCP 22,httpis TCP 80,httpsis TCP 443,nfsis TCP 2049.firewall-cmd --get-serviceslists them and--info-service=NAMEshows what one contains. - A port rule is
PORT/PROTOCOLsuch as8081/tcpfor something without a service name.
Prefer services when one exists (clearer, and covers multiple ports), and a port when it doesn’t.
Runtime and permanent
firewalld keeps two configurations:
| Configuration | Changed with | Survives reload / reboot |
|---|---|---|
| Runtime | Commands without --permanent | No |
| Permanent | Commands with --permanent | Yes, but not active until --reload |
The classic mistake is adding a rule without --permanent (works now, gone after a reboot) or with it and no --reload (saved, but not yet active). Two safe patterns:
firewall-cmd --permanent --add-service=httpthenfirewall-cmd --reloadfirewall-cmd --add-service=http(test it) thenfirewall-cmd --runtime-to-permanent
Nothing is allowed until you say so, except what the zone already includes. Remember the service itself must be running and listening: the firewall only decides whether packets reach it.
Try it
sudo systemctl enable --now firewalld
firewall-cmd --state
firewall-cmd --list-all
running
public (default, 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:
Allow a service
sudo dnf install -y httpd
echo 'hello from node1' | sudo tee /var/www/html/index.html
sudo systemctl enable --now httpd
curl -s -m3 http://node1 || echo "blocked" # from node2
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
curl -s http://node1 # from node2
blocked
success
success
hello from node1
A blocked packet is dropped without a reply, so curl waits and times out (-m3 limits the wait to 3 seconds). If instead you see Connection refused, the firewall let the packet through and nothing is listening: check ss -tlnp.
Open and close a port
sudo firewall-cmd --permanent --add-port=8081/tcp
sudo firewall-cmd --reload
firewall-cmd --list-ports
sudo firewall-cmd --permanent --remove-port=8081/tcp
sudo firewall-cmd --reload
success
success
8081/tcp
success
success
Runtime versus permanent
sudo firewall-cmd --add-service=https
firewall-cmd --list-services
sudo firewall-cmd --permanent --list-services
sudo firewall-cmd --runtime-to-permanent
success
cockpit dhcpv6-client http https ssh
cockpit dhcpv6-client http ssh
success
Zones for interfaces
firewall-cmd --get-active-zones
sudo firewall-cmd --permanent --zone=internal --change-interface=eth1
sudo firewall-cmd --permanent --zone=internal --add-service=nfs
sudo firewall-cmd --reload
firewall-cmd --get-active-zones
public
interfaces: eth0
success
success
success
internal
interfaces: eth1
public
interfaces: eth0
firewall-cmd --set-default-zone=internal changes the zone for all interfaces without an explicit assignment, immediately and permanently. If a connection is managed by NetworkManager, the zone is best stored in the connection: nmcli connection modify NAME connection.zone internal.
Going further
firewall-cmd --list-all-zones shows every zone, and --get-zones lists the names. --add-service accepts a timeout for temporary rules: --add-service=http --timeout=5m. --panic-on drops all traffic including established connections (and --panic-off ends it). firewall-cmd --check-config validates permanent files, and the files themselves live in /etc/firewalld/zones/ as XML. firewall-cmd --direct and nft list ruleset expose the underlying nftables rules, useful for debugging but not for normal configuration. Custom services are described in /etc/firewalld/services/NAME.xml.
Practice
0 of 5 done
Do these on your own system. They get harder. Check each result, then tick it off.
- Warm-up
Is the firewall on, and what does it allow?
Check that
firewalldis running and enabled at boot. Show the default zone, the active zones and what the active zone allows.Check your result
firewall-cmd --stateprintsrunning.firewall-cmd --get-default-zoneprints a zone (usuallypublic).--list-allshowsservices: cockpit dhcpv6-client sshor similar.Hint
systemctl is-enabled firewalld,firewall-cmd --get-default-zone,--get-active-zones,--list-all.Solution
sudo systemctl enable --now firewalld firewall-cmd --state firewall-cmd --get-default-zone firewall-cmd --get-active-zones firewall-cmd --list-all - Practice
Open a web server
Install and start
httpd. From another hostcurl http://node1fails. Allow thehttpservice in the default zone permanently so it also applies right now, and prove it fromnode2.Check your result
firewall-cmd --list-servicesshowshttp(runtime) and--permanent --list-servicesshows it too.curl -s http://node1 | head -1from node2 returns the page.Hint
sudo firewall-cmd --permanent --add-service=httpthensudo firewall-cmd --reload. Without--permanentthe rule disappears at reload or reboot.Solution
sudo dnf install -y httpd echo 'hello from node1' | sudo tee /var/www/html/index.html sudo systemctl enable --now httpd sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --reload firewall-cmd --list-services # on node2 curl -s http://node1 - Practice
Open a non-standard port
A custom application listens on TCP port 8081 (simulate with
python3 -m http.server 8081 &from/tmp). Open that port permanently in the default zone, test it from node2, then close it and confirm it is blocked again.Check your result
firewall-cmd --list-portsshows8081/tcpwhile open;curl http://node1:8081works from node2. After--remove-portand reload, it times out.Hint
--add-port=8081/tcp. Specify the protocol. A blocked port gives a timeout, a closed port with nothing listening givesConnection refused.Solution
(cd /tmp && python3 -m http.server 8081 >/dev/null 2>&1 &) sudo firewall-cmd --permanent --add-port=8081/tcp sudo firewall-cmd --reload firewall-cmd --list-ports # from node2: curl -sI http://node1:8081 | head -1 sudo firewall-cmd --permanent --remove-port=8081/tcp sudo firewall-cmd --reload - Practice
Runtime versus permanent
Add the
httpsservice without--permanent, list runtime and permanent services to see the difference, then save the runtime configuration permanently in one command without retyping it. Finally, reload and show what happens to a runtime-only rule.Check your result
--list-servicesshowshttps;--permanent --list-servicesdoes not. After--runtime-to-permanentboth do. A runtime-only rule vanishes on--reload.Hint
firewall-cmd --runtime-to-permanentwrites the whole current runtime config to disk.Solution
sudo firewall-cmd --add-service=https firewall-cmd --list-services sudo firewall-cmd --permanent --list-services sudo firewall-cmd --runtime-to-permanent sudo firewall-cmd --permanent --list-services sudo firewall-cmd --remove-service=https --permanent sudo firewall-cmd --reload - Challenge
Put a network card in a different zone
Move the second interface
eth1(storage network) into theinternalzone permanently and allownfsthere only, leaving thepubliczone without NFS. Show both zones' rules and the interface assignment.Check your result
firewall-cmd --get-active-zoneslistsinternalwitheth1.--zone=internal --list-servicesincludesnfsand--zone=public --list-servicesdoes not.Hint
--permanent --zone=internal --change-interface=eth1(or via NetworkManager:nmcli con mod storage-net connection.zone internal), then--zone=internal --add-service=nfs.Solution
sudo nmcli connection modify storage-net connection.zone internal sudo nmcli connection up storage-net sudo firewall-cmd --permanent --zone=internal --add-service=nfs sudo firewall-cmd --reload firewall-cmd --get-active-zones firewall-cmd --zone=internal --list-services firewall-cmd --zone=public --list-services