在Debian服务器运维中,resolv.conf文件是DNS解析的核心枢纽,也是DNS劫持攻击最常瞄准的目标。许多运维人员习惯性地认为只要设置了nameserver就万事大吉,实际上攻击者可能通过修改这个文件、劫持本地解析服务或篡改网络配置,让服务器所有的域名查询都指向恶意DNS服务器。当你的APT源更新失败、证书验证报错或业务请求被重定向到钓鱼站点时,第一时间应该检查的就是resolv.conf的完整性。

DNS劫持在Debian系统上通常表现为三种形态:一是resolv.conf文件被直接篡改,nameserver指向了攻击者控制的IP;二是文件虽然看起来正常,但被设置了不可变属性或被后台进程持续覆盖;三是系统使用了本地DNS缓存服务如systemd-resolved,而该服务被重新配置指向了恶意上游。检测工作不能只停留在cat一下文件内容,必须建立一套多维度的验证体系。

直接内容校验与属性检查

最基本的检测手段是查看resolv.conf的实际内容。使用cat /etc/resolv.conf命令可以快速浏览当前配置,但要注意文件头部是否有明显的注释标识。正常的Debian系统如果由NetworkManager或systemd-networkd管理,文件头部会有"This file is managed by"之类的说明。如果这个文件突然变得异常简洁,只有两行nameserver记录且没有任何管理声明,就值得警惕了。

更关键的是检查文件的软链接状态。在较新的Debian版本中,/etc/resolv.conf通常是一个指向/run/systemd/resolve/stub-resolv.conf或/run/systemd/resolve/resolv.conf的符号链接。执行ls -la /etc/resolv.conf可以查看链接目标。如果它变成了一个普通文件而非软链接,说明可能被恶意程序替换过。攻击者经常删除软链接后创建一个同名的静态文件,这样即使重启网络服务也不会恢复原始配置。

文件属性的检测同样不可忽视。使用lsattr /etc/resolv.conf命令查看文件的扩展属性。如果输出中出现i标志,说明文件被设置了不可变属性,连root用户都无法修改。这是攻击者常用的持久化手段,他们会先篡改内容再锁定文件,让运维人员即使发现异常也无法直接编辑。正常的resolv.conf绝不应该有i属性,一旦发现必须立即使用chattr -i /etc/resolv.conf解除后再做进一步排查。

DNS服务器可达性与响应验证

确认了文件内容后,必须验证nameserver指向的IP是否真的是可信的DNS服务器。使用dig命令向特定DNS服务器发起查询是直接有效的方法。执行dig +short @8.8.8.8 www.baidu.com可以测试Google公共DNS的响应,但检测的重点是你当前配置的nameserver。假设resolv.conf中配置的是192.168.1.1,那么运行dig +short @192.168.1.1 www.baidu.com,观察返回的IP地址是否与预期一致。

这里有一个容易被忽略的技巧:对比多个DNS服务器的解析结果。同时向你的本地DNS、一个已知的公共DNS如114.114.114.114、以及你的ISP提供的DNS发起同一个域名的查询,比较返回的IP是否存在差异。如果本地DNS返回的某个常用网站的IP与其他几个完全不同,且该IP属于一个陌生的AS号或地理位置异常,那么极有可能发生了劫持。可以用whois命令查询可疑IP的归属信息来辅助判断。

还需要检测DNS服务器是否支持并正确配置了DNSSEC验证。执行dig +dnssec www.baidu.com命令,如果返回结果中包含ad标志,说明该DNS服务器成功验证了域名的DNSSEC签名。如果已知某个域名启用了DNSSEC但你的DNS服务器返回的结果中没有ad标志,可能意味着中间存在篡改。虽然国内很多域名尚未部署DNSSEC,但对于已部署的域名这是一个有效的检测维度。

系统级DNS配置覆盖点的排查

resolv.conf只是DNS配置链条的末端,攻击者完全可能绕过它,在更上游的环节做手脚。Debian系统中,/etc/nsswitch.conf文件中的hosts行决定了域名解析的优先级顺序。使用grep hosts /etc/nsswitch.conf查看配置,正常情况应该是"files dns"或包含resolve、mdns4_minimal等关键字。如果dns被移除或替换成了其他模块,系统可能根本不会读取resolv.conf。

systemd-resolved服务是另一个需要重点检查的对象。在Debian 11及之后的版本中,systemd-resolved默认接管了DNS解析。查看其运行状态使用systemctl status systemd-resolved,检查其配置使用resolvectl status命令。这个命令会显示每个网络接口的DNS服务器设置,以及全局的DNS配置。攻击者可能通过修改/etc/systemd/resolved.conf文件或使用resolvectl命令动态更改上游DNS,而/etc/resolv.conf作为软链接指向stub-resolv.conf时,显示的只是本地回环地址127.0.0.53,掩盖了真实的上游配置。

NetworkManager的连接配置文件也是潜在的篡改点。检查/etc/NetworkManager/system-connections/目录下的连接配置文件,搜索dns=关键字。攻击者可能在某个WiFi或有线连接的配置中插入dns=恶意IP;这样的参数,NetworkManager会在连接激活时自动将这些DNS写入resolv.conf。使用nmcli connection show命令列出所有连接,再用nmcli connection show "连接名称" | grep -i dns逐个检查。

DHCP客户端配置同样值得关注。检查/etc/dhcp/dhclient.conf文件,搜索supersede domain-name-servers或prepend domain-name-servers指令。攻击者如果获得了root权限,可以在这里写入恶意DNS地址,让DHCP客户端在获取租约时强制使用指定的DNS,覆盖DHCP服务器下发的配置。正常的服务器环境很少需要修改这个文件,如果发现有此类配置且不是你主动添加的,就是明确的入侵迹象。

进程行为监控与自动化检测脚本

持续的监控比一次性检查更有价值。使用auditd可以监控resolv.conf文件的任何变更。安装auditd后,执行以下命令添加监控规则:

auditctl -w /etc/resolv.conf -p wa -k resolv_changes

这条规则会记录所有对resolv.conf的写入和属性变更操作。当告警触发时,使用ausearch -k resolv_changes命令查看详细的审计日志,包括执行修改的进程PID、用户ID和时间戳。这能帮助你在第一时间发现篡改行为并追溯到攻击源头。

inotify工具同样适用于实时监控。编写一个简单的shell脚本配合inotifywait命令,可以在文件被修改时立即发送通知或执行预定义的恢复操作:

#!/bin/bash
while inotifywait -e modify,attrib /etc/resolv.conf; do
    echo "WARNING: resolv.conf changed at $(date)"
    # 在此处添加恢复逻辑或告警发送
done

这个脚本可以配置为systemd服务在后台持续运行,作为轻量级的入侵检测手段。

对于多台Debian服务器的运维场景,建议部署一个集中式的DNS一致性检查脚本。脚本逻辑包括:读取resolv.conf中的nameserver列表,向每个nameserver查询一组预定义的基准域名,将解析结果与预期值比对,同时检查文件的软链接状态和扩展属性。所有检查结果汇总后上报到日志中心或告警平台。基准域名的选择很关键,应该包含你业务依赖的核心域名、几个大型公共网站的域名,以及一个你自己完全控制的、启用了DNSSEC的验证域名。

恢复与加固策略

发现DNS劫持后的恢复不是简单地修改resolv.conf就结束了。首先要确认攻击者是否还留下了其他持久化机制。检查crontab中是否有定期覆盖resolv.conf的任务,检查systemd的timer单元,检查/etc/network/if-up.d/目录下是否有可疑脚本,这些都是在网络接口启动时自动执行的钩子脚本,攻击者常在这里植入恶意DNS配置。

对于需要固定DNS配置的服务器,最彻底的加固方式是禁用所有可能修改resolv.conf的服务。如果服务器使用静态网络配置,可以停止systemd-resolved和NetworkManager服务,手动创建resolv.conf文件并设置不可变属性。但要注意,设置不可变属性后,合法的系统更新或网络变更也会受阻,所以这个操作必须在变更管理流程中明确记录。

使用iptables或nftables限制DNS出站流量也是一个有效的防御层。只允许服务器向指定的可信DNS服务器发起UDP 53端口的请求,阻断其他所有DNS出站流量。这样即使resolv.conf被篡改,恶意DNS服务器也无法收到查询请求:

iptables -A OUTPUT -p udp --dport 53 -d 可信DNS_IP1 -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -d 可信DNS_IP2 -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -j DROP

最后,将DNS配置纳入配置管理系统的监控范围。无论是使用Ansible、Puppet还是简单的自定义脚本,都应该定期比对resolv.conf的当前状态与基线配置,任何偏差都触发告警。基线配置应该包括文件内容、软链接目标、文件权限和扩展属性的完整定义。