🧱

Level 10 of 24

Firewalls

firewalld and ufw, zones, ports, and nftables underneath both.

A firewall is the last line of network defense on a single server — deciding what traffic is allowed in and out before it ever reaches an application. RHEL and Ubuntu ship genuinely different firewall front-ends, both built on the same underlying nftables framework.

Prerequisites

  • • Level 7: Networking

By the end of this level, you can

  • ✓ Explain firewalld zones vs. ufw's simpler allow/deny model
  • ✓ Open and close specific ports and services on both firewall front-ends
  • ✓ Verify firewall rules are actually taking effect

firewalld (RHEL) and ufw (Ubuntu)

Two different front-ends, one underlying kernel packet-filtering framework. · 11 min

Both `firewalld` (the default on RHEL-family systems) and `ufw` — Uncomplicated Firewall (the default front-end on Ubuntu) — are front-ends that generate and manage rules for the kernel's actual packet-filtering framework, `nftables` (the modern successor to the older `iptables`, though iptables-style syntax and thinking still appears constantly in older documentation and habits). You rarely write raw nftables rules directly in day-to-day administration — these two tools exist specifically to make firewall management less error-prone than hand-writing low-level rules.

`firewalld` organizes rules around zones — named trust levels (`public`, `internal`, `trusted`, etc.) that a network interface is assigned to, each with its own set of allowed services and ports. This models a real-world scenario well: a laptop's Wi-Fi interface on a coffee-shop network might be in the restrictive `public` zone, while its Ethernet interface on a trusted office LAN is in a more permissive zone — the same machine, different trust levels per interface.

`ufw` takes a simpler, more direct approach without zones: you allow or deny specific ports or services directly, and rules apply globally rather than per-interface-trust-level. `sudo ufw allow 22`, `sudo ufw allow http`, `sudo ufw enable` — the entire mental model is closer to a straightforward allow-list, which is exactly why Ubuntu calls it "uncomplicated": it deliberately trades firewalld's more granular zone model for something faster to reason about on a typical single-purpose server.

CommandPurposeExample
sudo firewall-cmd --stateCheck whether firewalld is running—
sudo firewall-cmd --list-allShow the active zone's current rules—
sudo firewall-cmd --add-service=http --permanentAllow HTTP traffic permanently (survives reboot)—
sudo firewall-cmd --reloadApply --permanent changes without restarting the whole service—
sudo ufw status verboseShow ufw's current status and rules—
sudo ufw allow 22/tcpAllow a specific port and protocol—
sudo ufw enableTurn on ufw (make sure SSH is allowed FIRST if managing remotely)—

Common Mistakes

  • ⚠ Running `firewall-cmd --add-service` without `--permanent` and being confused when the rule disappears after a reboot — without --permanent, changes only apply to the current running state
  • ⚠ Enabling ufw on a remote server before confirming SSH (port 22) is allowed — this can instantly lock you out with no way back in except console access
  • ⚠ Assuming firewalld and ufw rules are compatible or interchangeable — they are different tools with different rule syntax on top of the same kernel framework
🧪

Hands-On Lab

Configure a basic web-server firewall

Objectives

  • ✓ Allow SSH, HTTP, and HTTPS traffic
  • ✓ Block a specific test port
  • ✓ Verify the resulting rule set
⚠️ Warning: If managing a remote server over SSH, always confirm SSH access is allowed BEFORE enabling the firewall — otherwise you can lock yourself out instantly.

Instructions

  1. On Ubuntu: `sudo ufw allow 22/tcp`, `sudo ufw allow 80/tcp`, `sudo ufw allow 443/tcp`, then `sudo ufw enable` — confirm with `sudo ufw status verbose`.
  2. On RHEL/Rocky: `sudo firewall-cmd --add-service=ssh --permanent`, `sudo firewall-cmd --add-service=http --permanent`, `sudo firewall-cmd --add-service=https --permanent`, then `sudo firewall-cmd --reload` — confirm with `sudo firewall-cmd --list-all`.
  3. Start a test service on port 8888 (e.g. `python3 -m http.server 8888`) and confirm it is NOT reachable from another machine, since that port was never explicitly allowed.
  4. From the server itself, confirm `curl -I http://localhost:8888` still works locally — a firewall governs traffic between hosts, not access from a machine to itself.
Hints (1)
  • If you're testing over SSH and lose connection after enabling the firewall, you likely forgot to allow port 22 first — this is exactly the mistake this lab is designed to make you feel once, safely, in a lab environment.

Quick Check

On RHEL/Rocky, you run `firewall-cmd --add-port=8080/tcp` without the `--permanent` flag, then reboot the server. What happens to that rule?

Takeaway: Both firewalld and ufw sit on top of the same nftables kernel framework, but their rule syntax and models (zones vs. direct allow-lists) are different enough that you must know which one a given server actually uses.

Sponsor / Advertisement