共计 7008 个字符,预计需要花费 18 分钟才能阅读完成。
问题现象
在 Ubuntu 22.04 系统上尝试安装软件包时,遇到了域名解析失败的问题:
root@lenovo:/etc# apt install net-tools
...
错误:1 http://security.ubuntu.com/ubuntu jammy-updates/main amd64 net-tools amd64
暂时不能解析域名 "cn.archive.ubuntu.com"
E: 无法下载 http://security.ubuntu.com/ubuntu/pool/main/n/net-tools/...
E: 有几个软件包无法下载,要不运行 apt-get update 或者加上 --fix-missing 的选项再试试?
进一步测试发现:
# 网络连接正常
root@lenovo:/etc# ping 8.8.8.8
64 bytes from 8.8.8.8: icmp_seq=1 ttl=111 time=49.8 ms
# 但域名解析完全失败
root@lenovo:/etc# ping www.baidu.com
ping: www.baidu.com: 域名解析出现暂时性错误
关键特征:
- ✅ 网络连通性正常(能 ping 通公网 IP)
- ❌ DNS 解析完全失败(任何域名都无法解析)
- ⚡ 解析失败速度极快(几乎瞬间报错)
排查过程
第一步:检查 DNS 配置
root@lenovo:/etc# cat /etc/resolv.conf
nameserver 127.0.0.53
options edns0 trust-ad
search .
发现系统使用 systemd-resolved 服务(127.0.0.53 是本地 DNS stub resolver)。
尝试修改 DNS 服务器为国内的阿里云 DNS:
# 编辑 systemd-resolved 配置
sudo nano /etc/systemd/resolved.conf
# 修改为:[Resolve]
DNS=223.5.5.5 223.6.6.6 114.114.114.114
FallbackDNS=8.8.8.8
# 重启服务
sudo systemctl restart systemd-resolved
结果:问题依旧,DNS 仍然无法解析。
第二步:检查 systemd-resolved 服务状态
root@lenovo:/etc# resolvectl query www.baidu.com
www.baidu.com: resolve call failed: Unit dbus-org.freedesktop.resolve1.service not found.
这个错误提示 systemd-resolved 服务没有正常运行。启动服务:
sudo systemctl start systemd-resolved
sudo systemctl enable systemd-resolved
服务启动成功,但 DNS 仍然无法解析!
第三步:直接测试 DNS 查询
绕过 systemd-resolved,直接向 DNS 服务器查询:
root@lenovo:/etc# dig @223.5.5.5 www.baidu.com
;; communications error to 223.5.5.5#53: connection refused
;; communications error to 223.5.5.5#53: connection refused
;; communications error to 223.5.5.5#53: connection refused
root@lenovo:/etc# nslookup www.baidu.com 223.5.5.5
;; communications error to 223.5.5.5#53: connection refused
关键发现:连接到 DNS 服务器的 53 端口被拒绝(connection refused)!
这说明问题不在 DNS 配置或 systemd-resolved,而是 网络层面的阻断。
第四步:检查防火墙规则
首先检查常见的防火墙工具:
# UFW 防火墙
root@lenovo:/etc# sudo ufw status
状态:不活动
# firewalld
root@lenovo:/etc# systemctl status firewalld
Unit firewalld.service could not be found.
常规防火墙都未启用,继续检查 iptables:
root@lenovo:/etc# sudo iptables -L OUTPUT -n -v
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
OUTPUT 链已经允许了 53 端口,但问题仍然存在。
第五步:检查 NAT 表(关键突破)
怀疑可能是 NAT 规则在捣鬼:
root@lenovo:/etc# sudo iptables -t nat -L -n -v
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
52716 3543K SANGFOR_OUTPUT all -- * * 0.0.0.0/0 0.0.0.0/0
...
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
31925 2273K DNAT udp -- * * 0.0.0.0/0 0.0.0.0/0 to:127.0.0.1:5373
找到罪魁祸首了!
存在一条 DNAT 规则,将 所有 UDP 流量 重定向到 127.0.0.1:5373,而这个端口没有任何服务监听。
同时发现了 SANGFOR_OUTPUT 链,这是深信服 EasyConnect VPN 软件添加的。
根本原因分析
EasyConnect VPN 的网络劫持机制
回顾操作历史,发现之前运行过 EasyConnect VPN 容器:
docker run
--name vpn
--restart=always
--network host # 关键:使用 host 网络模式
-d
--device /dev/net/tun
--cap-add NET_ADMIN
-ti
-e EC_VER=7.6.3
-e CLI_OPTS="-d https://211.13xxx:8443 -u sj35 -p 1234"
hagb/docker-easyconnect:cli
问题形成过程:
- VPN 容器启动时(使用
--network host):- 在宿主机的 iptables NAT 表添加规则
- 将所有 UDP 流量重定向到
127.0.0.1:5373 - 在 5373 端口启动 DNS 代理服务
- DNS 请求流程:
应用 → 53 端口 → NAT 重定向 → 5373 → VPN 隧道 → 企业 DNS → 返回
- VPN 容器正常运行时:
- 5373 端口有服务监听 ✅
- DNS 解析正常工作 ✅
- VPN 容器停止后(问题根源):
- 5373 端口的服务消失 ❌
- 但 iptables 规则仍然存在! ⚠️
- DNS 请求流程:
应用 → 53 端口 → NAT 重定向 → 5373(无服务)→ Connection Refused❌
为什么 iptables 规则没有自动清理?
使用 --network host 模式时,容器直接操作宿主机的网络栈,包括 iptables。Docker 在容器停止时:
- ✅ 会清理 桥接网络 的 iptables 规则
- ❌ 不会 清理 host 网络模式下容器添加的规则
这是因为 Docker 无法区分哪些规则是容器添加的,哪些是用户手动配置的。
影响范围
这个问题影响 所有使用 UDP 协议的 DNS 查询:
- DNS 查询(端口 53,UDP 协议)
- 部分应用的网络功能
- 系统软件包管理器(apt、yum 等)
解决方案
立即修复:删除劫持规则
# 删除 UDP DNS 重定向规则
sudo iptables -t nat -D OUTPUT -p udp -j DNAT --to-destination 127.0.0.1:5373
# 删除 SANGFOR_OUTPUT 链的引用
sudo iptables -t nat -D PREROUTING -j SANGFOR_OUTPUT
sudo iptables -t nat -D PREROUTING -j SANGFOR_OUTPUT
# 清空并删除 SANGFOR_OUTPUT 链
sudo iptables -t nat -F SANGFOR_OUTPUT
sudo iptables -t nat -X SANGFOR_OUTPUT
验证修复:
root@lenovo:/etc# ping www.baidu.com
PING www.a.shifen.com (39.156.70.239) 56(84) bytes of data.
64 bytes from 39.156.70.239: icmp_seq=1 ttl=52 time=25.5 ms
✅ 问题解决!
长期解决方案
为了既能使用 VPN 访问内网,又不影响本机的 DNS 解析,推荐以下方案:
方案一:VPN 管理脚本(推荐)
创建一个自动管理 VPN 和清理规则的脚本:
#!/bin/bash
# 文件路径:/usr/local/bin/vpn-manager.sh
VPN_CONTAINER="vpn"
start_vpn() {
echo "启动 EasyConnect VPN..."
if docker ps -a | grep -q "$VPN_CONTAINER"; then
docker start "$VPN_CONTAINER"
else
docker run
--name "$VPN_CONTAINER"
--restart=no
--network host
-d
-ti
--device /dev/net/tun
--cap-add NET_ADMIN
-e EC_VER=7.6.3
-e CLI_OPTS="-d https://your-vpn-url -u username -p password"
hagb/docker-easyconnect:cli
fi
echo "VPN 已启动"
}
stop_vpn() {
echo "停止 VPN 并清理规则..."
docker stop "$VPN_CONTAINER" 2>/dev/null
# 清理 iptables 规则
iptables -t nat -D OUTPUT -p udp -j DNAT --to-destination 127.0.0.1:5373 2>/dev/null
while iptables -t nat -D PREROUTING -j SANGFOR_OUTPUT 2>/dev/null; do
:
done
iptables -t nat -F SANGFOR_OUTPUT 2>/dev/null
iptables -t nat -X SANGFOR_OUTPUT 2>/dev/null
# 清理 tun0 相关规则
while iptables -D INPUT -i tun0 -p tcp -j DROP 2>/dev/null; do
:
done
while iptables -t nat -D POSTROUTING -o tun0 -j MASQUERADE 2>/dev/null; do
:
done
echo "VPN 已停止,规则已清理"
}
cleanup_rules() {
echo "清理 SANGFOR 规则..."
#(规则清理代码同上)echo "规则清理完成"
}
case "$1" in
start) start_vpn ;;
stop) stop_vpn ;;
cleanup) cleanup_rules ;;
*)
echo "用法: $0 {start|stop|cleanup}"
exit 1
;;
esac
使用方法:
# 赋予执行权限
sudo chmod +x /usr/local/bin/vpn-manager.sh
# 启动 VPN
sudo /usr/local/bin/vpn-manager.sh start
# 停止 VPN(自动清理规则)sudo /usr/local/bin/vpn-manager.sh stop
# 如果 VPN 异常退出,手动清理
sudo /usr/local/bin/vpn-manager.sh cleanup
方案二:系统服务自动清理
创建一个在系统启动时自动清理残留规则的服务:
# 创建服务文件
sudo nano /etc/systemd/system/vpn-cleanup.service
写入内容:
[Unit]
Description=Cleanup EasyConnect VPN rules on boot
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/vpn-manager.sh cleanup
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
启用服务:
sudo systemctl enable vpn-cleanup.service
sudo systemctl start vpn-cleanup.service
方案三:保存正常的 iptables 规则
# 安装持久化工具
sudo apt install iptables-persistent
# 在规则正常时保存
sudo netfilter-persistent save
# 如果规则被污染,恢复
sudo netfilter-persistent reload
排查技巧总结
遇到类似的 DNS 解析问题时,可以按以下顺序排查:
1. 确认问题层级
# 网络连通性
ping 8.8.8.8
# DNS 解析
ping www.baidu.com
# 直接 DNS 查询
dig @223.5.5.5 www.baidu.com
2. 检查 DNS 配置
# 查看当前 DNS 配置
cat /etc/resolv.conf
# 检查 systemd-resolved
systemctl status systemd-resolved
resolvectl status
3. 检查防火墙规则
# 常规防火墙
sudo ufw status
systemctl status firewalld
# iptables filter 表
sudo iptables -L -n -v
# iptables NAT 表(重点!)sudo iptables -t nat -L -n -v
# iptables mangle 表
sudo iptables -t mangle -L -n -v
4. 检查端口占用
# 查看 53 端口
sudo ss -tunlp | grep :53
sudo lsof -i :53
# 查看异常端口(如 5373)sudo ss -tunlp | grep 5373
5. 查看网络接口
# 检查是否有 VPN 接口
ip addr show
ip route show
# 查看 tun/tap 设备
ls -l /dev/net/
预防措施
为避免类似问题,建议:
1. 谨慎使用 --network host
除非必要,尽量使用 Docker 的桥接网络模式:
# 不推荐
docker run --network host ...
# 推荐
docker run -p 8080:80 ...
2. 容器添加清理机制
在容器的启动脚本中添加信号处理:
#!/bin/bash
# 清理函数
cleanup() {
echo "清理 iptables 规则..."
iptables -t nat -D OUTPUT ...
}
# 捕获退出信号
trap cleanup EXIT INT TERM
# 启动服务
/path/to/service
3. 定期检查 iptables 规则
# 添加到 crontab
0 */6 * * * /usr/local/bin/check-iptables.sh
4. 使用容器网络命名空间
对于需要特殊网络配置的容器,考虑使用网络命名空间隔离:
# 创建独立的网络命名空间
ip netns add vpn-ns
# 在命名空间中运行容器
ip netns exec vpn-ns docker run ...
经验教训
- DNS 解析失败 + connection refused = 检查 NAT 表
- 不要只关注 filter 表,NAT 表的 DNAT/REDIRECT 规则可能劫持流量
- 容器退出不等于环境还原
- 使用
--network host时,容器对宿主机的修改不会自动清理
- 使用
- VPN 软件是常见嫌疑犯
- EasyConnect、深信服、Cisco AnyConnect 等企业 VPN 都可能修改系统网络配置
- 问题排查要分层思考
- 应用层(DNS 配置)→ 系统层(systemd-resolved)→ 网络层(iptables)→ 链路层(网卡)
- 工具链的重要性
- dig、nslookup、ss、iptables、resolvectl 等工具要熟练掌握
参考资料
总结
这次 DNS 解析问题的根本原因是 EasyConnect VPN 容器在 host 网络模式下添加的 iptables NAT 规则,在容器停止后没有被清理,导致所有 DNS 查询被重定向到一个不存在的服务。
关键教训:
- 使用
--network host需要特别小心 - 容器退出时需要主动清理网络配置
- 排查网络问题要关注 NAT 表和 mangle 表
通过创建管理脚本和系统服务,可以在享受 VPN 便利的同时,避免对宿主机网络环境造成持久影响。
作者注:本文记录了一次真实的问题排查过程,希望能帮助遇到类似问题的朋友快速定位和解决。如有疑问或补充,欢迎讨论交流。