✅净化 DNS 抗主机/上游商家污染 驯服DHCP 一劳永逸构建你自己VPS上的DNS安全堡垒

😭事情起因:

我原本以为DD后系统会很干净,直到有一天,我DD完照例用同样的脚本,客户端timeout,直接ping ip是通的,查看程序运行状态Activating,配置文件完整无误,奇怪了,以前从没遇到这种情况。查看运行日志,发现了问题,程序在出站DNS走的不是8.8.8.8,1.1.1.1,而是主机上游的 xxxxx.com,从而导致的timeout,我查看resolv.conf,发现配置是这样的,前面竟然莫名奇妙的多了search xxxxx.com

search xxxxx.com
nameserver 8.8.8.8
nameserver 1.1.1.1

随后遍历互联网,不了解还真不知道,了解以后才发现主机商家的小动作还真不少


👿主机商家的动机:

他们添加额外的DNS配置,通常并非出于恶意,而是源于两个主要原因:

  1. 为了提供便捷的内部服务网络
    • 动机: 大型云服务商通常拥有一个庞大的内部网络,提供诸如对象存储、内网数据库、软件源镜像等服务。他们希望你能通过简单的主机名(如 storage-server 或 db-cluster-1)而不是复杂的私有IP来访问这些服务。
    • 实现方式: 他们会在你的VPS上自动添加一个 search 搜索域,通常是他们自己的域名(例如 search my-provider.internal)。这样,当你尝试访问 storage-server 时,系统会自动将其补全为 storage-server.my-provider.internal 并进行查询。
    • “污染”的副作用: 这个看似“便捷”的功能,却带来了巨大的隐患。当你的服务器查询任何一个不存在的内部域名时,这个查询会被发送到公网,无意中暴露了你的内部网络架构和应用命名习惯。这就是我们所说的 DNS 泄漏
  2. 为了优化网络性能与管理
    • 动机: 主机商会在他们的网络中部署自己的DNS解析服务器。他们认为,让你的VPS使用这些“就近”的DNS服务器,可以获得更低的解析延迟。同时,这也便于他们进行网络流量分析、故障排查,甚至在某些情况下用于安全防护(例如,拦截对已知恶意网站的访问)。
    • 实现方式: 他们通过DHCP协议,在你获取IP地址的同时,强制推送他们自己的DNS服务器地址。这就是为什么我们常常在 resolvectl status 的 Link 部分看到厂商的DNS。
    • “污染”的副作用: 这让你完全受制于主机商。你无法控制他们是否记录了你的每一次DNS查询(从而了解你服务器的所有网络活动);你也无法保证他们不会因为错误配置、审查要求或被攻击而提供错误的、被劫持的DNS结果;最重要的是,这些DNS查询几乎总是未经加密的,在传输路径上任何一个节点都可以被窃听和篡改。

😀这个脚本命令能做什么:

1. 直接修改etc/resolv.conf的弊端是无法持久化,反复被污染,我们的目的是永久斩断厂商控制,夺回DNS控制权

  • 驯服DHCP:通过修改DHCP客户端配置,强制忽略并拒绝接受任何由VPS提供商推送的DNS服务器和搜索域,从动态源头上根除DNS污染。
  • 净化静态配置:自动扫描并禁用所有在网络配置文件(如 /etc/network/interfaces)中由厂商预设的、残留的DNS指令。
  • 移除冲突软件:在Debian 11等存在兼容性问题的系统上,自动卸载与我们现代化方案冲突的旧有软件包(如 resolvconf),确保配置的唯一权威。

2. 构建坚不可摧的DNS安全堡垒

  • 强制端到端加密 (DoT):部署并配置 systemd-resolved,强制所有出站的DNS查询,都必须通过加密的DNS-over-TLS (DoT) 通道进行。这使得任何第三方(包括你的主机商)都无法窃听、篡改或劫持你的DNS流量。
  • 启用DNSSEC验证:开启DNSSEC校验,确保你的服务器获取到的每一个IP地址都是真实、未经篡改的,从根本上防御DNS缓存投毒攻击。
  • 收缩攻击面:自动禁用 LLMNR 和 mDNS 等在服务器上毫无用处且有潜在安全风险的本地网络发现协议。

3. 确保极致的可靠性与性能

  • 高可用DNS池: Google (8.8.8.8) 和 Cloudflare (1.1.1.1)
  • 本地缓存加速:通过 systemd-resolved 提供的本地DNS缓存,重复的DNS查询将从内存中毫秒级响应,极大地提升了你服务器上应用程序的性能。

🛠️失败挽救:

一般不会出现问题,如果出现问题导致VPS无法解析,可手动修改/etc/resolv.conf

nameserver 8.8.8.8
nameserver 1.1.1.1

📖使用方法(命令本体):

  • 不支持Alpine,ipv6 only小鸡慎用,Debian11/12测试没问题,其他系统自行测试。
  • 保证你VPS是root登录状态,直接全部复制代码,回车。
sudo bash << 'SCRIPT_END'
#!/usr/bin/env bash
set -euo pipefail
readonly TARGET_DNS="8.8.8.8#dns.google 1.1.1.1#cloudflare-dns.com"
readonly SECURE_RESOLVED_CONFIG="[Resolve]
DNS=${TARGET_DNS}
LLMNR=no
MulticastDNS=no
DNSSEC=allow-downgrade
DNSOverTLS=yes
"
readonly GREEN="\033[0;32m"
readonly YELLOW="\033[1;33m"
readonly RED="\033[0;31m"
readonly NC="\033[0m"
log() { echo -e "${GREEN}--> $1${NC}"; }
log_warn() { echo -e "${YELLOW}--> $1${NC}"; }
log_error() { echo -e "${RED}--> $1${NC}" >&2; }
purify_and_harden_dns() {
    echo -e "\n--- 开始执行DNS净化与安全加固流程 ---"
    local debian_version
    debian_version=$(grep "VERSION_ID" /etc/os-release | cut -d'=' -f2 | tr -d '"' || echo "unknown")
    log "阶段一:正在清除所有潜在的DNS冲突源..."
    local dhclient_conf="/etc/dhcp/dhclient.conf"
    if [[ -f "$dhclient_conf" ]]; then
        if ! grep -q "ignore domain-name-servers;" "$dhclient_conf" || ! grep -q "ignore domain-search;" "$dhclient_conf"; then
            log "正在驯服 DHCP 客户端 (dhclient)..."
            echo "" >> "$dhclient_conf"
            echo "ignore domain-name-servers;" >> "$dhclient_conf"
            echo "ignore domain-search;" >> "$dhclient_conf"
            log "${GREEN}✅ 已确保 'ignore' 指令存在于 ${dhclient_conf}${NC}"
        fi
    fi
    local ifup_script="/etc/network/if-up.d/resolved"
    if [[ -f "$ifup_script" ]] && [[ -x "$ifup_script" ]]; then
        log "正在禁用有冲突的 if-up.d 兼容性脚本..."
        chmod -x "$ifup_script"
        log "${GREEN}✅ 已移除 ${ifup_script} 的可执行权限。${NC}"
    fi
    local interfaces_file="/etc/network/interfaces"
    if [[ -f "$interfaces_file" ]] && grep -qE '^[[:space:]]*dns-(nameservers|search|domain)' "$interfaces_file"; then
        log "正在净化 /etc/network/interfaces 中的厂商残留DNS配置..."
        sed -i -E 's/^[[:space:]]*(dns-(nameservers|search|domain).*)/# \1/' "$interfaces_file"
        log "${GREEN}✅ 旧有DNS配置已成功注释禁用。${NC}"
    fi
    log "阶段二:正在配置 systemd-resolved..."
    if ! command -v resolvectl &> /dev/null; then
        log "正在安装 systemd-resolved..."
        apt-get update -y > /dev/null
        apt-get install -y systemd-resolved > /dev/null
    fi
    if [[ "$debian_version" == "11" ]] && dpkg -s resolvconf &> /dev/null; then
        log "检测到 Debian 11 上的 'resolvconf',正在卸载..."
        apt-get remove -y resolvconf > /dev/null
        rm -f /etc/resolv.conf
        log "${GREEN}✅ 'resolvconf' 已成功卸载。${NC}"
    fi
    log "正在启用并启动 systemd-resolved 服务..."
    systemctl enable systemd-resolved
    systemctl start systemd-resolved
    log "正在应用最终的DNS安全配置 (DoT, DNSSEC...)"
    echo -e "${SECURE_RESOLVED_CONFIG}" > /etc/systemd/resolved.conf
    ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
    systemctl restart systemd-resolved
    sleep 1
    log "阶段三:正在安全地重启网络服务以应用所有更改..."
    if systemctl is-enabled --quiet networking.service; then
        systemctl restart networking.service
        log "${GREEN}✅ networking.service 已安全重启。${NC}"
    fi
    echo -e "\n${GREEN}✅ 全部操作完成!以下是最终的 DNS 配置状态:${NC}"
    echo "===================================================="
    resolvectl status
    echo "===================================================="
    echo -e "\n${GREEN}DNS净化脚本执行完成${NC}"
    echo -e "贡献者:NSdesk"
    echo -e "更多信息:https://www.nodeseek.com/space/23129/"
    echo "===================================================="
}
main() {
    if [[ $EUID -ne 0 ]]; then
       log_error "错误: 此脚本必须以 root 用户身份运行。请使用 'sudo'。"
       exit 1
    fi
    echo "--- 开始执行全面系统DNS健康检查 ---"
    local is_perfect=true
    echo -n "1. 检查 systemd-resolved 实时状态... "
    if ! command -v resolvectl &> /dev/null || ! resolvectl status &> /dev/null; then
        echo -e "${YELLOW}服务未运行或无响应。${NC}"
        is_perfect=false
    else
        local status_output
        status_output=$(resolvectl status)
        local current_dns
        current_dns=$(echo "${status_output}" | sed -n '/Global/,/^\s*$/{/DNS Servers:/s/.*DNS Servers:[[:space:]]*//p}' | tr -d '\r\n' | xargs)
        if [[ "${current_dns}" != "${TARGET_DNS}" ]] || ! echo "${status_output}" | grep -q -- "-LLMNR" || ! echo "${status_output}" | grep -q -- "-mDNS" || ! echo "${status_output}" | grep -q -- "+DNSOverTLS" || ! echo "${status_output}" | grep -q "DNSSEC=allow-downgrade"; then
            echo -e "${YELLOW}实时配置与安全目标不符。${NC}"
            is_perfect=false
        else
            echo -e "${GREEN}配置正确。${NC}"
        fi
    fi
    echo -n "2. 检查 dhclient.conf 配置... "
    local dhclient_conf="/etc/dhcp/dhclient.conf"
    if [[ -f "$dhclient_conf" ]]; then
        if grep -q "ignore domain-name-servers;" "$dhclient_conf" && grep -q "ignore domain-search;" "$dhclient_conf"; then
            echo -e "${GREEN}已净化。${NC}"
        else
            echo -e "${YELLOW}未发现 'ignore' 净化参数。${NC}"
            is_perfect=false
        fi
    else
        echo -e "${GREEN}文件不存在,无需净化。${NC}"
    fi
    echo -n "3. 检查 if-up.d 冲突脚本... "
    local ifup_script="/etc/network/if-up.d/resolved"
    if [[ ! -f "$ifup_script" ]] || [[ ! -x "$ifup_script" ]]; then
        echo -e "${GREEN}已禁用或不存在。${NC}"
    else
        echo -e "${YELLOW}脚本存在且可执行。${NC}"
        is_perfect=false
    fi
    if [[ "$is_perfect" == true ]]; then
        echo -e "\n${GREEN}✅ 全面检查通过!系统DNS配置稳定且安全。无需任何操作。${NC}"
        echo -e "贡献者:NSdesk"
        echo -e "更多信息:https://www.nodeseek.com/space/23129/"
        exit 0
    else
        echo -e "\n${YELLOW}--> 一项或多项检查未通过。为了确保系统的长期稳定,将执行完整的净化与加固流程...${NC}"
        purify_and_harden_dns
    fi
}
main "$@"
SCRIPT_END

🤣最后:

  • 为什么用DOT而不是DOH,是因为DOT更适合服务器体质,DOH更适合客户端体质
  • 脚本代码由gemini,grok轮番打磨完成,由于本人对代码一窍不通,免不了被gemini,grok带了不少弯路,最后用了不少精力和时间将这两头倔驴拉到正轨完成任务,试用一段时候后DNS效果良好,无不良反应,后交由claude转译成全部代码复制粘贴直接执行的版本,claude更倔,框框一顿输出...
  • 抛砖引玉,佬友们有其他更好的方法可以一起沟通讨论,大家添砖加瓦,
  • 为什么不做成shell脚本放在github?小弟我github上还有其他好玩的脚本,不方便泄露,以后择机分享给大家
点赞

发表回复

电子邮件地址不会被公开。必填项已用 * 标注

×
订阅图标按钮