Firewall Basics: ufw, firewalld, and iptables

Open the ports your server needs, block the rest, and never lock yourself out — with ufw on Ubuntu, firewalld on RHEL, or raw iptables underneath.

A firewall controls which network traffic is allowed in and out of your server. Different distributions ship different front-ends: Ubuntu/Debian typically use ufw, while RHEL/CentOS/Rocky/AlmaLinux use firewalld. Both sit on top of the kernel's iptables (or the newer nftables). Keep the commands straight — a ufw command won't work on a firewalld box, and vice versa.

ufw (Ubuntu / Debian)

ufw stands for "Uncomplicated Firewall," and the syntax lives up to the name:

sudo ufw allow 22/tcp        # Allow port 22 (SSH)
sudo ufw allow OpenSSH       # Same thing, by application name
sudo ufw allow 80,443/tcp    # Allow HTTP and HTTPS
sudo ufw deny 23             # Block port 23 (Telnet)
sudo ufw enable              # Turn the firewall on
sudo ufw status verbose      # Show rules and default policies
sudo ufw delete allow 80     # Remove a rule

By default ufw denies incoming and allows outgoing traffic, so once it's enabled, any port you haven't explicitly allowed is blocked.

firewalld (RHEL / CentOS / Rocky / Alma)

firewalld is built around zones and services, and it separates runtime rules from permanent ones:

sudo firewall-cmd --add-service=http --permanent   # Permanently allow the HTTP service
sudo firewall-cmd --add-port=8080/tcp --permanent  # Permanently allow port 8080
sudo firewall-cmd --reload                          # Reload so permanent rules take effect
sudo firewall-cmd --list-all                        # List every rule in the active zone

The key gotcha: changes made with --permanent are written to disk but do not apply until you run --reload. Changes made without --permanent apply instantly but vanish on reboot. To get both, add a runtime rule first to confirm it works, then re-add it with --permanent to make it stick.

iptables fundamentals

iptables is the engine underneath — both ufw and firewalld ultimately generate iptables rules for you. A few concepts are enough to read what's going on:

  • Tables: the one you'll deal with most is filter, which allows or blocks traffic.
  • Chains: INPUT (traffic to this host), OUTPUT (traffic from this host), and FORWARD (routed traffic).
  • Rules: matched top to bottom; the first match wins and applies ACCEPT, DROP, or REJECT.

To inspect the current rules:

sudo iptables -L -n -v       # List all chains and rules (-n skips slow DNS lookups)

For day-to-day work, stick with ufw or firewalld and let them manage iptables; hand-writing raw rules is rarely necessary.

Don't lock yourself out

You manage a remote server entirely over SSH. If you enable the firewall before allowing the SSH port, your connection drops immediately and you can't reconnect. Do it in this order:

  • Allow the SSH port first (22/tcp by default, or whatever port you moved it to).
  • Confirm the rule is in place (ufw status or firewall-cmd --list-all).
  • Then run enable or reload.

If you do get locked out, don't panic — log in through your VPS provider's web console, VNC, or rescue mode, which bypasses SSH entirely and lets you fix the firewall rules directly.

Security groups ≠ host firewall

A cloud platform's security group runs at the network layer outside your virtual machine and is managed by the provider, while ufw/firewalld is a host firewall running inside the operating system. For a packet to reach your service, both layers must allow it. When a port seems unreachable, check both sides: a port left closed in the security group can't be fixed by host config alone, and the reverse is equally true.

Summary

Use ufw on Ubuntu and firewalld on RHEL; both are front-ends to iptables. Before changing anything, make sure the SSH port is allowed, then enable or reload — that's how you avoid locking yourself out. And remember that the security group (platform layer) and the host firewall (OS layer) are two separate doors: traffic only gets through when both are open.