iptables Cheat Sheet: Firewall Rules That Actually Make Sense

6 min read
Intermediate iptables Firewall Linux Security Cheat Sheet

Prerequisites

  • Root access to a Linux server
  • Basic understanding of TCP/IP and ports

Quick Answer: iptables -A INPUT -p tcp --dport 22 -j ACCEPT allows SSH. iptables -A INPUT -s 1.2.3.4 -j DROP blocks an IP. iptables -A INPUT -j DROP blocks everything else (put this LAST). iptables -L -n lists rules. iptables-save > /etc/iptables.rules saves them.

Need a VPS? Vultr (free credit), DigitalOcean ($200 free credit), or RackNerd (cheap annual deals).

How iptables Works

Traffic flows through chains:

  • INPUT — packets coming TO your server
  • OUTPUT — packets leaving FROM your server
  • FORWARD — packets passing THROUGH your server (routing)

Each rule says: if a packet matches these conditions, do this target:

  • ACCEPT — allow the packet
  • DROP — silently discard (sender gets no response)
  • REJECT — discard and send error back to sender
  • LOG — log the packet then continue to next rule

Rules are checked top to bottom. First match wins.

View Current Rules

# List all rules
iptables -L -n -v

# List with line numbers (for inserting/deleting)
iptables -L -n --line-numbers

# List specific chain
iptables -L INPUT -n -v

# List as iptables commands (easy to copy)
iptables-save

Basic Server Protection

This is a good starting firewall for any server:

# 1. Allow established connections (responses to your outgoing requests)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# 2. Allow loopback (localhost)
iptables -A INPUT -i lo -j ACCEPT

# 3. Allow SSH
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# 4. Allow HTTP and HTTPS
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# 5. Allow ping
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT

# 6. Drop everything else
iptables -A INPUT -j DROP

Allow and Block

Allow a Port

# Allow single port
iptables -A INPUT -p tcp --dport 3306 -j ACCEPT

# Allow port range
iptables -A INPUT -p tcp --dport 8000:8100 -j ACCEPT

# Allow UDP port
iptables -A INPUT -p udp --dport 51820 -j ACCEPT

# Allow port only from specific IP
iptables -A INPUT -p tcp -s 10.0.0.5 --dport 3306 -j ACCEPT

# Allow port from subnet
iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 22 -j ACCEPT

Block

# Block an IP
iptables -A INPUT -s 1.2.3.4 -j DROP

# Block a subnet
iptables -A INPUT -s 10.0.0.0/8 -j DROP

# Block a port
iptables -A INPUT -p tcp --dport 23 -j DROP

# Block outgoing to an IP (prevent your server from connecting)
iptables -A OUTPUT -d 1.2.3.4 -j DROP

Rate Limiting

Limit SSH Brute Force

# Allow max 4 new SSH connections per minute per IP
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m recent --set --name SSH
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP

Limit HTTP Requests

# Max 25 simultaneous connections per IP
iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 25 -j DROP
iptables -A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 25 -j DROP

Limit Ping

# Allow 1 ping per second
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j DROP

NAT and Port Forwarding

Enable IP Forwarding

# Temporary
echo 1 > /proc/sys/net/ipv4/ip_forward

# Permanent
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p

Port Forwarding

# Forward port 8080 on public IP to port 80 on internal server 192.168.1.100
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80
iptables -A FORWARD -p tcp -d 192.168.1.100 --dport 80 -j ACCEPT

# Masquerade outgoing traffic (for NAT gateway)
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Redirect Port Locally

# Redirect port 80 to 8080 on same machine
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

Block Countries (GeoIP)

# Using ipset for efficient country blocking
apt install ipset

# Create set
ipset create blocked-countries hash:net

# Add country IP ranges (download from ipdeny.com)
wget -q https://www.ipdeny.com/ipblocks/data/countries/cn.zone
while read cidr; do ipset add blocked-countries "$cidr"; done < cn.zone

# Block the set
iptables -A INPUT -m set --match-set blocked-countries src -j DROP

Logging

# Log dropped packets (insert BEFORE the final DROP rule)
iptables -I INPUT -j LOG --log-prefix "IPTABLES-DROP: " --log-level 4

# Log specific traffic
iptables -A INPUT -p tcp --dport 22 -j LOG --log-prefix "SSH-ATTEMPT: "

# View logs
journalctl -k | grep "IPTABLES-DROP"
# Or check /var/log/kern.log

Managing Rules

Delete Rules

# Delete by line number
iptables -L INPUT --line-numbers
iptables -D INPUT 3              # Delete rule #3

# Delete by exact match
iptables -D INPUT -s 1.2.3.4 -j DROP

# Flush all rules (careful!)
iptables -F                      # Flush all chains (WARNING: if default policy is DROP, this locks you out! Run: iptables -P INPUT ACCEPT first)
iptables -F INPUT                # Flush specific chain

Insert Rules (at specific position)

# Insert at position 1 (top)
iptables -I INPUT 1 -p tcp --dport 8443 -j ACCEPT

# Insert at position 3
iptables -I INPUT 3 -s 10.0.0.5 -j ACCEPT

Save and Restore

# Save current rules
iptables-save > /etc/iptables.rules

# Restore rules
iptables-restore < /etc/iptables.rules

# Auto-restore on boot (Debian/Ubuntu)
apt install iptables-persistent
netfilter-persistent save
# Rules saved to /etc/iptables/rules.v4

Complete Server Template

Copy-paste this for a fresh server:

# Flush existing rules
iptables -F
iptables -X

# Default policies
# WARNING: On a remote server, add all ACCEPT rules BEFORE setting DROP policy
# Flushing rules then setting DROP will lock you out!
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Allow established connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Allow loopback
iptables -A INPUT -i lo -j ACCEPT

# SSH (rate limited)
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --name SSH
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# HTTP/HTTPS
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Ping (limited)
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT

# Save
iptables-save > /etc/iptables.rules

iptables vs nftables vs UFW

iptables nftables UFW
Complexity Medium Medium Easy
Status Legacy (still works) Replacement for iptables Frontend for iptables
Best for Experienced admins New deployments Quick server setup
Command iptables nft ufw

UFW is just a wrapper around iptables. If you know iptables, you can do everything UFW does and more.

See Also

IPv6 (ip6tables)

All rules in this guide are IPv4 only (iptables). If your server has IPv6 enabled (most do by default), attackers can bypass your entire firewall by connecting over IPv6.

Option 1: Apply matching IPv6 rules

ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP

# Save IPv6 rules
ip6tables-save > /etc/iptables/rules.v6

Option 2: Disable IPv6 entirely

echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf
echo "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.conf
sysctl -p