There are few issues with your attempt:
localhost means connections are initiated from the host, not routed from an other host. Thus it requires the
OUTPUT chain, not the
PREROUTING chain since routing was not initially involved.
- the source IP, which would be
127.0.0.1 has to be changed too once the destination is changed. This requires an additional
POSTROUTING rule. An optional
-m conntrack --ctstate DNAT match will check that the first
OUTPUT did match first and it's not some spoofing attempt.
- At least on Linux
127.0.0.0/8 is handled specially in the routing stack, and the packet will be dropped before the
POSTROUTING rule triggers. To remove the special handling and allow routing "outside" of the
127.0.0.0/8 block, the interface on which the redirection will happen must have the flag
route_localnet set to 1, or none of the above will be enough.
Since the interface is not known from the Question, the interface name is retrieved from the route and put in a variable. Adapt it if needed. You can also use the
all pseudo-interface instead to set the flag globally.
So in the end this boils down to (as root or using
iptables -t nat -A OUTPUT -p tcp --dport 9099 -j DNAT --to-destination 172.17.0.2:8080
iptables -t nat -A POSTROUTING -s 127.0.0.0/8 -d 172.17.0.2 -m conntrack --ctstate DNAT -j MASQUERADE
interface=$(ip -o route get 172.17.0.2 | grep -o 'dev [^ ][^ ]*'|sed 's/^dev //')
echo 1 > /proc/sys/net/ipv4/conf/$(interface)/route_localnet
Once all that is said, you should try using a non-
127.0.0.0/8 address, still local to the host. This could simplify things, not requiring
route_localnet (and possibly sometimes not requiring the additional