Firewalls: iptables and nftables
iptables and nftables are two generations of the same kernel packet-filtering framework (netfilter) - most distros put a friendlier frontend like ufw or firewalld in front of whichever one they use.
Both tools configure netfilter, the packet-filtering framework built into the Linux kernel - they're interfaces to the same underlying mechanism, not competing implementations of separate ones.
iptables: the long-standing standard
iptables organizes rules into chains (INPUT, OUTPUT, FORWARD for
the filter table) processed in order, with a default policy for what
happens when nothing matches:
sudo iptables -L -v -n
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443
A minimal example allowing SSH and HTTPS, dropping everything else inbound:
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -P INPUT DROP
That ESTABLISHED,RELATED line matters: without it, reply traffic for
connections you initiated outbound gets dropped too, since each packet
is otherwise evaluated independently of any connection state.
Rules set this way don't survive a reboot on their own - persist them with
a distro-specific mechanism (iptables-persistent on Debian/Ubuntu,
saving to /etc/sysconfig/iptables and enabling the iptables service on
older RHEL-family systems).
nftables: the successor
nftables replaces iptables (and the separate ip6tables, arptables,
ebtables tools it required for IPv6 and non-IP protocols) with one
unified framework and syntax. It's the default backend on current Debian,
Ubuntu, Fedora, and RHEL - iptables commands on these systems are often
translated to nftables rules underneath via a compatibility layer
(iptables-nft), rather than using the original kernel implementation
directly.
sudo nft list ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
ct state invalid drop
iif lo accept
meta l4proto { icmp, ipv6-icmp } accept
tcp dport { 22, 443 } accept
}
}
With policy drop, anything not explicitly accepted is gone - which is
why the loopback line (iif lo) and ICMP matter as much as the open
ports. Without iif lo accept, services talking to each other over
localhost (a web app and its local database, for instance) break;
without ICMPv6, IPv6 itself stops working, since neighbor discovery runs
over it.
nftables rules are typically written to a config file
(/etc/nftables.conf) and loaded as a set, rather than built up one
imperative command at a time the way iptables commands usually are -
closer to declaring the desired ruleset than scripting it.
Frontends most distros actually put in front of this
Writing raw rules directly is uncommon day-to-day - most distributions ship a simpler management layer:
-
ufw (Uncomplicated Firewall) - the default on Ubuntu, wraps iptables/nftables with a much simpler syntax:
sudo ufw allow 22/tcp sudo ufw allow ssh sudo ufw enable sudo ufw status verbose -
firewalld - the default on Fedora and RHEL, organizes rules into named zones (
public,home,internal, …) representing trust levels, with a runtime vs. permanent configuration distinction:sudo firewall-cmd --add-service=ssh --permanent sudo firewall-cmd --add-service=https --permanent sudo firewall-cmd --reload sudo firewall-cmd --list-allThe
--permanentflag matters - without it, a change applies only to the current runtime configuration and is lost on the next reload or reboot.
Checking what's actually active
Before assuming a connectivity problem is a firewall rule, confirm which tool is even managing it:
sudo ufw status
sudo firewall-cmd --state
sudo nft list ruleset
See Troubleshooting Connectivity for where firewall checks fit into a broader diagnostic process, and Hardening Basics for firewall configuration as part of baseline server hardening.