iptables & nftables Cheatsheet

Logging and Rate Limiting

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

LOG and NFLOG

LOG is non-terminating. The packet keeps going through the chain after the log entry is written, so a log rule sits above the rule that actually acts.

sudo iptables -A INPUT -p tcp --dport 22 -j LOG --log-prefix "ssh-in: " --log-level info
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
OptionEffect
--log-prefix "text: "tag, up to 29 characters, keep the trailing space
--log-levelemerg … debug, warning and info are the usual choices
--log-tcp-optionsinclude TCP options
--log-ip-optionsinclude IP options
--log-tcp-sequenceinclude sequence numbers, avoid on shared logs
--log-uidinclude the owning uid for locally generated packets
# NFLOG hands packets to userspace, which is what ulogd and tcpdump read
sudo iptables -A INPUT -j NFLOG --nflog-group 1 --nflog-prefix "dropped"
sudo tcpdump -i nflog:1 -n            # watch logged packets as packets
TargetDestinationPick it when
LOGkernel log, dmesg, journal, syslogquick diagnosis, small volume
NFLOGa netlink group, read by ulogd or tcpdumpstructured logs, full packets, high volume
TRACEkernel log, every table and chain the packet visitsdebugging rule order, never in production

Reading firewall logs

sudo dmesg -w | grep -i 'ssh-in'
sudo journalctl -k -f
sudo journalctl -k --since '10 min ago' | grep 'iptables-drop'
sudo grep 'iptables-drop' /var/log/kern.log      # Debian and Ubuntu
sudo tail -f /var/log/messages                   # RHEL family
kernel: iptables-drop: IN=eth0 OUT= MAC=52:54:00:12:34:56:52:54:00:ab:cd:ef:08:00
  SRC=198.51.100.9 DST=10.0.0.5 LEN=60 TOS=0x00 PREC=0x00 TTL=54 ID=41928 DF
  PROTO=TCP SPT=44122 DPT=3306 WINDOW=29200 RES=0x00 SYN URGP=0
FieldMeaning
IN / OUTinterfaces, an empty OUT means the packet was destined here
MACdestination MAC, source MAC, ethertype, concatenated
SRC / DSTaddresses after any NAT that already happened
PROTO, SPT, DPTprotocol and ports
TTLremaining hops, a rough hint at how far away the sender is
SYN, ACK, RSTthe TCP flags that were set
# top talkers hitting your drop rule
sudo journalctl -k --since today | grep 'iptables-drop' \
  | grep -oP 'SRC=\K[0-9.]+' | sort | uniq -c | sort -rn | head

# most probed ports
sudo journalctl -k --since today | grep 'iptables-drop' \
  | grep -oP 'DPT=\K[0-9]+' | sort -n | uniq -c | sort -rn | head

Keeping logs from filling the disk

An unthrottled log rule on an internet-facing host will write gigabytes during a scan and can wedge the box before the firewall ever fails.

# a log chain that throttles, then drops
sudo iptables -N logdrop
sudo iptables -A logdrop -m limit --limit 5/min --limit-burst 10 \
  -j LOG --log-prefix "iptables-drop: " --log-level info
sudo iptables -A logdrop -j DROP

sudo iptables -A INPUT -j logdrop
GuardCommand fragment
Global cap-m limit --limit 5/min --limit-burst 10
Per source cap-m hashlimit --hashlimit-above 1/min --hashlimit-mode srcip
Skip the noisiest traffican -j RETURN rule above the log for broadcast and multicast
Keep it out of the console--log-level debug plus a syslog rule, or NFLOG
Rate-limit the kernel itselfsysctl net.core.message_cost, net.core.message_burst
# do not log the usual LAN chatter at all
sudo iptables -I logdrop -m addrtype --dst-type BROADCAST -j DROP
sudo iptables -I logdrop -d 224.0.0.0/4 -j DROP

# send firewall logs to their own file, rsyslog
echo ':msg, contains, "iptables-drop:" -/var/log/firewall.log' \
  | sudo tee /etc/rsyslog.d/10-firewall.conf
echo '& stop' | sudo tee -a /etc/rsyslog.d/10-firewall.conf
sudo systemctl restart rsyslog

Rate limiting traffic, not just logs

ModuleScopeBest for
limitone bucket for the whole rulelogs, ICMP, anything where a global cap is fine
hashlimitone bucket per key, such as source addressper-client fairness and abuse control
connlimitconcurrent connections per host or prefixslowloris style connection hoarding
recentaddress history with a time windowbrute-force lockouts
# ICMP echo: 2 a second overall
sudo iptables -A INPUT -p icmp --icmp-type echo-request \
  -m limit --limit 2/second --limit-burst 5 -j ACCEPT
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j DROP

# new SMTP connections: 30 a minute per source
sudo iptables -A INPUT -p tcp --dport 25 -m conntrack --ctstate NEW \
  -m hashlimit --hashlimit-above 30/min --hashlimit-burst 40 \
  --hashlimit-mode srcip --hashlimit-name smtp -j DROP

# no more than 15 open HTTP connections per client
sudo iptables -A INPUT -p tcp --dport 80 \
  -m connlimit --connlimit-above 15 --connlimit-mask 32 \
  -j REJECT --reject-with tcp-reset
# inspect the hashlimit buckets
sudo cat /proc/net/ipt_hashlimit/smtp

The token bucket is worth understanding: --limit 5/min refills five tokens a minute, --limit-burst 10 sets the bucket size. Ten packets can match back to back, then the rule matches at the refill rate, and quiet time refills the bucket. A burst of 1 makes the rule match strictly at the average rate.

SYN floods and connection abuse

# cap new connection attempts per second, globally
sudo iptables -A INPUT -p tcp --syn -m limit --limit 100/second \
  --limit-burst 200 -j ACCEPT
sudo iptables -A INPUT -p tcp --syn -j DROP

# SYN cookies do more than any rule here
sudo sysctl -w net.ipv4.tcp_syncookies=1
sudo sysctl -w net.ipv4.tcp_max_syn_backlog=4096
sudo sysctl -w net.ipv4.tcp_synack_retries=2
DefenceWhat it actually helps with
tcp_syncookiesreal SYN floods, this is the primary mitigation
--syn -m limitsmall floods and scans, and it also limits legitimate bursts
hashlimit per sourceone abusive client, not a distributed flood
connlimitconnection hoarding by a small number of hosts
recentrepeated login attempts from the same address
Upstream scrubbingvolumetric attacks, nothing on the host helps once the link is full

A rule that drops SYNs above a global rate also drops legitimate traffic during a launch or a cache flush. Set the threshold from measured peak load, not from a blog post, and prefer syncookies as the first line.

An audit-friendly logging layout

sudo iptables -N logaccept
sudo iptables -A logaccept -m limit --limit 10/min \
  -j LOG --log-prefix "fw-accept: " --log-level info
sudo iptables -A logaccept -j ACCEPT

sudo iptables -N logreject
sudo iptables -A logreject -m limit --limit 5/min \
  -j LOG --log-prefix "fw-reject: " --log-level warning
sudo iptables -A logreject -j REJECT --reject-with icmp-admin-prohibited

# rules then read as intent, and every decision is labelled
sudo iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/8 -j logaccept
sudo iptables -A INPUT -j logreject
PracticePayoff
One log chain per decisionevery rule ends in a labelled, throttled outcome
Distinct --log-prefix per chaingrep and alerting become trivial
warning for denies, info for acceptsseverity filtering works without parsing
Its own log file plus rotationfirewall noise does not bury other kernel messages
-m comment on the rulesthe log prefix says what, the comment says why