ZeroTier路由环境下CentOS 7 VPS中Docker部署的dnsmasq容器无法外部访问
我之前在ZeroTier路由到本地的场景下踩过dnsmasq的坑,太懂这种明明配置看起来和其他服务一样,就是访问不了的抓狂感!咱们一步步来排查解决:
1. 先补全SNAT规则(最可能的核心问题)
你现在只加了PREROUTING的DNAT规则,把ZeroTier过来的流量转去127.0.0.1,但返回的数据包源地址是127.0.0.1,ZeroTier客户端收到后会直接丢弃——因为它请求的是VPS的ZeroTier IP,结果返回包的源是本地回环地址,完全不匹配。
解决办法是加一条POSTROUTING的SNAT规则,把从回环接口发向ZeroTier子网的流量,源地址改成VPS的ZeroTier接口IP:
# 先获取你的ZeroTier接口IP ZT_IP=$(ip addr show ztyxa6sxte | grep 'inet ' | awk '{print $2}' | cut -d/ -f1) # 替换成你的ZeroTier子网,比如常见的192.168.192.0/24 ZT_SUBNET="192.168.192.0/24" # 加SNAT规则 sudo iptables -t nat -A POSTROUTING -o lo -d $ZT_SUBNET -j SNAT --to-source $ZT_IP
2. 调整iptables规则的顺序,避免被Docker规则覆盖
Docker会自动往iptables里塞一堆规则,如果你用-A追加PREROUTING规则,很大概率排在Docker规则后面,导致流量还没走到你的规则就被Docker的链处理了。改成用-I把规则插入到最前面:
sudo iptables -t nat -I PREROUTING -i ztyxa6sxte -j DNAT --to-destination 127.0.0.1
3. 检查Firewalld是否在搞事情
CentOS7默认开的firewalld经常偷偷拦截转发流量,先临时关了测试下:
sudo systemctl stop firewalld
如果关了之后能访问了,那给ZeroTier接口加个信任区规则,再重新开firewalld:
sudo firewall-cmd --add-interface=ztyxa6sxte --zone=trusted --permanent sudo firewall-cmd --reload sudo systemctl start firewalld
4. 确认容器内部的dnsmasq确实在监听所有接口
虽然你Docker端口绑的是127.0.0.1,但容器内部的dnsmasq如果只监听自身回环地址的话,主机转进去的流量也接不到。进容器看看:
docker exec -it dns netstat -tulpn | grep 53
如果输出里只有127.0.0.1:53,那去你的/etc/dnsmasq/dnsmasq.conf里加一行:
listen-address=0.0.0.0
然后重启容器:docker restart dns
最后测试
先测webUI:用ZeroTier客户端机器访问http://<VPS的ZT IP>:5380,如果能进登录界面,说明转发没问题;再测DNS:用dig @<VPS的ZT IP> example.com或者nslookup example.com <VPS的ZT IP>,看能不能返回解析结果。
对了,这些iptables规则重启后会消失,要是想永久保存,记得装iptables-services,然后保存规则:
sudo yum install -y iptables-services sudo systemctl stop firewalld sudo systemctl enable iptables sudo service iptables save
备注:内容来源于stack exchange,提问作者Nikita Melnikov

