11.2 Writing iptables Rules and NAT
Key Takeaways
- Rules are managed with -A (append), -I (insert), -D (delete), and matched with options like -p, --dport, -s, -d, and -i.
- DROP silently discards a packet; REJECT discards it but returns an ICMP error (or TCP RST with --reject-with tcp-reset).
- Stateful firewalls use `-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT` to permit return traffic without opening ports inbound.
- MASQUERADE is source NAT for dynamic/outbound interfaces; SNAT with --to-source is preferred on static addresses.
- Rules are lost at reboot unless saved: iptables-save/iptables-restore plus the netfilter-persistent service (iptables-persistent package) make them survive.
11.2 Writing iptables Rules and NAT
Section 11.1 gave you the framework; now you write the rules. Every iptables command follows the same grammar: table, chain operation, match criteria, and a target (the -j jump action). Because the KLCP exam is closed-book, you should be able to read a rule and state exactly what it does without documentation.
Rule Management Syntax
Chain operations you must know cold:
iptables -A INPUT ...— append a rule to the end of the chainiptables -I INPUT 3 ...— insert at position 3 (defaults to position 1, the top)iptables -D INPUT 3— delete rule number 3 (or repeat the full rule specification)iptables -R INPUT 3 ...— replace a rule in placeiptables -F INPUT— flush (delete) all rules in the chain; the policy staysiptables -L -v -n --line-numbers— list rules with counters, numeric output, and rule numbersiptables -P INPUT DROP— set the chain policy
Match criteria narrow which packets a rule sees: -p tcp or -p udp selects the protocol, --dport 22 and --sport select ports, -s 10.0.0.0/8 and -d match source/destination addresses, and -i eth0 / -o eth0 match the incoming/outgoing interface. Negate any match by placing ! before the option, as in ! -s 192.168.1.0/24 — the old intrapositioned form -s ! 192.168.1.0/24 is rejected by modern iptables.
A Worked Host Firewall
Here is a minimal, correct inbound ruleset for a Kali box that should only accept SSH from one management subnet:
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 22 -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -j LOG --log-prefix 'IPTABLES-DROP: '
Read it top to bottom: loopback is always allowed; return traffic for connections this host initiated is allowed by the stateful rule (so your outbound updates and tool traffic keep working despite the DROP policy); new SSH attempts from 192.168.1.0/24 are allowed; everything else falls off the end of the chain and hits the DROP policy — after being logged by the non-terminating LOG rule.
DROP vs REJECT
Both targets discard the packet. The difference is what the sender learns:
| Target | Sender sees | Typical use |
|---|---|---|
| DROP | Nothing — connections hang until timeout | Internet-facing interfaces; slows port scans and reveals less |
| REJECT | ICMP port-unreachable (customizable with --reject-with tcp-reset for TCP) | Internal networks where fast, clean failure helps diagnostics |
The exam trap is assuming REJECT is 'more secure.' It isn't inherently — REJECT confirms a live host is there, while DROP makes the host nearly indistinguishable from a dead IP. A common compromise is REJECT on trusted internal interfaces and DROP facing outward.
Stateful Matching with conntrack
The conntrack module tracks connection state in the kernel. Its key states: NEW (first packet of a flow), ESTABLISHED (part of a flow already seen in both directions), RELATED (a new flow tied to an existing one — classic example: FTP data connections), and INVALID (unidentifiable packets, usually dropped). The single most important firewall line on any Linux host is:
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
Without it, a default-DROP INPUT policy would break your own outbound connections, because the replies would be dropped on the way back in. Note the older syntax -m state --state ... still appears in old documentation; conntrack is the modern form.
NAT: MASQUERADE vs SNAT
Network Address Translation lives in the nat table. For source NAT — sharing one public address with an internal network, such as routing a victim VM lab through your Kali box — you rewrite the source address in POSTROUTING:
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Two prerequisites people forget: IP forwarding must be enabled (net.ipv4.ip_forward=1, made permanent in /etc/sysctl.conf), and the FORWARD chain must permit the traffic.
- MASQUERADE rewrites the source to whatever address the outbound interface currently has. It is designed for dynamic addresses (DHCP, dial-up): if the link drops, related conntrack entries are discarded so stale NAT state doesn't linger. That per-packet address lookup costs a little overhead.
- SNAT with
--to-source 203.0.113.10rewrites the source to a fixed address. Use it when the interface has a static IP — it is slightly cheaper and the only correct choice for a stable mapping.iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 203.0.113.10.
Remember the direction: SNAT/MASQUERADE happen in POSTROUTING; destination NAT (DNAT, port forwarding) happens in PREROUTING.
Saving and Restoring Rules
iptables rules live only in kernel memory — reboot and they're gone. The persistence toolchain:
iptables-save > /etc/iptables/rules.v4— dump the live ruleset in restore format.iptables-restore < /etc/iptables/rules.v4— load it back atomically.- Install the iptables-persistent package — the iptables/ip6tables plugin for the separate netfilter-persistent package, which it pulls in as a dependency. The netfilter-persistent service loads
rules.v4(andrules.v6) at boot. After changing rules, runnetfilter-persistent saveto update the files.
Kali Linux Revealed teaches a fourth, ifupdown-based variant you should recognize on the exam: put your iptables calls in a script (the book's example is /usr/local/etc/arrakis.fw) and register it with an up /usr/local/etc/arrakis.fw directive inside the interface stanza of /etc/network/interfaces, so the rules are installed every time that interface comes up. A box running NetworkManager or systemd-networkd instead of ifupdown uses their dispatcher hooks rather than interfaces.
Always test a new ruleset before saving it — a mistaken -A INPUT -j DROP at the top of the chain, persisted across reboots, is a self-inflicted lockout on a remote box.
One more operational note: iptables-restore applies a file atomically, replacing the current ruleset, which makes it far safer than pasting individual iptables commands into a remote shell. If you are tuning firewall rules over SSH, load the candidate ruleset with a timed fallback (for example, schedule iptables-restore of the previous rules via at a few minutes out, then cancel the job once you confirm you still have connectivity). The exam won't test the at trick, but it will test that you know rules are memory-resident until explicitly saved, and that flushing rules with iptables -F does not reset chain policies — a flushed chain with a DROP policy still drops everything.
Which iptables rule allows response traffic for connections that your Kali machine initiated, while keeping a default DROP policy on INPUT?
Your Kali box shares its DHCP-assigned uplink with an internal lab network. Which NAT configuration is most appropriate?