Skip to content
RunStack
Wiki· Updated 29 Oct 2026

iptables and nftables

iptables and nft: list rules, open and close a port, allowlists, keep rules after reboot, port forwarding, ufw.

Check the result with the rules analyzer: paste the output of iptables-save or nft list ruleset.

What do I have

iptables -V                      # (nf_tables) — iptables on top of nft; (legacy) — the old one
nft list ruleset | head          # nft rules (including those created via iptables-nft)
ufw status verbose               # if ufw is installed
firewall-cmd --state             # if firewalld is installed

Change rules through one tool: ufw, firewalld or iptables/nft directly. Mixing them is the main source of "the rule is there but does not work".

List rules

iptables -S                              # as commands
iptables -L INPUT -n -v --line-numbers   # as a table, with counters and numbers
iptables -t nat -L -n -v                 # NAT (Docker, port forwarding)
iptables-save > rules-$(date +%F).txt    # everything — for the analyzer and as a backup
ip6tables-save                           # the same for IPv6 — often forgotten
nft list ruleset

Baseline: everything closed except what you need

iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
iptables -P INPUT DROP      # last — after the SSH rule

The same in nft:

table inet filter {
  chain input {
    type filter hook input priority 0; policy drop;
    iif "lo" accept
    ct state established,related accept
    tcp dport 22 accept
    tcp dport { 80, 443 } accept
  }
}
nft -f /etc/nftables.conf && systemctl enable nftables

Open, close, delete

iptables -I INPUT 3 -p tcp --dport 8443 -j ACCEPT    # insert as the third rule
iptables -A INPUT -p tcp --dport 6379 -j DROP
iptables -L INPUT --line-numbers                     # rule number
iptables -D INPUT 5                                   # delete by number
iptables -D INPUT -p tcp --dport 8443 -j ACCEPT      # delete by text
nft -a list chain inet filter input                   # numbers (handles) in nft
nft delete rule inet filter input handle 12

Order matters: rules are checked top to bottom until the first ACCEPT or DROP. A DROP below a rule that accepts everyone closes nothing.

Allowlist: only from my addresses

iptables -A INPUT -p tcp --dport 22 -s 203.0.113.10 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -s 198.51.100.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP

Many addresses — use a set (ipset with iptables, a set in nft):

ipset create admins hash:net
ipset add admins 203.0.113.10
iptables -A INPUT -p tcp --dport 22 -m set --match-set admins src -j ACCEPT
set admins { type ipv4_addr; flags interval; elements = { 203.0.113.10, 198.51.100.0/24 } }
tcp dport 22 ip saddr @admins accept

Rate limiting (SSH)

iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 6 -j DROP
tcp dport 22 ct state new limit rate over 6/minute drop

fail2ban is more convenient: it bans only those who guess passwords. rs harden turns it on.

Keep rules after a reboot

apt install iptables-persistent
netfilter-persistent save          # → /etc/iptables/rules.v4 and rules.v6
iptables-restore < /etc/iptables/rules.v4
nft list ruleset > /etc/nftables.conf && systemctl enable nftables

Port forwarding (NAT)

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A PREROUTING -p tcp --dport 2222 -j DNAT --to-destination 10.0.0.5:22
iptables -t nat -A POSTROUTING -d 10.0.0.5 -p tcp --dport 22 -j MASQUERADE
iptables -A FORWARD -d 10.0.0.5 -p tcp --dport 22 -j ACCEPT

ufw — the same, shorter

ufw default deny incoming && ufw default allow outgoing
ufw allow 22/tcp
ufw allow from 203.0.113.10 to any port 5432 proto tcp
ufw status numbered && ufw delete 3

Ports published by Docker are not closed by ufw — see Docker and the firewall.

Do not lock yourself out

iptables-save > /root/fw-ok.txt
echo "iptables-restore < /root/fw-ok.txt" | at now + 5 minutes   # rollback in 5 minutes
# … change the rules, test a login from a NEW window …
atq && atrm <number>                                             # all good — cancel the rollback

rs harden rolls SSH and the firewall back the same way. As a last resort — the console (VNC) in your provider's panel.