mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
fix: correct hairpin NAT rules and bind hairpin check to gateway interface
The POSTROUTING MASQUERADE rules in forward-port failed to handle two hairpin scenarios: 1. Host-to-target hairpin (OUTPUT DNAT): when sip is a WAN IP (tunnel case), the old rule matched `-s sip` but the actual source of locally-originated packets is a local interface IP, not the WAN IP. Fix: use `-m addrtype --src-type LOCAL -m conntrack --ctorigdst sip` to match any local source while tying the rule to the specific sip. 2. Same-subnet self-hairpin (PREROUTING DNAT): when a WireGuard peer connects to itself via the tunnel's public IP, traffic is DNAT'd back to the peer. Without MASQUERADE the response takes a loopback shortcut, bypassing the tunnel server's conntrack and breaking NAT reversal. Fix: add `-s dip/dprefix -d dip` to masquerade same-subnet traffic, which also subsumes the old bridge_subnet rule. Also bind the hairpin detection socket to the gateway interface and local IP for consistency with the echoip client.
This commit is contained in:
@@ -58,15 +58,18 @@ iptables -t nat -A ${NAME}_OUTPUT -d "$sip" -p udp --dport "$sport" -j DNAT --to
|
||||
iptables -A ${NAME}_FORWARD -d $dip -p tcp --dport $dport -m state --state NEW -j ACCEPT
|
||||
iptables -A ${NAME}_FORWARD -d $dip -p udp --dport $dport -m state --state NEW -j ACCEPT
|
||||
|
||||
# NAT hairpin: masquerade traffic from the bridge subnet or host to the DNAT
|
||||
# target, so replies route back through the host for proper NAT reversal.
|
||||
# Container-to-container hairpin (source is on the bridge subnet)
|
||||
if [ -n "$bridge_subnet" ]; then
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -s "$bridge_subnet" -d "$dip" -p tcp --dport "$dport" -j MASQUERADE
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -s "$bridge_subnet" -d "$dip" -p udp --dport "$dport" -j MASQUERADE
|
||||
fi
|
||||
# Host-to-container hairpin (host connects to its own gateway IP, source is sip)
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -s "$sip" -d "$dip" -p tcp --dport "$dport" -j MASQUERADE
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -s "$sip" -d "$dip" -p udp --dport "$dport" -j MASQUERADE
|
||||
# NAT hairpin: masquerade so replies route back through this host for proper
|
||||
# NAT reversal instead of taking a direct path that bypasses conntrack.
|
||||
# Host-to-target hairpin: locally-originated packets whose original destination
|
||||
# was sip (before OUTPUT DNAT rewrote it to dip). Using --ctorigdst ties the
|
||||
# rule to this specific sip, so multiple WAN IPs forwarding the same port to
|
||||
# different targets each get their own masquerade.
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -m addrtype --src-type LOCAL -m conntrack --ctorigdst "$sip" -d "$dip" -p tcp --dport "$dport" -j MASQUERADE
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -m addrtype --src-type LOCAL -m conntrack --ctorigdst "$sip" -d "$dip" -p udp --dport "$dport" -j MASQUERADE
|
||||
# Same-subnet hairpin: when traffic originates from the same subnet as the DNAT
|
||||
# target (e.g. a container reaching another container, or a WireGuard peer
|
||||
# connecting to itself via the tunnel's public IP).
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -s "$dip/$dprefix" -d "$dip" -p tcp --dport "$dport" -j MASQUERADE
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -s "$dip/$dprefix" -d "$dip" -p udp --dport "$dport" -j MASQUERADE
|
||||
|
||||
exit $err
|
||||
|
||||
Reference in New Issue
Block a user