iptables NAT rules don’t actually do Network Address Translation; they just tell the kernel to consider doing it.

Let’s see this in action. Imagine a simple network setup: your Linux machine is acting as a gateway between a private network (e.g., 192.168.1.0/24) and the internet. You want devices on the private network to access the internet, but they don’t have public IP addresses themselves. This is where NAT comes in, specifically Masquerading, which is a form of Source NAT (SNAT).

Here’s how you’d set up your gateway:

First, you need to enable IP forwarding so the kernel will pass packets between interfaces.

echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

This tells the kernel that it’s allowed to route packets destined for networks other than the one the packet arrived on. Without this, the gateway would just drop packets that aren’t for its directly connected networks.

Now, the core of it: the iptables rules. We’ll use the nat table, which is specifically for altering packet source or destination addresses.

To allow devices on your internal network (192.168.1.0/24) to reach the internet, you need to modify the source IP address of outgoing packets from those internal IPs to the IP address of your gateway’s external interface. This is POSTROUTING.

sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE

Let’s break this down:

  • -t nat: We’re working with the nat table.
  • -A POSTROUTING: We’re appending a rule to the POSTROUTING chain. This chain is processed just before a packet leaves the network namespace.
  • -s 192.168.1.0/24: This rule applies to packets originating from any IP address in the 192.168.1.0/24 subnet.
  • -o eth0: This rule applies to packets that are being sent out of the eth0 interface (assuming eth0 is your external, internet-facing interface).
  • -j MASQUERADE: This is the target. MASQUERADE is a special form of SNAT where the source IP address is automatically set to the IP of the outgoing interface. This is useful because your gateway’s public IP might change (e.g., if you’re on DHCP). If your external IP were static, you’d use -j SNAT --to-source <your_static_ip>.

This rule effectively says: "For any packet coming from my private network that’s about to leave my external interface, change its source IP to my external interface’s IP." When the response comes back to your gateway’s public IP, the kernel remembers which internal machine initiated the connection (via a connection tracking table) and rewrites the destination IP back to the original private IP before forwarding it internally.

PREROUTING is for changing the destination IP address of incoming packets before the kernel decides where to route them. A common use case is Destination NAT (DNAT), often used for port forwarding.

Let’s say you have a web server on your private network at 192.168.1.100 running on port 80, and you want external users to access it by hitting your gateway’s public IP on port 8080.

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

Here’s the breakdown:

  • -t nat: Again, the nat table.
  • -A PREROUTING: Appending to the PREROUTING chain. This chain is processed as soon as a packet arrives on a network interface, before routing decisions are made.
  • -i eth0: This rule applies to packets arriving on the eth0 interface (your external interface).
  • -p tcp --dport 8080: This matches TCP packets destined for port 8080.
  • -j DNAT --to-destination 192.168.1.100:80: This is the target. DNAT changes the destination IP and port. We’re telling it to change the destination to 192.168.1.100 on port 80.

So, when an external user tries to connect to your_gateway_public_ip:8080, the packet arrives at eth0, hits the PREROUTING chain, and iptables rewrites the destination to 192.168.1.100:80. The kernel then routes this modified packet to the internal web server.

The magic happens because iptables is stateful. When the web server sends a response back, it originates from 192.168.1.100:80. The kernel sees this packet is a reply to a connection it tracked during the PREROUTING DNAT operation. It then uses the nat table’s POSTROUTING chain (often with a rule similar to the MASQUERADE one, but it’s the connection tracking that makes it work) to rewrite the source IP back to your gateway’s public IP and the destination port to 8080 before sending it out eth0.

A key detail often missed is that MASQUERADE is dynamic. It automatically uses the IP address of the outgoing interface (-o eth0 in our example). If that IP address changes (e.g., your ISP gives you a new DHCP lease), MASQUERADE adapts without you needing to change the rule. If you were using SNAT with a static IP, you’d need to manually update the rule if your IP ever changed.

The next hurdle you’ll face is ensuring that your firewall rules in the filter table (specifically the FORWARD chain) allow the traffic to be routed between interfaces after NAT has done its job.

Want structured learning?

Take the full Iptables course →