Learn › RHCSA (EX200) › Basic Networking
firewall-cmd - a hands-on Linux lab on a real virtual machine.
First contact with firewalld on RHEL 10, driven entirely through firewall-cmd. Read the firewall with --state (running), --get-default-zone (public), and --list-all (interfaces, allowed services, open ports), then learn the exam rhythm that makes changes stick: save with --permanent --add-service=http or --add-port=8080/tcp, apply with --reload, verify with --list-services. A runtime change made without --permanent is lost on the next reload or reboot, the classic point-loser. Zones and rich rules come in a later security lesson. Serves EX200 basic-networking: configure firewall settings using firewall-cmd.
You are mid-exam. A task says open the web port so the server answers HTTP. You type a quick firewall command, the browser connects, you tick the box and move on. Twenty minutes later the grading script reboots the machine, and the port is shut again. Your points evaporate. The command worked, the browser proved it, and none of it survived, because you changed the running firewall but never wrote the change to disk.
The firewall is the gate between the network and every service on the box. On RHEL 10 that gate is managed by a service called firewalld, and you drive it with one command: firewall-cmd. This lesson is your first contact with it. You will read the firewall's current state, see which services it already lets through, then learn the two-step rhythm that makes a change stick: change it permanently, then reload. Get that rhythm wrong and, like the story above, your work disappears.
The black boxes below are a practice terminal: a safe sandbox that checks the one command each step teaches. The outputs are from a real RHEL 10 machine (AlmaLinux 10.2) running firewalld. A few values depend on the machine, most obviously the interface names (dummy0 ens3 here) and the exact set of already-open services, so treat those as illustrative. What is stable everywhere is the SHAPE: --state prints one word, --get-default-zone prints the zone name, --list-all prints a labelled block, and a change plus --reload is how anything becomes permanent.
A firewall is a filter that decides which network traffic is allowed to reach the machine and which is dropped. Picture a building with one staffed front desk. Every visitor is stopped and checked against a guest list; anyone not on the list is turned away. firewalld is that front desk for your server, and the guest list is a set of allowed services and ports.
firewalld is the firewall management service that ships and runs by default on RHEL 10. You do not talk to it by hand-editing files. You talk to it through its command-line client, firewall-cmd. Every question and every change goes through firewall-cmd with a flag. --state asks if it is running, --list-all asks what it allows, --add-service opens something up. This whole lesson is one command with different flags.
A service is only useful if traffic can reach it. You can install a web server, start it, and confirm it is listening, and outside connections still fail, because the firewall is silently dropping them at the gate. When that happens, firewall-cmd is the tool that tells you what the gate currently allows and lets you open exactly the port or service you need, no more.
On the exam this comes up constantly: make a service reachable, and the graded server must still allow it after a reboot. In real work it is the same instinct. Before you debug an application that cannot be reached, you check the firewall. firewall-cmd is fast, it is scriptable, and it is the one supported way to manage the firewall on RHEL 10.
Before you inspect or change anything, confirm the firewall is actually running. A stopped firewalld allows everything, which is a very different world from a running one that filters. The simplest question you can ask is firewall-cmd --state. It prints one word: running if firewalld is up, or not running if it is stopped.
Ask the firewall whether it is on:
firewall-cmd --state
prompt: [root@servera ~]# answer: firewall-cmd --state output: running hint: The state check is the --state flag on firewall-cmd: firewall-cmd --state
One word, running, confirms firewalld is up and filtering traffic. This is the very first thing to check when a service is unreachable and you suspect the firewall: if --state said not running, the firewall is not the cause and you look elsewhere. If it says running, the gate is staffed and the next question is what it lets through. Everything else in this lesson assumes a running firewall.
firewalld groups its rules into zones. A zone is a named trust level, a bundle of rules that gets applied to a network interface. You do not need the full theory of zones yet; that is a later security lesson. For first contact you only need one fact: there is a default zone, and unless you say otherwise, your changes land in it. On a standard RHEL 10 install that default is a zone named public.
Ask which zone is the default:
firewall-cmd --get-default-zone
prompt: [root@servera ~]# answer: firewall-cmd --get-default-zone output: public hint: The default-zone question is --get-default-zone: firewall-cmd --get-default-zone
The default zone is public. That means when you run a plain --add-service or --add-port with no --zone= flag, the rule is added to public, and that is where it takes effect. Knowing the default zone matters because every command in this lesson operates on it implicitly. When a task does not mention a zone, it means the default, and now you know how to confirm what that is.
--state and --get-default-zone each answer one narrow question. --list-all answers the big one: show me everything about the active zone in a single block. It prints the zone name and, indented under it, the interfaces bound to that zone, the services currently allowed through, and the ports opened by number. This is the command you run to see, at a glance, exactly what the gate permits right now.
Dump the full picture of the active zone:
firewall-cmd --list-all
prompt: [root@servera ~]# answer: firewall-cmd --list-all output: public (default, active) target: default interfaces: dummy0 ens3 services: cockpit dhcpv6-client ssh ports: hint: The full-zone dump is --list-all: firewall-cmd --list-all
The header public (default, active) names the zone and confirms it is both the default and currently in use. Under it: target: default is the fallback policy for traffic that matches no rule. interfaces: dummy0 ens3 are the network cards this zone governs. services: cockpit dhcpv6-client ssh is the guest list, the named services allowed in, and here ssh is already open, which is why you can reach the box remotely. ports: is empty, meaning nothing is opened by raw port number yet. This one block is your map of the gate before you touch it.
Your interfaces: line and the exact services: set will differ from this capture. Interface names depend on the hardware (ens3, enp1s0, eth0 are all common), and which services are pre-opened depends on the install. What is reliable across a fresh RHEL 10 machine is that ssh is in the services list, so you do not lock yourself out, and that ports: starts empty. Read your own machine's --list-all output; do not assume it matches this one line for line.
Here is the heart of the lesson, and the single thing that costs people points. firewalld keeps two separate configurations: the runtime rules, which are live right now, and the permanent rules, which are saved to disk and survive a reboot. A plain change like firewall-cmd --add-service=http touches only the runtime. It works instantly, and it is gone the moment the firewall reloads or the machine reboots.
To make a change stick, you write it to the permanent set with --permanent, then load that permanent set into the runtime with --reload. That is the two-step rhythm: --permanent to save it, --reload to apply it. First, permanently allow the http service:
firewall-cmd --permanent --add-service=http
prompt: [root@servera ~]# answer: firewall-cmd --permanent --add-service=http output: hint: Save it to disk with --permanent plus --add-service=http: firewall-cmd --permanent --add-service=http
firewalld normally prints success here, and in this sandbox the line is treated as complete once you enter it. But notice what has and has not happened. The http rule is now written to the PERMANENT configuration on disk, yet it is not live in the runtime, so http traffic is still being blocked this second. You have saved the change but not applied it. That is exactly the half-finished state that loses exam points if you stop here. One more command loads it in.
--reload throws away the current runtime rules and rebuilds them from the permanent set on disk. After a reload, everything you saved with --permanent becomes live, and the two configurations, runtime and permanent, match again. This is the second half of the rhythm and the step people forget under time pressure.
Load the permanent configuration into the runtime:
firewall-cmd --reload
prompt: [root@servera ~]# answer: firewall-cmd --reload output: hint: Apply the saved rules with the --reload flag: firewall-cmd --reload
After --reload, the http rule you saved is now live: the runtime was rebuilt from disk, so what you saved is what is running. Confirm it with firewall-cmd --list-services, which prints just the allowed-service names on one line. It now reads cockpit dhcpv6-client http ssh, with http added to the set from the earlier --list-all. Save with --permanent, apply with --reload, verify with a list command. That loop is the whole exam pattern.
prompt: [root@servera ~]# answer: firewall-cmd --list-services output: cockpit dhcpv6-client http ssh hint: List just the allowed services with --list-services: firewall-cmd --list-services
Runtime changes without --permanent are lost on the next reload or reboot. This is the classic exam trap. If you open a service or port to test it, the graded machine reboots before scoring and your rule is gone. The safe habit: always make the change with --permanent, then --reload to apply it, then verify. Never rely on a runtime-only change surviving. If you already made a runtime change and want to keep it, firewall-cmd --runtime-to-permanent copies the live rules to disk.
Not everything has a named service. When an application listens on a nonstandard port, like a dev server on 8080, you open the port by number instead of by service name. The flag is --add-port, and the value is the port plus its protocol, written 8080/tcp. It follows the same rhythm as a service: --permanent to save, then --reload to apply.
Permanently open TCP port 8080:
firewall-cmd --permanent --add-port=8080/tcp
prompt: [root@servera ~]# answer: firewall-cmd --permanent --add-port=8080/tcp output: hint: Open a raw port with --add-port and the port/protocol form: firewall-cmd --permanent --add-port=8080/tcp
Same shape as the service rule, but keyed on a number instead of a name. The 8080/tcp value is two parts joined by a slash: the port number and the protocol (tcp or udp). Because you used --permanent, this is saved to disk but not yet live, so you would follow it with firewall-cmd --reload to apply it and firewall-cmd --list-ports to confirm 8080/tcp shows up. Named services when one exists, raw ports when it does not, and the --permanent plus --reload rhythm every single time.
Scaffolding off. No command is printed this time. You have every piece you need.
A task tells you to allow the http service so the web server is reachable, and it must still be allowed after the machine reboots. You have already inspected the zone and you know http is not yet in the services list. The trap is stopping after one command, because a plain runtime change vanishes at reboot. What single firewall-cmd invocation writes the http rule to the SAVED configuration on disk, the half that survives a reboot, as the first step of the two-step rhythm?
prompt: [root@servera ~]# answer: firewall-cmd --permanent --add-service=http output: hint: The saved half needs --permanent, and the service is added with --add-service=http: firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=http writes the http rule to the permanent configuration on disk, the part that survives a reboot. That is step one. You would then run firewall-cmd --reload to make it live. --permanent alone saves without applying; a plain --add-service=http alone applies without saving and is lost at reboot. Only the pair, save then reload, both keeps the rule and turns it on. That is the exam-safe habit for every firewall change.
You earned this cheat sheet. Every row is a flag you just ran or built:
The one thing to burn in for the exam: a change is not real until it is permanent. Make it with --permanent, apply it with --reload, verify it with a list command. Skip the reload and the rule is saved but not live; skip the permanent and the rule is live but dies at reboot.
When you list rules, watch which configuration you are reading. Plain firewall-cmd --list-services shows the RUNTIME set, what is live now. Add --permanent (firewall-cmd --permanent --list-services) to read the SAVED set on disk. If those two disagree, you have an unsaved runtime change or a saved change you have not reloaded yet, which is the exact situation that costs points. After a proper --permanent then --reload, the two match.
The practice terminal has walked you through first contact with firewalld. --state to confirm the gate is staffed, --get-default-zone and --list-all to read what it allows, then the rhythm that matters most: --permanent to save a service or port, --reload to apply it, and a list command to verify. Every one of those you typed yourself.
The basic-networking mission, Operation Uplink, is where you run these against a real RHEL 10 machine. A full VM boots for you, with a live firewalld and real interfaces. The mission hands you objectives that use exactly what you practiced here. Open a service so a server is reachable, open a port by number, and make every change survive a reboot. One difference from this lesson: the mission shows no commands. You read the objective, recall the --permanent and --reload and --add-service forms, and type them. That recall, plus the discipline of never trusting a runtime-only change, is what makes it stick on exam day.
Finish the other basic-networking lessons, then take Operation Uplink and open a firewall on a real machine.
Practice firewall-cmd First Contact in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.