IPTABLES
iptables is a user-space command-line tool used to configure Linux kernel packet filtering through the Netfilter framework. It controls how inbound, outbound, and forwarded traffic is handled by defining rules that match packets and apply actions such as accept, drop, reject, log, or jump to another chain.
iptables is widely used on Linux servers because it is powerful, scriptable, and deeply integrated with the kernel. Many higher-level firewall tools (such as UFW) translate their configurations into underlying iptables rules.
This page focuses on understanding core iptables concepts, reading existing rule sets, and applying safe patterns for common server use cases.
On many modern distributions, nftables is the preferred firewall framework, and iptables commands may be implemented via compatibility layers (for example, iptables-nft). The syntax remains useful for reading legacy systems and understanding how higher-level tooling works.
Where iptables Fits in the Linux Networking Stack
Netfilter is built into the Linux kernel. iptables is one way to configure Netfilter rules.
Core Concepts
Tables
Tables group rules by purpose.
| Table | Purpose | Common Use |
|---|---|---|
| - | -- | |
filter | Packet filtering (allow/deny) | Host firewall rules |
nat | Network Address Translation | Port forwarding, masquerading |
mangle | Packet modification | Advanced routing/QoS |
raw | Connection tracking exemptions | Specialized performance cases |
security | SELinux-related rules | Special security policies |
Most server firewall configurations primarily use the filter table.
Chains
Chains are ordered lists of rules applied at specific points in packet processing.
Common built-in chains (filter table):
| Chain | Traffic Type |
|---|---|
INPUT | Packets destined to the local machine |
OUTPUT | Packets originating from the local machine |
FORWARD | Packets routed through the machine |
nat table common chains:
| Chain | Traffic Type |
|---|---|
| - | - |
PREROUTING | Before routing decision |
POSTROUTING | After routing decision |
OUTPUT | Locally generated packets (NAT) |
Policies, Rules, Matches, Targets
- Policy: default action for a chain if no rule matches (example:
DROP) - Rule: a line that matches packet attributes and applies an action
- Match: condition (source, destination, protocol, port, interface, state)
- Target: action (
ACCEPT,DROP,REJECT,LOG,DNAT,SNAT, jump)
Inspecting Existing Rules (Safe Read-Only Commands)
List rules (filter table)
sudo iptables -L
List rules with numeric output (avoid DNS lookups)
sudo iptables -L -n
Show verbose output with packet/byte counters
sudo iptables -L -n -v
Show exact rule syntax (best for backup/recreate)
sudo iptables -S
View NAT rules
sudo iptables -t nat -L -n -v
sudo iptables -t nat -S
Common Match Types
| Match | Meaning | Example |
|---|---|---|
| -- | ||
-p tcp | TCP protocol | -p tcp |
--dport 22 | Destination port | --dport 22 |
-s 203.0.113.10 | Source IP | -s 203.0.113.10 |
-d 192.0.2.10 | Destination IP | -d 192.0.2.10 |
-i eth0 | Incoming interface | -i eth0 |
-o eth0 | Outgoing interface | -o eth0 |
-m conntrack --ctstate | Connection state | -m conntrack --ctstate ESTABLISHED,RELATED |
Essential Baseline Rules (Typical Server)
These examples are shown for learning and understanding. If you are using UFW, prefer managing policy there to avoid conflicts.
1) Allow established connections
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
2) Allow loopback
sudo iptables -A INPUT -i lo -j ACCEPT
3) Allow SSH (customize port)
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
4) Allow HTTP/HTTPS
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
5) Set default policy (deny inbound)
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
Logging and Reject vs Drop
Drop
- silently discards packets
- reduces feedback to attackers
sudo iptables -A INPUT -p tcp --dport 23 -j DROP
Reject
- discards and replies (ICMP or TCP RST)
- useful for clarity and debugging
sudo iptables -A INPUT -p tcp --dport 23 -j REJECT
Log (with rate limiting)
Logging every packet can overwhelm logs. Use rate limiting:
sudo iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "IPTABLES BLOCK: " --log-level 4
Port Allow List and IP Allow List
Allow SSH only from one IP
sudo iptables -A INPUT -p tcp -s 203.0.113.10 --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j DROP
NAT and Port Forwarding (Common VPS Use)
Enable IP forwarding (temporary)
sudo sysctl -w net.ipv4.ip_forward=1
Persist (example):
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-ipforward.conf
sudo sysctl --system
Masquerade (typical outbound NAT)
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Port forwarding (DNAT) example
Forward public port 443 to an internal host:
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 10.0.0.10:443
sudo iptables -A FORWARD -p tcp -d 10.0.0.10 --dport 443 -j ACCEPT
Persistence (Important)
iptables rules can be lost on reboot unless saved and restored by a persistence mechanism.
Common approaches:
iptables-persistent/netfilter-persistenton Debian/Ubuntu- systemd unit scripts that restore rules at boot
- migration to
nftablesnative ruleset for modern systems
Debian/Ubuntu persistence
Install:
sudo apt update
sudo apt install iptables-persistent -y
Save current rules:
sudo netfilter-persistent save
Reload:
sudo netfilter-persistent reload
Common Pitfalls
| Problem | Cause | Fix |
|---|---|---|
| -- | ||
| Locked out over SSH | Dropped INPUT before allowing SSH | Add SSH allow rule first; use console recovery |
| Rules appear ignored | Another firewall tool is managing rules | Avoid mixing with UFW/firewalld unless you know the interaction |
* timeouts in traceroute | Routers rate-limit ICMP | Validate with TCP traceroute or application tests |
| NAT not working | IP forwarding disabled | Enable net.ipv4.ip_forward=1 |
| Rules lost after reboot | No persistence | Install persistence or migrate to nftables |
Relationship to UFW
UFW is a higher-level interface that writes rules into iptables chains. If you manage firewall policy with UFW, avoid manually editing iptables unless:
- you are auditing (
iptables -S,iptables -L -n -v) - you are troubleshooting low-level issues
- you fully understand the resulting rule interactions
Quick Reference
| Task | Command |
|---|---|
| -- | |
| List rules | iptables -L -n -v |
| Show rule syntax | iptables -S |
| List NAT rules | iptables -t nat -L -n -v |
| Allow SSH | iptables -A INPUT -p tcp --dport 22 -j ACCEPT |
| Set default deny inbound | iptables -P INPUT DROP |
| Save rules (Ubuntu) | netfilter-persistent save |
| Reload rules | netfilter-persistent reload |