Firewalls — firewalld and ufw¶
A host firewall controls which network traffic is allowed in and out. This page explains the Linux firewall landscape, then shows practical management with firewalld (RHEL/AlmaLinux) and ufw (Ubuntu), finishing with a common "SSH + HTTP + HTTPS only" recipe.
Tested on
firewalld 1.2 on AlmaLinux 9.4; ufw 0.36 on Ubuntu 22.04. Both are front-ends over the kernel's packet-filtering framework.
The Linux firewall landscape¶
All Linux firewalling happens in the kernel via netfilter. The tools are just layers on top:
netfilter ← kernel packet-filtering framework (the actual engine)
└── nftables ← modern rule syntax / userspace tool (nft)
└── iptables ← legacy tool, now a compatibility shim over nftables
firewalld / ufw ← high-level front-ends that generate the rules for you
You rarely write nft/iptables rules by hand on a modern system. firewalld (RHEL family) and ufw (Debian/Ubuntu) manage the rules with simple, persistent commands. Use the front-end your distro ships and avoid mixing it with hand-written rules.
firewalld (RHEL / AlmaLinux)¶
firewalld organises rules into zones. A zone is a trust level applied to one or more interfaces; the default is usually public. Within a zone you allow named services (e.g. ssh, http) or raw ports.
Check state and zones¶
# Is the firewall running?
sudo firewall-cmd --state
# running
# Which zones are active, and on which interfaces?
sudo firewall-cmd --get-active-zones
Permanent vs runtime¶
firewalld keeps a runtime config (lost on reload/reboot) and a permanent config (on disk). The standard pattern is to add a rule permanently, then reload:
# Allow the HTTP and HTTPS services permanently
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
# Apply the permanent rules to the running firewall
sudo firewall-cmd --reload
Tip
firewall-cmd --get-services lists every predefined service name (which maps to known ports). Use a service name when one exists; it's clearer than a raw port number.
Open a raw port¶
# Allow a custom TCP port (e.g. an app on 8080)
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
Rich rules (fine-grained control)¶
Rich rules let you scope a rule to a source address, log it, or rate-limit it:
# Allow SSH only from the office subnet
sudo firewall-cmd --permanent --add-rich-rule='
rule family="ipv4" source address="203.0.113.0/24" service name="ssh" accept'
sudo firewall-cmd --reload
Masquerading / NAT (brief)¶
When a host routes traffic for other machines (e.g. a gateway or VM host), enable masquerade so outbound packets are rewritten with the host's IP:
Removing rules¶
# Mirror the --add-* commands with --remove-*
sudo firewall-cmd --permanent --remove-service=http
sudo firewall-cmd --permanent --remove-port=8080/tcp
sudo firewall-cmd --reload
ufw (Ubuntu)¶
Ubuntu's Uncomplicated Firewall wraps the same kernel machinery with a simpler syntax.
# Set sane defaults: block all incoming, allow all outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (the OpenSSH app profile maps to port 22)
sudo ufw allow OpenSSH
# Allow web ports
sudo ufw allow 80,443/tcp
# Turn the firewall on (enables at boot too)
sudo ufw enable
Allow SSH before enabling
Always ufw allow OpenSSH (or firewall-cmd --add-service=ssh) before enabling/locking down the firewall on a remote box. Otherwise you cut your own connection. Keep a second session open — see the SSH page.
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing)
To Action From
-- ------ ----
22/tcp (OpenSSH) ALLOW IN Anywhere
80,443/tcp ALLOW IN Anywhere
Raw nft / iptables (for awareness)¶
You may still encounter direct rule tools — handy for reading what a front-end produced or working on a minimal system:
# nftables: list the full active ruleset
sudo nft list ruleset
# iptables (legacy syntax, shimmed over nftables): list rules with packet counts
sudo iptables -L -n -v
Don't combine hand-written nft/iptables rules with firewalld/ufw on the same host — let one tool own the ruleset to avoid conflicting or overwritten rules.
Common recipe: allow SSH + HTTP + HTTPS only¶
A typical web server should accept only these three inbound services.
Result
Inbound connections succeed only on 22, 80, and 443; everything else is dropped. Outbound traffic (updates, DNS, etc.) remains unrestricted.
Verify your work¶
# firewalld: confirm only the intended services are allowed
sudo firewall-cmd --list-all
# ufw: confirm status and rules
sudo ufw status verbose
# From another machine, confirm an allowed port is open and a blocked one is not
nc -vz <server-ip> 443 # should succeed
nc -vz <server-ip> 3306 # should time out / be refused
Expected: allowed services/ports appear in the listing, an external nc to 443 succeeds, and a blocked port (e.g. 3306) does not connect.
Summary¶
- All Linux firewalling rides on kernel netfilter; nftables (with the legacy
iptablesshim) is the rule engine, and firewalld/ufw are the practical front-ends. - firewalld (RHEL/AlmaLinux) uses zones, services, and ports; add rules with
--permanent --add-service/--add-port, then--reload. Rich rules scope by source;--add-masqueradeenables NAT. - ufw (Ubuntu) is simpler:
ufw default deny incoming,ufw allow OpenSSH,ufw allow 80,443/tcp,ufw enable. - Always permit SSH before locking down a remote host, and keep a second session open.
- The web-server recipe allows only SSH + HTTP + HTTPS; verify with
--list-all/ufw statusand an externalncprobe.
See SSH for securing remote access and the Server Hardening Checklist for the full lockdown.