Iptables – Forward incoming external DNS packets to the embedded docker DNS

dockerdomain-name-systemiptablesopenvpn

I'm running an openvpn service in a docker container. Linked to this container are some other containers, including a gitlab container. I want to be able to reach this gitlab container using its container name as host from VPN clients. I like the idea to use docker networks to manage what containers will be reachable through VPN.

Since docker has its embedded DNS feature, I think this should be possible by forwarding DNS packets to it (127.0.0.11:53), so I can use the VPN server's address as name server on the clients.

At least, executed locally in the OVPN container, it seems like there is a working DNS service:

bash-4.3# nslookup gitlab 127.0.0.11             
Server:    127.0.0.11
Address 1: 127.0.0.11

Name:      gitlab
Address 1: 172.19.0.2 gitlab_gitlab_1.gitlab_default

However, my iptables routes won't work:

iptables -t nat -I PREROUTING 1 -p udp -s 192.168.255.0/24 --dport 53 -j DNAT --to-destination 127.0.0.11:53
iptables -t nat -I PREROUTING 1 -p tcp -s 192.168.255.0/24 --dport 53 -j DNAT --to-destination 127.0.0.11:53

Where 192.168.255.0/24 is my VPN net.

This is the entire iptables list:

bash-4.3# iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
DNAT       tcp  --  192.168.255.0/24     anywhere             tcp dpt:domain to:127.0.0.11:53
DNAT       udp  --  192.168.255.0/24     anywhere             udp dpt:domain to:127.0.0.11:53

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
DOCKER_OUTPUT  all  --  anywhere             127.0.0.11          

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
DOCKER_POSTROUTING  all  --  anywhere             127.0.0.11          
MASQUERADE  all  --  192.168.255.0/24     anywhere            

Chain DOCKER_OUTPUT (1 references)
target     prot opt source               destination         
DNAT       tcp  --  anywhere             127.0.0.11           tcp dpt:domain to:127.0.0.11:36429
DNAT       udp  --  anywhere             127.0.0.11           udp dpt:domain to:127.0.0.11:33172

Chain DOCKER_POSTROUTING (1 references)
target     prot opt source               destination         
SNAT       tcp  --  127.0.0.11           anywhere             tcp spt:36429 to::53
SNAT       udp  --  127.0.0.11           anywhere             udp  spt:33172 to::53

All rules except of the two mentioned above are created automatically by docker. What am I doing wrong?

Best Answer

The destination port of dockers embedded DNS change with every start (to:127.0.0.11:36429). Try to dispatch the incoming traffic to the DOCKER_OUTPUT rule:

iptables -t nat -A PREROUTING -p udp --dport 53 -j DOCKER_OUTPUT
iptables -t nat -A PREROUTING -p tcp --dport 53 -j DOCKER_OUTPUT

To forward the response to the original requester add the masquerade rule to any package that had its origin outside your localhost:

iptables -t nat -A POSTROUTING ! -s 127.0.0.1 -p udp --dport 53 -j MASQUERADE
iptables -t nat -A POSTROUTING ! -s 127.0.0.1 -p tcp --dport 53 -j MASQUERADE