I've a Debian 10 server (public IP 85.xxx.xxx.xxx at enp6s0
) running a bunch of LXC containers on a network bridge cbr0
. I've the following iptables rules in place so the containers can communicate to the outside world:
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -i cbr0 -j ACCEPT
iptables -A FORWARD -i cbr0 -o enp6s0 -j ACCEPT
iptables -A FORWARD -i enp6s0 -o cbr0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -A POSTROUTING -o enp6s0 -j MASQUERADE
I've created a container (10.0.0.1) that has nginx and redirected incoming http traffic to that container:
iptables -t nat -A PREROUTING -i enp6s0 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1:80
iptables -A FORWARD -i enp6s0 -o cbr0 -p tcp --dport 80 -j ACCEPT
Everything works as expected, I'm able to browse the website on the container etc.
Now I've created a second container (10.0.0.2) that needs fetch some data from the nginx container. If I use the internal 10.0.0.1 IP for the communication my second container is able to wget pages. However if I try to use the server's external IP:
root@second-container:~# nc example.com 80
example.com [85.xxx.xxx.xxx] 80 (http) : Connection refused
My current workaround is to manually add entries at /etc/hosts
for all the domains hosted by nginx so they will resolve to 10.0.0.1 instead of their public IP.
What rules do I need to also allow communication using the external server IP?
Thank you.
This is the common problem about hairpin routing. The proper solution is that you set up the local DNS with the local address so that you do not waste routing resources with NAT, which is resource intensive. The local DNS should point to the local address so that traffic remains local and can be bridged rather than routed.
This is a common problem called a hairpin NAT. The solution I generally prefer is to just add SNAT (source address translation). Therefore the device that already does DNAT at the same time would do SNAT for the same packet.
The overhead is miniscule. And, most importantly, you leave your DNS pure, simple, predictable.