iptables & nftables Cheatsheet

Concepts and Tables

Use this iptables & nftables reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Two commands, one kernel subsystem

Modern Linux filters packets with nftables in the kernel. The iptables command you type is usually a compatibility front end that writes nftables rules.

CommandWhat it talks toVersion string shows
iptables-legacythe old x_tables kernel API(legacy)
iptables-nftnftables, through a translation layer(nf_tables)
nftnftables directly, native syntaxnftables v1.x
iptables --version              # iptables v1.8.10 (nf_tables)
sudo iptables-legacy -S         # rules in the legacy tables, often empty
sudo nft list ruleset           # the real ruleset, including iptables-nft rules

Do not mix the two backends. Rules added with iptables-legacy are invisible to nft list ruleset and to iptables-nft, and both sets are evaluated by the kernel, so a "missing" rule that still blocks traffic is almost always the other backend.

What ships where

DistributionDefault packet filterDefault front end
RHEL / Rocky / Alma 8, 9, 10nftablesfirewalld
Fedora 32+nftablesfirewalld
CentOS / RHEL 7 (legacy)legacy iptablesfirewalld, iptables backend
Debian 10+iptables-nft is the default alternativenone, nft or iptables by hand
Ubuntu 20.04+iptables-nftufw installed, inactive by default on server
openSUSE Leap 15+ / SLES 15nftablesfirewalld
Arch Linuxnftables (iptables-nft package)none
Alpine Linuxiptables or nftables, both packagednone, /etc/init.d scripts
# which backend does /sbin/iptables actually point at, Debian and Ubuntu
sudo update-alternatives --display iptables
sudo update-alternatives --config iptables      # switch legacy <-> nft

# RHEL family, the service that owns the ruleset
systemctl is-active firewalld nftables iptables

On any RHEL-family box, assume firewalld owns the ruleset. Writing raw iptables rules there works until firewalld reloads and flushes them.

Tables

A table groups rules by the kind of work they do. Five exist, and most systems only use two.

TablePurposeCommon chains
filteraccept or drop, the default tableINPUT, FORWARD, OUTPUT
natrewrite addresses and portsPREROUTING, INPUT, OUTPUT, POSTROUTING
manglechange packet fields (TTL, TOS, MARK)all five
rawact before connection tracking (NOTRACK)PREROUTING, OUTPUT
securitySELinux packet labelling (SECMARK)INPUT, FORWARD, OUTPUT
sudo iptables -t filter -L -n        # -t filter is the default, so -t is optional
sudo iptables -t nat -L -n -v
sudo iptables -t mangle -S
sudo iptables -t raw -S

Chains and hooks

A chain is a list of rules attached to a point in the kernel's packet path.

ChainFires when
PREROUTINGpacket arrives, before the routing decision
INPUTpacket is destined for this host
FORWARDpacket is routed through this host to somewhere else
OUTPUTpacket was generated locally
POSTROUTINGpacket is about to leave, after the routing decision
                       ┌─────────────┐
   in ──> PREROUTING ──> routing?    ├──> FORWARD ──> POSTROUTING ──> out
          (nat, mangle)└──┬──────────┘   (filter)     (nat, mangle)
                          │ local                        ▲
                          v                              │
                        INPUT                          OUTPUT
                        (filter)                       (filter, nat)
                          │                              ▲
                          v                              │
                     local process ─────────────────────-┘

Two rules follow from the diagram and explain most confusion:

- Traffic to this host hits INPUT, traffic through it hits FORWARD. Blocking a port in INPUT does nothing to traffic being routed or bridged. - DNAT happens in PREROUTING, so by the time FORWARD and INPUT see the packet its destination is already the new address. Filter on the real target, not on the published one.

Rule evaluation

Rules in a chain are checked top to bottom. The first rule whose matches all succeed applies its target, and for terminating targets evaluation of that chain stops there.

TargetEffectTerminating
ACCEPTlet the packet through this table's chainyes
DROPdiscard silently, sender waits and times outyes
REJECTdiscard and send an ICMP error or TCP resetyes
RETURNleave a user chain, or apply the built-in policyyes
LOGwrite to the kernel log, then keep goingno
DNAT / SNAT / MASQUERADE / REDIRECTrewrite addressesyes, in nat
MARK / TOS / TTLchange metadata, then keep goingno
<user chain>jump into another chainno, returns if it falls through

If no rule matches, the chain's policy applies. Policies exist only on built-in chains, and the shipped default is ACCEPT everywhere.

sudo iptables -L INPUT -n | head -1    # Chain INPUT (policy ACCEPT)
sudo iptables -P INPUT DROP            # set the policy, see the warning below

Setting a DROP policy over SSH locks you out the instant it takes effect unless an ACCEPT rule for your own session already exists above it. Add the allow rules first, verify them, then change the policy.

Connection tracking

conntrack is what makes a stateful firewall possible. The kernel remembers flows, so replies to connections you allowed do not need their own rules.

StateMeaning
NEWfirst packet of a flow conntrack has not seen before
ESTABLISHEDpart of a flow already seen in both directions
RELATEDa new flow spawned by an existing one, such as an FTP data channel or an ICMP error
INVALIDdoes not fit any known flow, often a late or malformed packet
UNTRACKEDmatched a NOTRACK rule in the raw table
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo conntrack -L | head            # live flow table, needs conntrack-tools
cat /proc/sys/net/netfilter/nf_conntrack_count   # current entries
cat /proc/sys/net/netfilter/nf_conntrack_max     # table size

That single ESTABLISHED,RELATED rule at the top of INPUT is the foundation of every practical ruleset. Without it, a default-deny policy also blocks the answers to your own outbound connections.