Ubuntu DNS解析失败排查记录:EasyConnect VPN劫持问题完全解析

552次阅读
没有评论

共计 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

问题形成过程

  1. VPN 容器启动时(使用 --network host):
    • 在宿主机的 iptables NAT 表添加规则
    • 将所有 UDP 流量重定向到 127.0.0.1:5373
    • 在 5373 端口启动 DNS 代理服务
    • DNS 请求流程:应用 → 53 端口 → NAT 重定向 → 5373 → VPN 隧道 → 企业 DNS → 返回
  2. VPN 容器正常运行时
    • 5373 端口有服务监听 ✅
    • DNS 解析正常工作 ✅
  3. 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 ...

经验教训

  1. DNS 解析失败 + connection refused = 检查 NAT 表
    • 不要只关注 filter 表,NAT 表的 DNAT/REDIRECT 规则可能劫持流量
  2. 容器退出不等于环境还原
    • 使用 --network host 时,容器对宿主机的修改不会自动清理
  3. VPN 软件是常见嫌疑犯
    • EasyConnect、深信服、Cisco AnyConnect 等企业 VPN 都可能修改系统网络配置
  4. 问题排查要分层思考
    • 应用层(DNS 配置)→ 系统层(systemd-resolved)→ 网络层(iptables)→ 链路层(网卡)
  5. 工具链的重要性
    • dig、nslookup、ss、iptables、resolvectl 等工具要熟练掌握

参考资料


总结

这次 DNS 解析问题的根本原因是 EasyConnect VPN 容器在 host 网络模式下添加的 iptables NAT 规则,在容器停止后没有被清理,导致所有 DNS 查询被重定向到一个不存在的服务

关键教训:

  • 使用 --network host 需要特别小心
  • 容器退出时需要主动清理网络配置
  • 排查网络问题要关注 NAT 表和 mangle 表

通过创建管理脚本和系统服务,可以在享受 VPN 便利的同时,避免对宿主机网络环境造成持久影响。


作者注:本文记录了一次真实的问题排查过程,希望能帮助遇到类似问题的朋友快速定位和解决。如有疑问或补充,欢迎讨论交流。

正文完
 0
nwnusun
版权声明:本站原创文章,由 nwnusun 于2026-01-05发表,共计7008字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)