firewalld from scratch.
zones, services, rich rules, and timed rules.
secadmin wants a clean firewall: web open to everyone, SSH only from the management subnet, a custom port for one host, and Cockpit open for an hour. All permanent, all reloaded.
secadmin wants the firewall on server1 rebuilt from a clean sheet, and the rules map straight onto who should reach what. Web open to the world on 80 and 443. SSH only from the management subnet, 10.0.50.0/24, refused from everywhere else. A management UI on 8443 open to exactly one host, 10.0.50.5. Cockpit cracked open for a single one-hour maintenance window. And a snapshot of every rule for the change ticket. All permanent, reloaded after.
Two flags decide whether any of this survives. —permanent writes the rule to disk, —reload pushes the saved rules into the running firewall. Forget —permanent and the rule evaporates at the next reboot. Forget —reload and it sits on disk doing nothing while you swear the change is not working. You will type both more today than anything else.
The ticket
sudo systemctl is-active firewalld || sudo systemctl enable --now firewalld sudo firewall-cmd --state sudo firewall-cmd --get-default-zone sudo firewall-cmd --list-all
Concept review. Zones and the two flags.
man firewall-cmd man firewalld.richlanguage
A packet is matched to a zone, usually by the interface or its source address, and the zone decides what is allowed through named services, ports, or rich rules. Anything not allowed is dropped. The default zone on RHEL 9 is public. The trusted zone allows everything, drop silently discards, and block rejects with a reply.
--permanent write the rule to disk, so it survives a reboot --reload apply the on-disk rules to the running firewall # without --permanent the rule vanishes on reboot # without --reload a permanent rule does not take effect yet
Inventory the current firewall
Know your baseline before you change anything.
sudo firewall-cmd --get-default-zone sudo firewall-cmd --get-active-zones sudo firewall-cmd --list-all sudo firewall-cmd --get-services | tr ' ' '\n' | head -20
Open HTTP and HTTPS
Named services are easier than raw ports, because they already map to the standard port numbers. Add them permanently, then reload.
sudo firewall-cmd --permanent --zone=public --add-service=http sudo firewall-cmd --permanent --zone=public --add-service=https sudo firewall-cmd --reload sudo firewall-cmd --zone=public --list-services
Restrict SSH with a rich rule
This is the step people get subtly wrong. Adding a rich rule that allows SSH from one subnet does nothing if the global ssh service is still open, because that still lets anyone in. Remove the global allow first, then add the scoped rule.
sudo firewall-cmd --permanent --zone=public --remove-service=ssh
sudo firewall-cmd --permanent --zone=public \
--add-rich-rule='rule family="ipv4" source address="10.0.50.0/24" service name="ssh" accept'
sudo firewall-cmd --reload
sudo firewall-cmd --zone=public --list-rich-rulesA custom port for one host
Same idea, scoped to a single address and a raw port instead of a named service.
sudo firewall-cmd --permanent --zone=public \
--add-rich-rule='rule family="ipv4" source address="10.0.50.5" port port="8443" protocol="tcp" accept'
sudo firewall-cmd --reload
sudo firewall-cmd --zone=public --list-rich-rulesA timed rule for Cockpit
A maintenance window wants a rule that cleans itself up. A timeout rule is runtime only, not permanent, and firewalld removes it automatically when the clock runs out.
sudo firewall-cmd --zone=public --add-service=cockpit --timeout=3600 sudo firewall-cmd --zone=public --list-services # cockpit is removed from the runtime config automatically after an hour
Generate the report
sudo mkdir -p /srv/audit_reports sudo firewall-cmd --list-all-zones | sudo tee /srv/audit_reports/firewall_$(date +%Y%m%d).txt
Exam questions
Write the command first.
Q1. Permanently open port 8080/tcp in the default zone and reload.
Q2. Open HTTPS in the public zone permanently and verify it is listed.
Q3. Create a rich rule allowing SSH only from 10.0.0.5, permanent and applied immediately.
Answers.
# A1
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
# A2
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload; sudo firewall-cmd --zone=public --list-services
# A3 (remember to remove the global ssh service too)
sudo firewall-cmd --permanent --zone=public --remove-service=ssh
sudo firewall-cmd --permanent --zone=public \
--add-rich-rule='rule family="ipv4" source address="10.0.0.5" service name="ssh" accept'
sudo firewall-cmd --reloadFinal checklist: confirm everything works
If every check passes, the ticket is done.
# 1. http and https open publicly sudo firewall-cmd --zone=public --list-services # 2. SSH scoped to the management subnet, custom port to one host sudo firewall-cmd --zone=public --list-rich-rules # 3. cockpit opened with a 1 hour timeout (runtime only) # 4. snapshot saved, every permanent change followed by --reload # 5. all three exam commands written from scratch # 6. tracker entry checked off
Reply to secadmin: firewalld configured. Web public, SSH limited to the management subnet, port 8443 to the single management host, Cockpit timed open for an hour. Snapshot in /srv/audit_reports/.