The classic Linux firewall — a userspace interface to the kernel's netfilter framework for filtering, NATting, and manipulating IP packets on any Linux host.
filter · nat · mangle · raw · IPv4 & IPv6 · stateful inspection
01 — What is iptables?
iptables is a userspace command-line tool that configures the Linux kernel's netfilter packet-filtering framework. Rules are organised into tables → chains → rules. Each incoming, outgoing, or forwarded packet traverses the relevant chains in order; the first matching rule wins.
iptables has been the backbone of Linux firewalling for over two decades — understanding its table–chain–rule model unlocks full control over every packet that crosses your host.
ip6tables is the IPv6 counterpart with an identical syntax. Everything in this document applies to both unless noted.
| Table | Purpose | Built-in chains |
| filter | Allow, drop, or reject packets. The default table. | INPUT, FORWARD, OUTPUT |
| nat | Network Address Translation — SNAT, DNAT, masquerade. | PREROUTING, OUTPUT, POSTROUTING |
| mangle | Modify packet headers (TTL, TOS, mark). | PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING |
| raw | Bypass connection tracking (NOTRACK). Highest priority. | PREROUTING, OUTPUT |
| security | Mandatory access control (SELinux). Rarely touched directly. | INPUT, FORWARD, OUTPUT |
Packet flow — simplified
Incoming packet
│
▼
[raw PREROUTING] → [mangle PREROUTING] → [nat PREROUTING] ← DNAT here
│
├─ Destined for THIS host ──► [mangle INPUT] → [filter INPUT] → local process
│
└─ Destined for ANOTHER host ─► [mangle FORWARD] → [filter FORWARD]
│
[mangle POSTROUTING] → [nat POSTROUTING] → wire
▲
Outgoing packet from local process SNAT / MASQUERADE here
│
▼
[raw OUTPUT] → [mangle OUTPUT] → [nat OUTPUT] → [filter OUTPUT]
│
└──────────────────────────────────────────────► [POSTROUTING] → wire
02 — Installation & Basics
Install
# Debian / Ubuntu
sudo apt install iptables iptables-persistent
# RHEL / Fedora (replace nftables-based firewalld)
sudo dnf install iptables-services
sudo systemctl disable --now firewalld
sudo systemctl enable --now iptables
# Arch
sudo pacman -S iptables
Essential commands
# List rules with line numbers and packet counts
sudo iptables -L -v -n --line-numbers
# List a specific table
sudo iptables -t nat -L -v -n --line-numbers
# Flush ALL rules (dangerous — test first!)
sudo iptables -F
# Flush a specific chain
sudo iptables -F INPUT
# Zero packet/byte counters
sudo iptables -Z
# Save rules (Debian/Ubuntu with iptables-persistent)
sudo netfilter-persistent save
# Save rules (RHEL)
sudo service iptables save
# Restore rules from file
sudo iptables-restore < /etc/iptables/rules.v4
⚠ Always have console / out-of-band access when modifying firewall rules on a remote host. A single wrong DROP rule will lock you out. Use iptables-restore with a timed reset script as a safety net.
03 — Rule Syntax
Every iptables command follows a consistent structure. Understanding each flag makes building rules intuitive.
iptables [-t table] COMMAND chain [matches] -j TARGET
# Examples
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -I INPUT 1 -s 10.0.0.5 -j DROP
iptables -D INPUT -p tcp --dport 80 -j ACCEPT
Commands
| Command | Meaning |
| -A chain | Append rule to end of chain. |
| -I chain [n] | Insert rule at position n (default: 1 = top). |
| -D chain [n|rule] | Delete rule by number or specification. |
| -R chain n | Replace rule at position n. |
| -L [chain] | List rules. |
| -F [chain] | Flush (delete all rules in) chain or table. |
| -P chain TARGET | Set default policy for a built-in chain. |
| -N chain | Create a new user-defined chain. |
| -X [chain] | Delete a user-defined chain. |
| -E old new | Rename a chain. |
| -Z [chain] | Zero packet/byte counters. |
Common matches
| Match | Description | Example |
| -s <ip/cidr> | Source IP or range | -s 192.168.1.0/24 |
| -d <ip/cidr> | Destination IP or range | -d 10.0.0.1 |
| -p <proto> | Protocol: tcp, udp, icmp, all | -p tcp |
| --dport <port> | Destination port (requires -p) | --dport 443 |
| --sport <port> | Source port | --sport 1024:65535 |
| -i <iface> | Incoming interface | -i eth0 |
| -o <iface> | Outgoing interface | -o eth1 |
| -m state --state | Connection tracking state | -m state --state ESTABLISHED,RELATED |
| -m conntrack --ctstate | Modern conntrack (preferred) | -m conntrack --ctstate NEW,ESTABLISHED |
| -m multiport --dports | Multiple destination ports | -m multiport --dports 80,443,8080 |
| -m iprange --src-range | IP range (not CIDR) | --src-range 10.0.0.1-10.0.0.50 |
| -m limit --limit | Rate limit matches | -m limit --limit 5/min |
| -m recent | Recent IP tracking (rate limiting) | -m recent --name SSH --update |
| -m string --string | Match payload string | --string "attack" --algo bm |
| -m mac --mac-source | Match source MAC address | --mac-source AA:BB:CC:DD:EE:FF |
Targets
| Target | Effect |
| ACCEPT | Allow the packet through. |
| DROP | Silently discard the packet. Sender gets no response. |
| REJECT | Discard and send an ICMP error back to the sender. |
| LOG | Log the packet to the kernel log (dmesg / syslog) and continue. |
| RETURN | Stop traversing the current chain; return to the calling chain. |
| MASQUERADE | SNAT for dynamic IPs (nat table, POSTROUTING). |
| SNAT | Rewrite source IP to a fixed address (nat POSTROUTING). |
| DNAT | Rewrite destination IP / port (nat PREROUTING). |
| REDIRECT | Redirect to a local port (nat PREROUTING). |
| MARK | Set packet mark for routing / QoS (mangle table). |
| custom chain | Jump to a user-defined chain. |
04 — Stateful Firewall (the Standard Template)
A stateful firewall tracks connection state so you only need to explicitly allow new inbound connections; return traffic for established sessions is permitted automatically. This is the foundation of almost every Linux server firewall.
Standard server ruleset — apply in order
#!/bin/bash
# ── FLUSH existing rules ──────────────────────────────────────────────
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
# ── DEFAULT POLICIES ──────────────────────────────────────────────────
# Drop everything, then selectively allow
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT # outbound unrestricted (tighten as needed)
# ── LOOPBACK ──────────────────────────────────────────────────────────
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# ── ESTABLISHED / RELATED ─────────────────────────────────────────────
# Accept return traffic for connections we initiated or allowed in
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# ── ICMP ──────────────────────────────────────────────────────────────
# Allow ping (rate-limited to mitigate flood)
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 5 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
# ── SSH ───────────────────────────────────────────────────────────────
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
# ── HTTP / HTTPS ──────────────────────────────────────────────────────
iptables -A INPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate NEW -j ACCEPT
# ── LOG & DROP everything else ────────────────────────────────────────
iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables-INPUT-DROP: " --log-level 4
iptables -A INPUT -j DROP
iptables -A FORWARD -j DROP
Save the ruleset
# Debian / Ubuntu
sudo netfilter-persistent save # writes /etc/iptables/rules.v4 and rules.v6
# RHEL / CentOS
sudo service iptables save # writes /etc/sysconfig/iptables
# Manual
sudo iptables-save > /etc/iptables/rules.v4
05 — Common Service Rules
SSH
# Allow SSH from anywhere (simplest)
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
# Allow SSH from a specific IP or subnet only
iptables -A INPUT -p tcp --dport 22 -s 203.0.113.0/24 -m conntrack --ctstate NEW -j ACCEPT
# Rate-limit SSH to 3 new connections per minute (brute-force mitigation)
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 -m conntrack --ctstate NEW -j ACCEPT
# Custom SSH port
iptables -A INPUT -p tcp --dport 2222 -m conntrack --ctstate NEW -j ACCEPT
HTTP & HTTPS
# Allow HTTP and HTTPS
iptables -A INPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate NEW -j ACCEPT
# Allow only HTTPS, block plain HTTP
iptables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j REJECT --reject-with tcp-reset
# Rate-limit HTTP new connections (basic DDoS mitigation)
iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW \
-m limit --limit 50/s --limit-burst 200 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -j DROP
FTP (active & passive)
# Load the conntrack FTP helper (required for stateful FTP)
sudo modprobe nf_conntrack_ftp
# FTP control channel
iptables -A INPUT -p tcp --dport 21 -m conntrack --ctstate NEW -j ACCEPT
# Active FTP — server connects back on port 20
iptables -A INPUT -p tcp --dport 20 -m conntrack --ctstate ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp --sport 20 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Passive FTP — server opens high port; conntrack tracks it automatically
# (requires nf_conntrack_ftp module loaded above)
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# FTPS (FTP over TLS explicit)
iptables -A INPUT -p tcp --dport 990 -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -p tcp --dport 989 -m conntrack --ctstate NEW -j ACCEPT
SMTP / Mail
# SMTP (plain — outbound relay, often blocked by ISPs)
iptables -A INPUT -p tcp --dport 25 -m conntrack --ctstate NEW -j ACCEPT
# Submission (authenticated SMTP — preferred for clients)
iptables -A INPUT -p tcp --dport 587 -m conntrack --ctstate NEW -j ACCEPT
# SMTPS (SMTP over TLS)
iptables -A INPUT -p tcp --dport 465 -m conntrack --ctstate NEW -j ACCEPT
IMAP & POP3
# IMAP
iptables -A INPUT -p tcp --dport 143 -m conntrack --ctstate NEW -j ACCEPT
# IMAPS (IMAP over TLS)
iptables -A INPUT -p tcp --dport 993 -m conntrack --ctstate NEW -j ACCEPT
# POP3
iptables -A INPUT -p tcp --dport 110 -m conntrack --ctstate NEW -j ACCEPT
# POP3S (POP3 over TLS)
iptables -A INPUT -p tcp --dport 995 -m conntrack --ctstate NEW -j ACCEPT
DNS
# DNS server (accepts queries from clients)
iptables -A INPUT -p udp --dport 53 -j ACCEPT
iptables -A INPUT -p tcp --dport 53 -j ACCEPT # TCP for zone transfers / large responses
# Allow outbound DNS queries from this host
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT
MySQL / MariaDB
# Allow MySQL from a specific app server only
iptables -A INPUT -p tcp --dport 3306 -s 10.0.0.20 -m conntrack --ctstate NEW -j ACCEPT
# Block MySQL from everywhere else
iptables -A INPUT -p tcp --dport 3306 -j DROP
PostgreSQL
# Allow PostgreSQL from app subnet only
iptables -A INPUT -p tcp --dport 5432 -s 10.0.0.0/24 -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -p tcp --dport 5432 -j DROP
Redis
# Redis — bind to localhost only is safer; if exposed:
iptables -A INPUT -p tcp --dport 6379 -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 6379 -j DROP
NTP
# Allow outbound NTP queries
iptables -A OUTPUT -p udp --dport 123 -j ACCEPT
iptables -A INPUT -p udp --sport 123 -m conntrack --ctstate ESTABLISHED -j ACCEPT
# NTP server — accept client queries
iptables -A INPUT -p udp --dport 123 -j ACCEPT
06 — NAT & Routing
NAT rules live in the nat table. Common uses: masquerading for internet sharing, port forwarding (DNAT), and load balancing. You must first enable IP forwarding in the kernel.
Enable IP forwarding
# Temporary (lost on reboot)
echo 1 > /proc/sys/net/ipv4/ip_forward
# Permanent — add to /etc/sysctl.conf or /etc/sysctl.d/99-forward.conf
net.ipv4.ip_forward = 1
# Apply immediately
sysctl -p
Masquerade / Internet sharing (SNAT with dynamic IP)
# All traffic leaving eth0 gets the public IP of eth0 (MASQUERADE)
# eth1 is the LAN-facing interface
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# Allow forwarding between interfaces
iptables -A FORWARD -i eth1 -o eth0 -m conntrack --ctstate NEW -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
SNAT — fixed source IP
# Rewrite source to a fixed public IP (better than MASQUERADE on static IPs)
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 203.0.113.5
DNAT — port forwarding
# Forward inbound TCP:80 to an internal web server
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 \
-j DNAT --to-destination 192.168.1.10:80
# Allow the forwarded traffic through the FORWARD chain
iptables -A FORWARD -p tcp -d 192.168.1.10 --dport 80 \
-m conntrack --ctstate NEW -j ACCEPT
# Forward a non-standard external port to a different internal port
# External :8443 → internal 192.168.1.20:443
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8443 \
-j DNAT --to-destination 192.168.1.20:443
Redirect (transparent proxy / local port redirect)
# Redirect all outbound HTTP to a local Squid proxy on port 3128
iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-ports 3128
# PREROUTING redirect for traffic from other hosts
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3128
07 — Rate Limiting & DDoS Mitigation
iptables can slow brute-force attacks and connection floods using the limit, recent, and hashlimit modules — without external tools.
Limit new TCP connections per source IP (hashlimit)
# Allow each source IP no more than 10 new SSH connections per minute
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
-m hashlimit \
--hashlimit-name ssh \
--hashlimit-above 10/minute \
--hashlimit-mode srcip \
--hashlimit-burst 5 \
-j DROP
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
SYN flood protection
# Drop excessive SYN packets
iptables -A INPUT -p tcp --syn -m limit --limit 1/s --limit-burst 3 -j ACCEPT
iptables -A INPUT -p tcp --syn -j DROP
# Enable kernel-level SYN cookies (persistent)
echo "net.ipv4.tcp_syncookies = 1" >> /etc/sysctl.d/99-hardening.conf
sysctl -p /etc/sysctl.d/99-hardening.conf
Port scan / stealth scan detection
# XMAS scan
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP
# NULL scan
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
# FIN scan
iptables -A INPUT -p tcp --tcp-flags ALL FIN -j DROP
# Invalid state packets
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
ICMP flood protection
# Allow ping but cap rate
iptables -A INPUT -p icmp --icmp-type echo-request \
-m limit --limit 1/s --limit-burst 5 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j DROP
Block specific countries with ipset (CIDR list)
# Install ipset
sudo apt install ipset
# Create a set and populate it
sudo ipset create blocklist hash:net
sudo ipset add blocklist 203.0.113.0/24
sudo ipset add blocklist 198.51.100.0/24
# Use the set in iptables
iptables -A INPUT -m set --match-set blocklist src -j DROP
# Save ipset rules
sudo ipset save > /etc/ipset.rules
# Restore on boot (add to /etc/rc.local or a systemd unit)
sudo ipset restore < /etc/ipset.rules
08 — Logging
The LOG target writes matched packets to the kernel log without affecting packet flow. Always put LOG rules before the DROP/ACCEPT rule for the same traffic.
Basic logging
# Log dropped INPUT packets (rate-limited to avoid log flooding)
iptables -A INPUT -m limit --limit 5/min --limit-burst 10 \
-j LOG --log-prefix "iptables-DROP: " --log-level 4
# Log new SSH connections
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
-j LOG --log-prefix "SSH-NEW: " --log-level 6
# Then allow
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
View log output
# Via dmesg
dmesg | grep iptables
# Via syslog
grep "iptables" /var/log/syslog
grep "iptables" /var/log/kern.log
# Via journald
journalctl -k | grep iptables
Log to a dedicated file (rsyslog)
# /etc/rsyslog.d/10-iptables.conf
:msg, contains, "iptables-" /var/log/iptables.log
& stop
09 — User-Defined Chains
User-defined chains let you group related rules, reduce repetition, and build modular rulesets. They act like subroutines — you jump to them from a built-in chain and they return when no rule matches.
Example — shared SSH rate-limit chain
# Create the chain
iptables -N RATE-LIMIT-SSH
# Populate it
iptables -A RATE-LIMIT-SSH -m recent --set --name SSHTRACK
iptables -A RATE-LIMIT-SSH -m recent --update --seconds 60 \
--hitcount 4 --name SSHTRACK \
-j LOG --log-prefix "SSH-RATELIMIT: "
iptables -A RATE-LIMIT-SSH -m recent --update --seconds 60 \
--hitcount 4 --name SSHTRACK -j DROP
iptables -A RATE-LIMIT-SSH -j ACCEPT # passes if not rate-limited
# Jump to it from INPUT
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
-j RATE-LIMIT-SSH
Example — shared blocklist chain
# Create
iptables -N BLOCKLIST
# Add known bad IPs
iptables -A BLOCKLIST -s 203.0.113.0/24 -j DROP
iptables -A BLOCKLIST -s 198.51.100.50 -j DROP
iptables -A BLOCKLIST -j RETURN # return to caller if no match
# Apply early in INPUT
iptables -I INPUT 1 -j BLOCKLIST
10 — Persisting Rules
iptables rules are in-memory and are lost on reboot. There are several approaches to persist them across restarts.
iptables-persistent (Debian / Ubuntu)
# Install
sudo apt install iptables-persistent
# Save current rules (IPv4 and IPv6)
sudo netfilter-persistent save
# Saves to: /etc/iptables/rules.v4 and /etc/iptables/rules.v6
# Reload from saved files
sudo netfilter-persistent reload
iptables-services (RHEL / CentOS)
sudo dnf install iptables-services
sudo systemctl enable --now iptables
# Save current rules
sudo service iptables save
# Saves to: /etc/sysconfig/iptables
Systemd unit (distro-agnostic)
# Save rules to a file
sudo iptables-save > /etc/iptables/rules.v4
sudo ip6tables-save > /etc/iptables/rules.v6
# /etc/systemd/system/iptables-restore.service
[Unit]
Description=Restore iptables rules
Before=network-pre.target
Wants=network-pre.target
[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables/rules.v4
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now iptables-restore
11 — IPv6 (ip6tables)
ip6tables has an identical syntax to iptables. Maintain separate but parallel rulesets for IPv4 and IPv6 — gaps in the IPv6 rules are a common source of firewall bypasses.
Minimal IPv6 stateful firewall
#!/bin/bash
ip6tables -F; ip6tables -X
# Default policies
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT ACCEPT
# Loopback
ip6tables -A INPUT -i lo -j ACCEPT
# Established / Related
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# ICMPv6 — required for IPv6 to function correctly (NDP, path MTU, etc.)
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp -j ACCEPT
# SSH
ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
# HTTP / HTTPS
ip6tables -A INPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate NEW -j ACCEPT
⚠ Never block ICMPv6 entirely. Neighbour Discovery Protocol (NDP) — the IPv6 equivalent of ARP — depends on ICMPv6 types 133–137. Blocking all ICMPv6 breaks IPv6 connectivity completely.
12 — Troubleshooting
| Symptom | Cause & Fix |
| Rules lost on reboot | Not persisted — run netfilter-persistent save or service iptables save. |
| Locked out of SSH | Use out-of-band console. Add your IP to an ACCEPT rule before setting DROP policy. |
| Rule added but traffic still blocked | Check rule order with iptables -L -n --line-numbers. An earlier DROP rule may match first. |
| NAT not working | IP forwarding disabled — sysctl net.ipv4.ip_forward must be 1. |
| FTP passive mode broken | nf_conntrack_ftp module not loaded — modprobe nf_conntrack_ftp. |
| IPv6 traffic not filtered | ip6tables rules are separate — iptables rules do not apply to IPv6. |
| Counters not incrementing | Traffic may be bypassing the host (switched locally) or hitting a different interface name. |
| REJECT vs DROP confusion | DROP is silent (better for public); REJECT sends ICMP error (faster feedback for LAN). |
Diagnostic commands
# List all rules with packet/byte counters
iptables -L -v -n --line-numbers
# Trace a packet through the ruleset (requires iptables-extensions)
modprobe nf_log_ipv4
iptables -t raw -A PREROUTING -p tcp --dport 22 -j TRACE
iptables -t raw -A OUTPUT -p tcp --dport 22 -j TRACE
# Then watch: journalctl -k | grep TRACE
# Check connection tracking table
cat /proc/net/nf_conntrack
conntrack -L # (requires conntrack-tools)
# Count active rules per chain
iptables -L INPUT --line-numbers | wc -l
13 — Quick Reference
| Command | What it does |
| iptables -L -v -n --line-numbers | List all filter rules with counters and line numbers |
| iptables -t nat -L -v -n | List NAT table rules |
| iptables -F | Flush all filter rules (dangerous — resets to policy) |
| iptables -P INPUT DROP | Set default INPUT policy to DROP |
| iptables -A INPUT -p tcp --dport 22 -j ACCEPT | Allow inbound SSH |
| iptables -I INPUT 1 -s <IP> -j DROP | Block a specific IP immediately (top of chain) |
| iptables -D INPUT <n> | Delete rule at line number n |
| iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE | Enable NAT masquerade on eth0 |
| iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to 192.168.1.10 | Forward port 80 to internal host |
| netfilter-persistent save | Persist rules across reboots (Debian/Ubuntu) |
| iptables-save > rules.v4 | Export current ruleset to file |
| iptables-restore < rules.v4 | Import ruleset from file |
ℹ Consider migrating to nftables for new deployments — it unifies IPv4/IPv6 into a single ruleset, has a cleaner syntax, and is the long-term replacement for iptables on Linux.