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
Output from container:
Starts within the container's network namespace.
Hits the container’s own OUTPUT chain first.
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).
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.
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.
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
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.
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.).
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.
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.
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.
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
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).
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.
DOCKER chain (nat table) applies DNAT
Matches exposed ports and rewrites the destination IP and port to the container’s internal address:
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).
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.
Allowed by Docker’s default ACCEPT rules
Unless you've overridden them, Docker sets permissive FORWARD rules to allow this routing.
Packet delivered via docker0 bridge to container
Reaches the container's application listening on the internal port.
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