Where to start First steps track · 15 stages
Linux Labs

Firewall — nftables / ufw

6 min Ubuntu 24.04 · nftables · ufw

The Linux firewall: filtering traffic with simple ufw and low-level nftables, checking the rules.

Cheat sheet

4 commands Printable version
  • sudo ufw allow 22/tcp SSH FIRST
  • sudo ufw status verbose rules
  • sudo ufw default deny incoming
  • sudo nft list ruleset low level

Overview

On Ubuntu, packet filtering is handled by nftables (in the kernel). On top of it sits ufw (Uncomplicated Firewall), a convenient wrapper for typical rules.

  • For beginners/typical servers — ufw.
  • For complex policies (NAT, sets, forwarding) — nftables directly.

Danger. When configuring a firewall remotely, allow SSH (22/tcp) first, or you'll lock yourself out. Keep a second session open.

Recipes

2

ufw

bash
sudo ufw status verbose           # current rules
sudo ufw allow 22/tcp             # allow SSH (FIRST!)
sudo ufw allow 80,443/tcp         # web
sudo ufw allow from 192.168.1.0/24 to any port 5432  # subnet only
sudo ufw deny 23/tcp
sudo ufw delete allow 80/tcp
sudo ufw enable                   # turn on, default policy applies
sudo ufw default deny incoming    # deny incoming traffic by default

A persistent nftables set

Rules added with nft add live until a reboot. The persistent set is the file /etc/nftables.conf, loaded by the nftables service (ExecStart=/usr/sbin/nft -f /etc/nftables.conf). On the lab machine the service is disabled, and the file from the package lets everything through: its chains have no rules and no policy.

A basic "drop whatever is not allowed" set:

text
#!/usr/sbin/nft -f
flush ruleset

table inet filter {
	chain input {
		type filter hook input priority filter; policy drop;
		iif lo accept
		ct state established,related accept
		meta l4proto { icmp, ipv6-icmp } accept
		tcp dport { 22, 80 } accept
		counter
	}
}
Line Why
policy drop everything not allowed below is dropped
iif lo accept the machine talks to itself: the database and admin panel on 127.0.0.1 keep working
ct state established,related accept replies to connections the machine opened itself
meta l4proto { icmp, ipv6-icmp } accept ping and ICMP service messages
tcp dport { 22, 80 } accept the ports open to the outside, as a list
counter how many packets reached the drop policy
bash
sudo nft -c -f /etc/nftables.conf      # check without applying
sudo systemctl enable --now nftables   # load now and at boot
sudo nft list ruleset

nft -c prints the error with its position in the line and exits with 1:

text
/tmp/bad.nft:1:54-54: Error: syntax error, unexpected '}'
table inet filter { chain input { tcp dport 22 acept } }
                                                     ^

From outside — another network namespace or another machine — closed ports show up as filtered: the packet is dropped with no answer (see nmap):

text
PORT     STATE    SERVICE
22/tcp   open     ssh
80/tcp   open     http
5432/tcp filtered postgresql
8080/tcp filtered http-proxy

flush ruleset at the top of the file deletes every table, other people's too: a table created by hand was gone after systemctl enable --now nftables. The fail2ban and Docker tables come back after restart nftables only when those services restart.

Troubleshooting

4

I locked myself out of SSH

Get in through the hypervisor console/serial; ufw disable, or fix the rule set and reload it.

A rule isn't taking effect

  • Order matters: in nftables, you need an accept for the traffic you want before policy drop.
  • ufw and manual nft rules can conflict — use one or the other.
  • Check whether the traffic even arrives: ss -tlnp (is the service listening?) before blaming the firewall.

The port isn't reachable from outside, even though the service is listening

sudo ufw status / nft list ruleset — is the port allowed; don't forget an external NAT/router.

The port is open from the machine itself, though it was closed

curl http://192.0.2.1:8080/ on the machine itself answers 200 although the drop policy closes the port. A packet to the machine's own address goes through the lo interface, and iif lo accept lets it in. Test the filter from outside: from another machine or a separate network namespace (ip netns exec … nmap).

Danger zone

2

ufw default deny incoming / enable without an SSH rule

Both commands take effect immediately, dropping an already-open session if it has no matching allow rule. On a remote machine with no hypervisor console, this means total loss of access until you can intervene physically/via console. The order matters: ufw allow 22/tcp first, then default deny incoming and enable.

Replacing the whole nftables ruleset (nft -f)

sudo nft -f /etc/nftables.conf loads the file as-is, with no confirmation. A mistake in the file (say, policy drop with no accept for the SSH port) applies immediately and cuts off a remote session just like ufw can.

Terms

6
nftables table
A named group of chains and other objects within an nftables family.
nftables chain
An ordered sequence of rules; a base chain attaches to a packet-processing hook.
Netfilter hook
A network processing stage to which a filtering base chain can attach.
firewall verdict
A rule-processing decision such as accepting, dropping or explicitly rejecting a packet.
chain policy
The default decision when a packet reaches the end of a base chain without a terminating verdict. Also appears in: nmap: which ports are visible from outside
packet counter
The number of packets reaching a counter action, normally accompanied by a byte counter. Also appears in: The classic iptables firewall

See also

  • ss — Who's listening on ports and what connections are open — the modern replacement for netstat.
  • ip — Managing interfaces, addresses, and routes (iproute2) — the replacement for ifconfig/route.
  • sshd — The SSH server — remote access via keys, config and hardening.
Loading…