Iptables

IPtables

  • iptables -L [-v]: List all chain, -v for interface and other additional information

  • iptables -L -t nat: List nat table

  • iptables -t nat -A POSTROUTING -s $NetworkofZeroTier -j MASQUERADE: configure masquerade

  • iptables -t nat -A POSTROUTING -o eth0 -s 172.29.0.0/16 -j MASQUERADE: masquerade traffic from 172.29.0.0/16 going out of eth0

Enable NAT for outgoing traffic

  • iptables -A FORWARD -i $PHY_IFACE -o $ZT_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT: allow return traffic

  • iptables -A FORWARD -i $ZT_IFACE -o $PHY_IFACE -j ACCEPT: allow traffic going through router

Delete NAT rule

  • iptables -t nat -L POSTROUTING --line-numbers: list rules with line numbers

  • iptables -t nat -D POSTROUTING $LINE_NUMBER: delete rule with line_number

Make IPtable rules persistent

  • Save iptables and make it persistent: rule will be saved in /etc/iptables/rules.v4 and rules.v6: sudo netfilter-persistent save

  • Installation if required

IPTable FLows

Traffic path from containers to host

  1. Output from container:

  • Starts within the container's network namespace.

  • Hits the container’s own OUTPUT chain first.

  1. Routing to host:

  • The packet exits the container’s namespace and enters the host’s PREROUTING chain (in the nat table first, then mangle, and then filter).

  1. Handled by INPUT or FORWARD?

  • Since the destination IP is the host’s own IP, it goes through the host's INPUT chain, not FORWARD.

  1. So filter or inspect as following:

  • iptables -t filter -L INPUT -v: Where access control decisions happen.

  • iptables -t nat -L PREROUTING -v: If any NAT or redirection is triggered.

  • Optionally raw, mangle, etc., if additional processing is used.

  1. Key point:

  • Even though the traffic originates from a container, it behaves just like a remote machine accessing the host's IP—so the INPUT chain on the host handles it.

Traffic path from host to containers

  1. Host generates the packet

  • The connection originates from your host (e.g., localhost or 127.0.0.1), targeting a port that’s been published by Docker.

  1. Hits the host’s OUTPUT chain

  • Since it’s locally generated, the packet goes through the OUTPUT chain in all relevant tables (e.g., filter, nat, mangle, etc.).

  1. Matches nat table OUTPUT rule

  • If the nat table has:

  • local connections from 127.0.0.1 typically skip this rule because of the !127.0.0.0/8 condition.

  • But if accessing via the host's external IP or hostname, this rule kicks in and redirects the traffic to the appropriate container using DNAT in the DOCKER chain.

  1. Packet is rerouted internally

  • Docker rewrites the destination IP to the container's internal address (e.g., 172.17.0.X), maintaining the port mapping.

  1. Arrives at the container

  • The packet is delivered to the container as if it came from the host's IP, routed through the docker0 bridge.

  1. So in summary:

  • Host → container → OUTPUT chain → NAT translation (if applicable) → routed via docker0 → container.

  • It never touches the INPUT or FORWARD chains, since it’s not considered “incoming” traffic in the same way external traffic is.

Traffic Flow from External → Host IP:Published Port → Docker Container

  1. Packet hits the host’s network interface (e.g., eth0)

  • This is where the journey begins. External client targets your host's public IP on a specific port (e.g., 443).

  1. Passes through nat table → PREROUTING chain

  • Docker injects a rule here like:

  • This matches packets destined for the host and jumps to the DOCKER chain.

  1. DOCKER chain (nat table) applies DNAT

  • Matches exposed ports and rewrites the destination IP and port to the container’s internal address:

  1. Routing decision takes place

  • After DNAT, Linux determines where to send the packet: in this case, the container’s internal IP (172.17.0.3).

  1. Packet enters FORWARD chain (in the filter table)

  • Since it’s not destined for the host anymore (thanks to DNAT), iptables treats it as forwarded traffic.

  1. Allowed by Docker’s default ACCEPT rules

  • Unless you've overridden them, Docker sets permissive FORWARD rules to allow this routing.

  1. Packet delivered via docker0 bridge to container

  • Reaches the container's application listening on the internal port.

  1. Summary

  • Key chains involved: PREROUTING → DOCKER (nat) → FORWARD (filter)

  • The original destination is rewritten (DNAT), and the packet is routed and forwarded to the container.

  • This is how services exposed with -p or --publish work seamlessly from the outside.

Last updated