# iptables — Manage the Linux Firewall with netfilter

> Practical guide to iptables — controlling the Linux firewall via netfilter, with chains, tables, targets, NAT, connection tracking and persistent rules.

Source: https://www.jpkc.com/db/en/cheatsheets/security/iptables/

<!-- PROSE:intro -->
iptables is the classic front-end for the netfilter firewall in the Linux kernel: a single command lets you filter packets, build NAT rules or lock down open ports. You organise rules in tables (filter, nat, mangle, raw) and chains (INPUT, OUTPUT, FORWARD) that every packet traverses in order until a target such as ACCEPT, DROP or REJECT applies. On modern systems nftables is gradually replacing the tool – but iptables-nft keeps the familiar syntax alive as a compatibility layer. This guide walks you through the options you reach for daily, from listing rules to a stateful firewall with connection tracking.
<!-- PROSE:intro:end -->

## Listing Rules

`iptables -L` — List all rules in the filter table.

```bash
sudo iptables -L
```

`iptables -L -n` — List rules with numeric addresses and ports (no DNS lookups).

```bash
sudo iptables -L -n
```

`iptables -L -v` — List rules with packet/byte counters.

```bash
sudo iptables -L -v
```

`iptables -L -n -v --line-numbers` — List rules with line numbers, numeric, and verbose. Most useful format.

```bash
sudo iptables -L -n -v --line-numbers
```

`iptables -L CHAIN` — List rules in a specific chain (INPUT, OUTPUT, FORWARD).

```bash
sudo iptables -L INPUT -n
```

`iptables -t TABLE -L` — List rules in a specific table (filter, nat, mangle, raw).

```bash
sudo iptables -t nat -L -n
```

`iptables -S` — List rules in iptables-save format (easier to read and re-apply).

```bash
sudo iptables -S
```

`iptables -S CHAIN` — List rules for a specific chain in save format.

```bash
sudo iptables -S INPUT
```

## Basic Rule Management

`iptables -A CHAIN RULE` — Append a rule to the end of a chain.

```bash
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
```

`iptables -I CHAIN RULE` — Insert a rule at the beginning of a chain.

```bash
sudo iptables -I INPUT -p tcp --dport 443 -j ACCEPT
```

`iptables -I CHAIN NUM RULE` — Insert a rule at a specific position (line number).

```bash
sudo iptables -I INPUT 3 -p tcp --dport 8080 -j ACCEPT
```

`iptables -D CHAIN RULE` — Delete a rule by specification.

```bash
sudo iptables -D INPUT -p tcp --dport 80 -j ACCEPT
```

`iptables -D CHAIN NUM` — Delete a rule by line number.

```bash
sudo iptables -D INPUT 3
```

`iptables -R CHAIN NUM RULE` — Replace a rule at a specific line number.

```bash
sudo iptables -R INPUT 2 -p tcp --dport 8443 -j ACCEPT
```

## Chain Management

`iptables -P CHAIN TARGET` — Set the default policy for a built-in chain (ACCEPT or DROP). Caution: `-P INPUT DROP` over SSH locks you out unless an ACCEPT rule for your connection already exists – add it first.

```bash
sudo iptables -P INPUT DROP
```

`iptables -F` — Flush (delete) all rules in all chains. If the policy is set to DROP, a flush can lock out a remote/SSH session – ideally run it locally at the console.

```bash
sudo iptables -F
```

`iptables -F CHAIN` — Flush all rules in a specific chain.

```bash
sudo iptables -F INPUT
```

`iptables -Z` — Zero all packet and byte counters.

```bash
sudo iptables -Z
```

`iptables -N CHAIN` — Create a new custom chain.

```bash
sudo iptables -N MY_CHAIN
```

`iptables -X CHAIN` — Delete a custom chain (must be empty and unreferenced).

```bash
sudo iptables -X MY_CHAIN
```

`iptables -E OLD NEW` — Rename a custom chain.

```bash
sudo iptables -E MY_CHAIN NEW_CHAIN
```

## Match Criteria

`-p PROTOCOL` — Match by protocol: tcp, udp, icmp, all.

```bash
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
```

`-s SOURCE/MASK` — Match by source IP or network.

```bash
sudo iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT
```

`-d DEST/MASK` — Match by destination IP or network.

```bash
sudo iptables -A OUTPUT -d 10.0.0.0/8 -j DROP
```

`--dport PORT` — Match by destination port (requires -p tcp or -p udp).

```bash
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
```

`--sport PORT` — Match by source port.

```bash
sudo iptables -A INPUT -p tcp --sport 1024:65535 -j ACCEPT
```

`--dport PORT1:PORT2` — Match a range of destination ports.

```bash
sudo iptables -A INPUT -p tcp --dport 8000:9000 -j ACCEPT
```

`-i INTERFACE` — Match incoming interface (for INPUT and FORWARD).

```bash
sudo iptables -A INPUT -i eth0 -j ACCEPT
```

`-o INTERFACE` — Match outgoing interface (for OUTPUT and FORWARD).

```bash
sudo iptables -A OUTPUT -o lo -j ACCEPT
```

`! -s SOURCE` — Negate a match (NOT).

```bash
sudo iptables -A INPUT ! -s 192.168.1.0/24 -j DROP
```

## Targets (Actions)

`-j ACCEPT` — Allow the packet.

`-j DROP` — Silently discard the packet (no response).

`-j REJECT` — Reject the packet and send an ICMP error.

`-j REJECT --reject-with TYPE` — Reject with a specific ICMP type: icmp-port-unreachable, tcp-reset, etc.

```bash
sudo iptables -A INPUT -p tcp --dport 23 -j REJECT --reject-with tcp-reset
```

`-j LOG` — Log the packet to syslog/journal, then continue processing.

```bash
sudo iptables -A INPUT -j LOG --log-prefix 'IPT-DROP: '
```

`-j LOG --log-prefix 'PREFIX'` — Log with a custom prefix for easy identification in logs.

```bash
sudo iptables -A INPUT -p tcp --dport 22 -j LOG --log-prefix 'SSH: '
```

`-j RETURN` — Return from the current chain to the calling chain.

## Connection Tracking (Stateful)

`-m conntrack --ctstate STATE` — Match by connection state: NEW, ESTABLISHED, RELATED, INVALID.

```bash
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
```

`-m conntrack --ctstate NEW` — Match new connections only.

```bash
sudo iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT
```

`-m conntrack --ctstate INVALID` — Match packets that don't belong to any known connection.

```bash
sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
```

## Extended Modules

`-m multiport --dports P1,P2,P3` — Match multiple ports in a single rule.

```bash
sudo iptables -A INPUT -p tcp -m multiport --dports 80,443,8080 -j ACCEPT
```

`-m iprange --src-range IP1-IP2` — Match a range of source IPs.

```bash
sudo iptables -A INPUT -m iprange --src-range 192.168.1.100-192.168.1.200 -j ACCEPT
```

`-m limit --limit RATE` — Rate limit matches (e.g., 5/min, 1/sec).

```bash
sudo iptables -A INPUT -p icmp -m limit --limit 1/sec -j ACCEPT
```

`-m limit --limit-burst N` — Set the burst limit for rate limiting.

```bash
sudo iptables -A INPUT -p icmp -m limit --limit 1/sec --limit-burst 4 -j ACCEPT
```

`-m recent --name LIST --set` — Add source IP to a named tracking list.

```bash
sudo iptables -A INPUT -p tcp --dport 22 -m recent --name ssh --set
```

`-m recent --name LIST --rcheck --seconds S --hitcount N` — Check if source IP hit N times in S seconds.

```bash
sudo iptables -A INPUT -p tcp --dport 22 -m recent --name ssh --rcheck --seconds 60 --hitcount 4 -j DROP
```

`-m comment --comment 'TEXT'` — Add a comment to a rule for documentation.

```bash
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT -m comment --comment 'Allow HTTP'
```

## NAT Table

`iptables -t nat -A POSTROUTING -o IFACE -j MASQUERADE` — Enable NAT/masquerading for internet sharing.

```bash
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
```

`iptables -t nat -A PREROUTING -p tcp --dport PORT -j DNAT --to-destination IP:PORT` — Port forwarding: redirect incoming traffic to another host/port.

```bash
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80
```

`iptables -t nat -A PREROUTING -p tcp --dport PORT -j REDIRECT --to-port PORT` — Redirect traffic to a different local port.

```bash
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
```

`iptables -t nat -A POSTROUTING -s NETWORK -j SNAT --to-source IP` — Source NAT: change source IP for outgoing packets.

```bash
sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -j SNAT --to-source 203.0.113.1
```

## Save & Restore

`iptables-save` — Dump all current rules in a restorable format. Rules are not persistent – without this step (or netfilter-persistent) they are lost on reboot.

```bash
sudo iptables-save > /etc/iptables/rules.v4
```

`iptables-restore < FILE` — Restore rules from a saved file.

```bash
sudo iptables-restore < /etc/iptables/rules.v4
```

`iptables-save -t TABLE` — Save rules from a specific table only.

```bash
sudo iptables-save -t filter
```

`iptables-save -c` — Save rules including packet/byte counters.

```bash
sudo iptables-save -c > rules-with-counters.v4
```

## Common Firewall Patterns

`iptables -A INPUT -i lo -j ACCEPT` — Allow all loopback traffic (essential).

```bash
sudo iptables -A INPUT -i lo -j ACCEPT
```

`iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT` — Allow established and related connections (stateful firewall base rule).

```bash
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
```

`iptables -A INPUT -p tcp -m multiport --dports 22,80,443 -j ACCEPT` — Allow SSH, HTTP, and HTTPS.

```bash
sudo iptables -A INPUT -p tcp -m multiport --dports 22,80,443 -j ACCEPT
```

`iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT` — Allow ping requests.

```bash
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
```

`iptables -P INPUT DROP` — Set default policy to DROP (deny all not explicitly allowed). Only set this after the ACCEPT rules above, otherwise you lock yourself out over SSH; then persist the rules with `iptables-save`.

```bash
sudo iptables -P INPUT DROP
```

<!-- PROSE:outro -->
## Conclusion

iptables looks daunting at first because of its sheer number of options, yet it follows a clear logic: packets traverse chains until a target decides their fate. Once you understand rule order and how connection tracking interacts with the default policy, you can build robust firewalls. Remember to persist your rules with `iptables-save` or `netfilter-persistent` – otherwise they vanish on the next reboot. And whenever possible, test a default DROP policy locally or over a second session before you lock yourself out.

## Further Reading

- [iptables(8) – manual page](https://man7.org/linux/man-pages/man8/iptables.8.html) – the complete option reference
- [iptables – Arch Wiki](https://wiki.archlinux.org/title/Iptables) – in-depth practical guide
- [iptables – Wikipedia](https://en.wikipedia.org/wiki/Iptables) – background and history
<!-- PROSE:outro:end -->

## Related Commands

- [age](https://www.jpkc.com/db/en/cheatsheets/security/age/) – modern file encryption with simple keys
- [clamav](https://www.jpkc.com/db/en/cheatsheets/security/clamav/) – open-source virus scanner for files and mail
- [fail2ban](https://www.jpkc.com/db/en/cheatsheets/security/fail2ban/) – automatically bans attackers after failed login attempts

