iptables & nftables Cheatsheet
Matches and Modules
Use this iptables & nftables reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
The match modules worth knowing
-m <module> loads an extension that adds its own options. Some are implied:
-p tcp loads the tcp module automatically, which is why --dport works
without an explicit -m tcp.
| Module | Matches on | Typical use |
|---|---|---|
tcp / udp | ports, flags | the bulk of every ruleset |
conntrack | flow state | stateful accept, INVALID drops |
multiport | up to 15 ports at once | shorter rulesets |
iprange | an arbitrary address range | ranges that are not a clean CIDR |
limit | packet rate, token bucket | log throttling, ICMP limits |
hashlimit | rate per source or destination | per-client abuse control |
recent | recent-address list | SSH brute-force slowdown |
connlimit | concurrent connections per host | connection flood limits |
state | older spelling of conntrack | legacy rules, still works |
mac | source MAC address | LAN-only, MACs do not cross routers |
owner | local uid, gid | egress control in OUTPUT |
comment | nothing, adds a label | documenting rules |
time | time of day, weekday | maintenance windows |
set | an ipset | large address lists |
addrtype | LOCAL, BROADCAST, MULTICAST | anti-spoofing, martians |
ttl | IP TTL value | detecting oddities |
Ports and TCP flags
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT sudo iptables -A INPUT -p tcp --dport 6000:6010 -j ACCEPT # inclusive range sudo iptables -A INPUT -p tcp --sport 1024: -j ACCEPT # 1024 and up sudo iptables -A INPUT -p tcp -m multiport --dports 80,443,8080 -j ACCEPT sudo iptables -A INPUT -p tcp -m multiport --ports 22,80 -j ACCEPT # src or dst sudo iptables -A INPUT -p udp --dport 53 -j ACCEPT
| Flag option | Meaning |
|---|---|
--dport, --sport | single port, a:b range, or :b / a: open ended |
--syn | shorthand for --tcp-flags SYN,RST,ACK,FIN SYN |
--tcp-flags <examine> <set> | check the first list, require exactly the second set |
--tcp-option <n> | a specific TCP option is present |
# only connection openers, replies handled by the state rule sudo iptables -A INPUT -p tcp --syn --dport 22 -j ACCEPT # drop the classic scan and malformed-flag patterns sudo iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP # null scan sudo iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP # xmas sudo iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP sudo iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP sudo iptables -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP
Connection state
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP sudo iptables -A INPUT -m conntrack --ctstate NEW -p tcp --dport 22 -j ACCEPT sudo iptables -A INPUT -m conntrack --ctstate NEW ! --syn -p tcp -j DROP sudo iptables -A INPUT -m conntrack --ctstate DNAT -j ACCEPT # was DNATed
| Option | Matches |
|---|---|
--ctstate | NEW, ESTABLISHED, RELATED, INVALID, UNTRACKED, SNAT, DNAT |
--ctproto | the tracked protocol |
--ctorigsrc, --ctorigdst | the original addresses before NAT |
--ctrepldst | the reply-direction destination |
--ctdir | ORIGINAL or REPLY direction |
RELATED depends on protocol helper modules. Active FTP, TFTP, and SIP need
nf_conntrack_ftp and friends loaded, and on modern kernels helpers must be
enabled explicitly:
sudo modprobe nf_conntrack_ftp
sudo sysctl -w net.netfilter.nf_conntrack_helper=1
sudo iptables -A INPUT -p tcp -m conntrack --ctstate RELATED \
-m helper --helper ftp -j ACCEPTRate limiting per source
limit is global, one bucket for the whole rule. hashlimit and recent track
per address, which is what abuse control needs.
# global: at most 5 matches a second, burst of 10 sudo iptables -A INPUT -p icmp -m limit --limit 5/second --limit-burst 10 -j ACCEPT # per source: 10 new HTTP connections a second per client IP sudo iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW \ -m hashlimit --hashlimit-above 10/sec --hashlimit-burst 20 \ --hashlimit-mode srcip --hashlimit-name http-flood -j DROP # at most 20 concurrent connections per client sudo iptables -A INPUT -p tcp --dport 80 \ -m connlimit --connlimit-above 20 --connlimit-mask 32 -j REJECT # per /24 rather than per host sudo iptables -A INPUT -p tcp --dport 80 \ -m connlimit --connlimit-above 200 --connlimit-mask 24 -j REJECT
| Option | Meaning |
|---|---|
--limit N/{second,minute,hour,day} | average rate the rule may match |
--limit-burst N | bucket size, matches allowed back to back |
--hashlimit-mode | srcip, dstip, srcport, dstport, comma-combined |
--hashlimit-above / --hashlimit-upto | match when over, or under, the rate |
--connlimit-mask | prefix length the count is grouped by, 32 is per host |
The recent module
recent keeps named lists of addresses with timestamps, so one rule can record
and another can act on that history.
# slow SSH brute force: 4 attempts per minute per source sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \ -m recent --set --name SSH --rsource sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \ -m recent --update --seconds 60 --hitcount 4 --name SSH --rsource \ -j LOG --log-prefix "ssh-throttle: " sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \ -m recent --update --seconds 60 --hitcount 4 --name SSH --rsource -j DROP
| Option | Effect |
|---|---|
--set | add the address to the list, refreshing its timestamp |
--update | match if listed within --seconds, and refresh the timestamp |
--rcheck | match if listed, do not refresh |
--remove | drop the address from the list |
--seconds N | only consider hits within the window |
--hitcount N | require at least N recorded hits |
--rsource / --rdest | key the list on source or destination address |
cat /proc/net/xt_recent/SSH # inspect the list echo / | sudo tee /proc/net/xt_recent/SSH # clear it echo +203.0.113.9 | sudo tee /proc/net/xt_recent/SSH # add by hand
The list is capped by ip_pkt_list_tot (20 hits by default), so a --hitcount
above that silently never fires. Load xt_recent with a larger
ip_pkt_list_tot if you need a bigger window.
Large address lists with ipset
Thousands of -s rules are checked one at a time. An ipset is a hash the kernel
looks up once, so one rule replaces the whole list.
sudo ipset create blocklist hash:net sudo ipset add blocklist 203.0.113.0/24 sudo ipset add blocklist 198.51.100.7 sudo iptables -I INPUT -m set --match-set blocklist src -j DROP sudo ipset create trusted hash:ip timeout 3600 # entries expire sudo ipset add trusted 192.0.2.10 timeout 600 sudo ipset list blocklist sudo ipset save > /etc/ipset.conf sudo ipset restore < /etc/ipset.conf
| Set type | Holds |
|---|---|
hash:ip | single addresses |
hash:net | CIDR prefixes |
hash:ip,port | address and port pairs |
hash:mac | MAC addresses |
bitmap:port | a port range, very fast |
Time windows and address types
# maintenance page reachable only during a window, UTC by default sudo iptables -A INPUT -p tcp --dport 8080 -m time \ --timestart 02:00 --timestop 04:00 --weekdays Sat,Sun -j ACCEPT # use local time instead of UTC sudo iptables -A INPUT -p tcp --dport 8080 -m time --kerneltz \ --timestart 09:00 --timestop 17:00 -j ACCEPT # martians and broadcast handling sudo iptables -A INPUT -m addrtype --src-type BROADCAST -j DROP sudo iptables -A INPUT -i wan0 -m addrtype --dst-type LOCAL -j ACCEPT
-m time compares against UTC unless --kerneltz is given, which is the single
most common mistake with this module.