NordBastion 的北极熊吉祥物站在一座昏暗的北欧石制大厅中,紧邻一个服务器机架,一只爪子抬向一块悬浮的青色全息符文石,上面列着域名;四道查询光束穿过一道符文拱门,向外散射至三座远处的服务器巨石,第五道光束被一面琥珀色的十字圆盾挡下,身后的窗外可见极光
操作指南 · 网络·15 分钟阅读 · 40 分钟实操

属于你自己的 DNS 解析器。
那份没人想过要搬走的日志。

你的设备查询过的每一个域名,都会变成别人文件里带时间戳的一行——你 ISP 的文件,或者某个免费公共解析器的文件。六个步骤,把这份文件搬到一台属于你自己的机器上:Unbound 从根开始解析,AdGuard Home 在前面负责过滤,加密 DNS 使用真实证书,以及一个永远不会面向互联网的 53 端口。这台机器每月只需 $3.90。已在 Debian 12 上测试。

六个步骤
  1. 01

    结构

    递归,而非转发

  2. 02

    关闭

    端口 53,抢在它开放之前

  3. 03

    解析

    Unbound,校验中

  4. 04

    过滤

    AdGuard Home,DoH、DoT

  5. 05

    接入

    先隧道,再客户端

  6. 06

    验证

    泄露、DNSSEC、正常运行时间

开始之前 · 到底泄露了什么

DNS 是你人生的索引。而在默认情况下,掌握它的人不是你。

硬盘加密了,流量走了隧道,用 Monero 付款,注册时不留邮箱——然后却依旧让笔记本电脑向一个你从未细想过的解析器求助,把它接触到的每一个主机名都翻译一遍,一次一条,全天候,全部明文传输。域名查询,是整套防护中人们最后才会去加固的一环,却也是最能清晰记录一个人实际在做什么的一环。

值得说清楚这份记录里到底装着什么,因为“DNS 会泄露你的浏览记录”这句话既对,又偷懒。查询日志是一份域名列表,带着时间戳,归属于发起查询的那个 IP 地址。它不是你读过的页面,通常也不是精确的 URL。它是更安静、但对画像分析来说更好用的东西:一段人生的轮廓。银行。雇主。每四分钟签到一次的交友软件。凌晨两点访问过一次的医疗网站。三个只会一起出现、因为它们属于同一个即时通讯客户端的域名。当索引已经这么好用时,没有人还需要去看内容本身。

有四方可能握有这份索引,而自建能把它从前两方,转移到第四方:

搭建 谁能看到你发出的每一条查询 在链路上可见的内容 匿名集合
ISP 解析器,明文(默认情况)你的 ISP,精确到姓名和账户一切内容,链路上任何人都能看到它的所有用户
公共解析器,加密(DoH/DoT)那一家公司,与你的 IP 绑定只有目的地址数百万
你自己的解析器,转发到上游你选定的那个上游——没有变化只有目的地址一个
你自己的解析器,从根开始递归没有人——每个区只能看到属于自己的域名只有目的地址一个,而且这个 IP 不是你本人

第三行是最容易让人掉进坑里的一行。在自己的服务器上装一个解析器,然后让它把一切都转发给 1.1.1.1,实际改变的只有一件事:你的查询现在换了一个不同的 IP 地址抵达 Cloudflare。日志依然存在,依然属于别人,而你多了一台需要维护的机器,换来的却什么都没有。

最后一列,是诚实摆出来的代价,本指南会两次回到这一点。一个只服务于一个家庭的解析器,其查询毫无疑问就属于那个家庭。让这笔交易依然值得的原因是,发起查询的那个 IP,属于一台背后没有任何身份证件的租用北欧服务器——不是登记着用户姓名的家庭宽带线路,也不是绑定着银行卡的大型云服务商账户。匿名集合确实缩小了;但真正被切断的,是它与某个具体的人之间的关联。

步骤 01 · 塑形

递归,还是仅仅中继?这两者之中,只有一个值得你为它开一台服务器。

“我自己的 DNS 服务器”这句话背后,其实藏着两种完全不同的机器,而几乎每一篇教程都会教你搭错那一种。在安装任何东西之前先把这一点想清楚,因为这决定了你搭出来的,究竟是一件隐私工具,还是别人隐私政策的一个代理。

转发器 会把你的查询原样转交给另一个解析器——通常是 Cloudflare、Google、Quad9 或者你的 ISP——然后缓存返回的结果。它简单、快,但会把你整条查询流交给单独一个第三方。凡是以“现在把你的上游设成 1.1.1.1”收尾的教程,搭出来的都是这一种。

递归解析器 则会亲自完成这项工作。要找到 mail.example.com,它先问根服务器谁在管理 .com,再问 .com 服务器谁在管理 example.com,最后向 example.com 自己的域名服务器索取记录。三到四次简短的对话,面对三到四个不同的运营方,彼此互不知晓对方的情况。开启 QNAME 最小化之后,它做得更进一步:只向根服务器询问 .com,只向 .com 服务器询问 example.com。完整的域名只会被透露给唯一一方——也就是托管该域名的那一方,而它反正三十毫秒之后就会收到你的连接。

属性 Unbound,递归 任意转发器 单独的 AdGuard Home
单一一方能看到每条查询无是会——它的上游能看到
本地校验 DNSSEC是信任来自上游信任来自上游
屏蔽列表与按设备规则仅能通过本地区域实现取决于所用的守护进程有,还带网页界面
面向客户端的 DoH / DoT 监听原生支持 DoT,DoH 需借助代理取决于所用的守护进程两者皆内置支持
冷启动延迟3–4 次往返1 次往返1 次往返
正确答案整套方案里负责隐私的那一半只应作为刻意为之的后备方案负责过滤的那一半,位于前端

右边这两列并不是竞争关系——它们是同一套方案的两个组成部分。AdGuard Home 占用 53 端口,负责应用屏蔽列表、终结加密传输;Unbound 则在它后面的回环地址上负责解析工作。这两半谁也做不好对方的活,而整条链路加起来大约只占用一百 MB 内存。

如果这一页你只记住一件事,那就记住这句:一个把请求转发给公共 DNS 提供商的解析器,并没有让你的 DNS 变得私密。它只是把日志挪动了一跳,还给你的维护清单里添了一台服务器。递归,才是这一切真正的重点。

步骤 02 · 关闭

在任何东西打开端口 53 之前,先把它关上。这不是那个需要仔细斟酌的步骤,而是那个必不可少的步骤。

先把机器下单买好。在面板中:订购 → VPS → Sentinel,镜像选 Debian 12,堡垒选离将要使用它的设备最近的那一个——Helsinki、Stockholm、Oslo 或 Reykjavík。开户不需要邮箱地址,账单可以用 Monero、Bitcoin、Lightning 或任何其他支持的资产结算,而这一点在这里比平时更重要:一个私人解析器的全部价值,都建立在发起查询的那个 IP 地址无法被追溯到某个名字这一前提之上。

然后过一遍首小时加固清单——只用 SSH 密钥、禁止密码登录、开启无人值守升级——因为接下来的一切,都是建立在这台机器尚未被攻破这个前提之上的。

在启动这个守护进程之前,先读一下这段

会回应整个互联网的递归解析器,被称为开放解析器,端口一开放,几个小时之内就会被全网扫描器发现。DNS 跑在 UDP 之上,UDP 的源地址极易伪造,一个 60 字节的查询就能换来大出几十倍的应答——所以开放解析器等于是白送给攻击者一个针对第三方的拒绝服务放大器。接下来的过程永远是同一套剧本:流量开始涌入,滥用投诉接踵而至,这个 IP 被空路由。这是第一台 DNS 服务器最常见的收场,没有之一。两条规则就能彻底防止这一切:在解析器配置中默认拒绝,以及在防火墙中永远不允许来自 0.0.0.0/0 的 53/udp 流量。

所以要先配防火墙,趁现在还没有任何服务在监听。下面的规则假定你有一条运行在 10.8.0.0/24 上的 WireGuard 隧道——如果你还没搭建,本页底部链接的 WireGuard 指南只需绕行十五分钟,而这也是整套设计运行起来最顺手的传输方式。

ufw default deny incoming
ufw allow 22/tcp                      # or your moved SSH port
ufw allow 51820/udp                   # WireGuard
ufw allow in on wg0 to any port 53 proto udp
ufw allow in on wg0 to any port 53 proto tcp
ufw enable

# DNS is NOT opened to the world. Confirm it:
ufw status numbered | grep 53

请注意这里刻意漏掉的东西:没有任何一条规则会在公网接口上开放 53 端口。端口 53 只存在于 wg0 和回环接口上。如果你之后再加上 DNS-over-TLS 或 DNS-over-HTTPS,它们用的是 TCP 853 和 TCP 443,靠证书完成身份验证,并会在步骤 05 里获得各自的规则——无论如何,明文的 UDP 端口都会一直保持关闭。

现在清空这个端口。 在 Ubuntu 上——以及任何有人手动启用过它的 Debian 上——systemd-resolved 早已监听在 127.0.0.53:53 上,这会导致 Unbound 因绑定错误而启动失败,而人们往往要花上二十分钟才能看懂这个报错到底是怎么回事。先检查一下,然后只禁用存根监听器,保留 systemd-resolved 作为本地客户端一侧的接口:

ss -lunp 'sport = :53'                # who owns UDP/53 right now?

mkdir -p /etc/systemd/resolved.conf.d
cat > /etc/systemd/resolved.conf.d/no-stub.conf <<'EOF'
[Resolve]
DNSStubListener=no
EOF
systemctl restart systemd-resolved
ss -lunp 'sport = :53'                # should now be empty

在一台干净的 Debian 12 上,第一条命令不会打印任何东西,也没有什么需要处理的。但还是执行一下——只需要一秒钟,而不这样做的代价,是去排查一个你根本不知道正在运行的服务所抛出的“address already in use”错误。

步骤 03 · 解析

Unbound,从根开始。一个软件包,一份配置文件,没有上游。

Unbound 是 NLnet Labs 出品的一款支持校验、递归、带缓存的解析器。它几乎在所有发行版里都有软件包,互联网上很大一部分服务器已经在运行它,而且开箱即用就是递归模式——你得费一番额外的功夫,才能把它变成转发器。把它和根提示软件包一起安装,这样它就能知道根服务器在哪里,而不需要从任何地方去抓取列表:

apt update && apt install -y unbound dns-root-data
unbound -V | head -2

dns-root-data 软件包会把根提示安装到 /usr/share/dns/root.hints,把 DNSSEC 根信任锚点安装到 /usr/share/dns/root.key,并通过正常的软件包渠道保持两者更新。这是获取它们那种略显无聊、但正确的方式;用 cron 任务从某个 URL 下载 root.hints,则是那种迟早会悄无声息失败的方式。

/etc/unbound/unbound.conf.d/nb-resolver.conf

server:
    # ---- Who may ask. Deny first, allow narrowly.
    interface: 127.0.0.1@5335
    interface: 10.8.0.1
    access-control: 0.0.0.0/0 refuse
    access-control: ::0/0 refuse
    access-control: 127.0.0.0/8 allow
    access-control: 10.8.0.0/24 allow

    # ---- Privacy of the recursion itself.
    qname-minimisation: yes
    qname-minimisation-strict: no
    hide-identity: yes
    hide-version: yes
    rrset-roundrobin: yes

    # ---- DNSSEC. The anchor is maintained by the dns-root-data package.
    auto-trust-anchor-file: "/var/lib/unbound/root.key"
    root-hints: "/usr/share/dns/root.hints"
    harden-dnssec-stripped: yes
    harden-glue: yes
    harden-below-nxdomain: yes
    harden-large-queries: yes
    harden-short-bufsize: yes
    aggressive-nsec: yes
    val-clean-additional: yes

    # ---- Do not be a reflector, even by accident.
    refuse-any: yes
    ratelimit: 1000
    ip-ratelimit: 200
    edns-buffer-size: 1232

    # ---- Cache. This is where the 4 GB actually earns its keep.
    msg-cache-size: 128m
    rrset-cache-size: 256m
    cache-min-ttl: 60
    cache-max-ttl: 86400
    prefetch: yes
    prefetch-key: yes
    serve-expired: yes
    serve-expired-ttl: 3600

    # ---- Never chase a name that has no business being on the internet.
    private-address: 10.0.0.0/8
    private-address: 172.16.0.0/12
    private-address: 192.168.0.0/16
    private-address: 169.254.0.0/16
    private-address: fd00::/8
    private-address: fe80::/10

    num-threads: 2
    so-reuseport: yes
    verbosity: 0
    logfile: ""

remote-control:
    control-enable: yes
    control-interface: /run/unbound.ctl

其中有四行才是真正在做事的,值得逐一说一句。

  • 01access-control: 0.0.0.0/0 refuse ——对互联网的默认回答是“不”。它之后的每一条,都是你刻意写下的例外。正是这一行,在防火墙都还来不及出手之前,就先一步挡住了步骤 02 里说的那种放大攻击。
  • 02qname-minimisation: yes ——询问根服务器时问的是 .com,而不是完整的主机名。这就是 RFC 9156,在当前版本的 Unbound 中默认开启,也是递归解析器实际泄露的信息比示意图看起来更少的原因。严格模式请保持关闭:仍有少数配置有误的区,会在严格模式下出问题。
  • 03refuse-any: yes ——ANY 查询会用最小的问题换来最大可能的应答,而这正是放大攻击想要的效果。没有任何正当用途需要它。
  • 04serve-expired: yes ——当权威服务器无法访问时,先把过期的应答发出去,再在后台刷新,而不是直接失败。正是这项设置,让一次远端的故障不会在家里每个人看来都像是网络断了。

留意这两个监听地址。回环地址上的 5335 端口,是下一步 AdGuard Home 要用到的;10.8.0.1 是隧道地址,这样如果你决定跳过过滤这一层,VPN 上的客户端也能直接查询 Unbound。如果你还没有运行 WireGuard,暂时先删掉第二行接口配置——如果地址在这台机器上并不存在,Unbound 是不会启动的。

启动它,并验证它确实在正常工作:

unbound-checkconf
systemctl restart unbound && systemctl enable unbound

apt install -y dnsutils
dig @127.0.0.1 -p 5335 nordbastion.com +short

# DNSSEC: the first must come back with the "ad" flag set,
# the second must FAIL — its signatures are deliberately broken.
dig @127.0.0.1 -p 5335 dnssec-tools.org +dnssec | grep -E 'flags:|status:'
dig @127.0.0.1 -p 5335 dnssec-failed.org  | grep 'status:'

第一条应答里的 ad 标志,说明 Unbound 已经从根开始一路校验了签名链,而且校验通过。第二条出现 SERVFAIL,说明校验拒绝了一条被篡改的应答,而不是把它原样传给了你。如果第二条返回的是一个地址而不是 SERVFAIL,说明校验并没有真正开启,这时首先该检查的就是信任锚点。

步骤 04 · 过滤

AdGuard Home 在前面把关。屏蔽列表、按设备规则,以及加密监听端口。

严格来说,这一步是可选的——单独的 Unbound 就已经是一个私密解析器——但几乎所有人还是会想要它。AdGuard Home 给你带来了屏蔽列表、按客户端设置的策略、调试时可读的查询日志,以及最有用的一点:原生的 DNS-over-TLS 和 DNS-over-HTTPS 监听端口,这样手机和浏览器就能在无法使用 VPN 的网络中,依然与你的解析器对话。

这套安排是一条链,而把端口配对就是全部的诀窍:AdGuard Home 占用 53 端口,面向客户端;Unbound 监听在 127.0.0.1:5335,面向互联网。这台机器上再没有别的东西监听 53 端口。

curl -fsSL https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v
# Web interface: http://<vps>:3000 — reach it over the SSH tunnel below,
# never by opening 3000 to the internet.
ssh -N -L 3000:127.0.0.1:3000 root@<vps>

通过隧道来完成首次运行向导。把管理界面的监听地址设为 127.0.0.1 或隧道地址,绝不要设成 0.0.0.0,并给它设一个够长的密码——这个界面能够改写你网络解析的每一个域名,这让它和解析器本身一样敏感。

然后只设置唯一一个上游: 在“设置 → DNS 设置”中,清空默认列表,填入你的 Unbound 实例。只留一行,别的什么都不要有。如果那个框里还留着某个公共解析器,步骤 01 里做的一切就都白费了。

127.0.0.1:5335

让“Parallel requests”保持关闭——只有一个上游时,它除了让流量翻倍之外什么也做不了——并且关闭“Use private reverse DNS resolvers”,除非你确实运行着一个内部区域。把引导服务器设置为你信任的地址;它们只用来解析上游服务器的主机名,而如果上游直接是一个 IP,那这些引导服务器根本就不会被用到。

屏蔽列表:从小范围开始。 本能的冲动是把列表页面上的所有选项都打开,然后看着计数器涨。要克制住这个冲动。每一份列表,都是别人替你的设备能访问什么做出的一套判断,而它出错的方式又静悄悄又让人摸不着头脑——页面加载了一半,按钮按下去没反应,银行 App 干脆打不开。先从默认的 AdGuard DNS filter 开始,或者选一份维护良好的聚合列表,比如 OISD,用上两周再说,之后再考虑添加更多。留一份允许列表,用它的时候不必有负罪感。

查询日志是最尴尬的部分

你刚刚搭建的这台机器,本来的目的就是阻止别人保留一份记录你设备解析过的每一个域名的清单——而 AdGuard Home 做的第一件事,恰恰就是保留这样一份清单,默认开启,保留九十天。在你调试屏蔽列表期间,它确实很有用,但它也是这台服务器上最敏感的那一个文件。请刻意做出决定:24 小时足以回答“这台设备刚才请求了什么?”,调试完成之后,零就是最诚实的设置,而九十天则是任何人都不该靠放任默认值来做出的决定。“设置 → 通用 → 查询日志”,顺手也把统计数据的保留期限设一下。

把防火墙规则对准这套新的安排——AdGuard Home 现在在隧道接口上占用了 53 端口,而 Unbound 只能通过回环地址访问。公网接口上的情况没有任何变化:上面依旧完全没有 DNS。

步骤 05 · 接入

两种接入方式。隧道是更好的那一种;加密 DNS 是更便携的那一种。

一个谁都连不上的解析器没有用,一个谁都能连上的解析器则是步骤 02 里说的那种失败模式。恰好有两个好答案,而大多数人最终会两个都用上——在家里和笔记本电脑上用隧道,在手机上以及那些会过滤 UDP 的网络上用加密 DNS。

方案 A——通过 WireGuard。 这是两者中更好的一个,也是本指南一直指向WireGuard 指南的原因。端口 53 对互联网始终不可见,查询本身也从不会以任何形式穿越不受信任的网络,而且没有证书需要续期。如果这台 VPS 已经是你的 VPN 出口,那么在每个对端的配置里只需要加一行:

# On the client, in the [Interface] section of the peer config:
DNS = 10.8.0.1

有一个容易咬人的细节:在 Linux 上,wg-quick 是通过 resolvconf 或 systemd-resolved 来设置这个地址的,如果两者都没装,它会悄无声息地什么也不做,而隧道本身却依然能完美地建立起来。隧道建好之后,用 resolvectl status 检查一下,或者读一下 /etc/resolv.conf——一条能正常工作、却还配置着旧解析器的隧道,正是这整套练习想要杜绝的那种泄露。

方案 B——DNS-over-TLS 与 DNS-over-HTTPS。 当设备无法一直挂在隧道上时,就需要它。这两者都靠证书完成身份验证,也就意味着需要一个主机名:把一条 A 记录指向这台 VPS,然后要么让 AdGuard Home 直接持有证书,要么把它放在一个已经替你终结了 TLS 的反向代理后面。监听端口分别是 DoT 用的 853/tcp 和 DoH 用的 443/tcp。

ufw allow 853/tcp                     # DNS-over-TLS
ufw allow 443/tcp                     # DNS-over-HTTPS
# 53/udp is still NOT open on the public interface. It never will be.
ufw status

给 DoH 端点一个猜不到的路径——AdGuard Home 允许你把它部署在 /dns-query/<random> 之下,给每个人分配不同的路径,就意味着可以逐个撤销某个人的访问权限,而不用为此给其他所有人都轮换一遍。这算不上什么强身份验证,但它能让一个公开的监听端点,不至于沦为一个多绕了几步的开放解析器。

客户端 设置项所在位置 所使用的传输方式
Android 9 及更高版本设置 → 网络 → 私人 DNS → 主机名仅支持 DNS-over-TLS
iOS 与 macOS一份已签名的 .mobileconfig DNS 配置描述文件DoH 或 DoT,系统全局生效
Windows 11设置 → 网络 → DNS 服务器分配DoH,按网络适配器设置
Firefox设置 → 隐私 → DNS over HTTPS → 自定义DoH,仅限浏览器本身
路由器(整个网络)WAN DNS,或 DHCP 选项 6明文 DNS——所以要给它套上隧道

路由器这一行,是需要认真想一想的。通过 DHCP 把你的解析器分发出去,能一次性覆盖家里的每一台设备,包括那些根本没有设置界面的设备——但这样一来,从路由器到 VPS 这一跳,就变成了穿越公网的明文 DNS,而这恰恰是你原本想要阻止的事。要么在路由器本身上运行隧道,要么就接受最后一段是不加密的,把递归带来的好处当作这次的收获。

还有最后一项值得了解的配置:一个开启了 DNS-over-HTTPS 的浏览器,会使用它自己的解析器,并悄悄无视系统设置,无论隧道是否存在。Firefox 和 Chrome 在部分地区都默认开启这项功能。要么把浏览器也指向你自己的端点,要么关掉它的 DoH,让它老老实实跟随操作系统的设置。

步骤 06 · 验证

四项检查。其中三项,几乎人人第一次搭建时都会翻车。

1. 它是否对互联网开放? 在一台不在隧道上的机器上运行这条命令——用移动数据的手机、另一台 VPS,什么都行。超时才是正确且理想的结果。如果收到了实际应答,就意味着互联网上的每一个扫描器都会发现它,你应该立刻回到步骤 02。

dig @<public-ip-of-vps> +timeout=3 example.com
# Expected: ";; connection timed out; no servers could be reached"

2. 设备是不是真的在用它? 这是最常翻车的一项检查,而且翻车时毫无察觉:隧道明明是通的,设置看起来也没问题,但操作系统依然在向咖啡馆通过 DHCP 塞给它的那个解析器发问。问一下解析器本身,它认为你是谁,再从另一端确认一遍。

# On the client — which resolver is the system actually using?
resolvectl status | grep -A2 'Current DNS'        # Linux
scutil --dns | grep nameserver                    # macOS

# On the server — does the query actually arrive?
tail -f /var/log/syslog | grep -i unbound &
unbound-control stats_noreset | grep total.num.queries

然后在该设备上打开一个公开的 DNS 泄露测试页面,看看结果。如果它显示的是你的 VPS 服务商,说明路径没问题。如果它显示的是你的 ISP 或某个大型公共解析器,说明隧道上游还有什么东西在替你应答。

3. 校验是否真的开着? 把步骤 03 里那两条 dig 命令再跑一遍,但这次要走完整条链路——客户端、AdGuard Home、Unbound——而不是直接对着 Unbound。那个签名损坏的域名,在这里也必须查询失败。如果 AdGuard Home 的设置里某处还留着第二个上游,问题就会在这里暴露出来。

4. 缓存是否真的在起作用? 正常使用一天之后,命中率应该落在百分之七十到九十之间。如果明显偏低,通常说明缓存太小,或者上游的 TTL 都很短,导致 cache-min-ttl 被忽略了;而在一个繁忙的网络里,命中率明显偏高则是一个好兆头,说明 prefetch 正在起作用。

unbound-control stats_noreset | grep -E 'total.num.(queries|cachehits)'
# hit rate = cachehits / queries

接下来是两个运维层面的决定。 第一:这台机器重启时会发生什么。每一台指向它的设备都会失去域名解析能力,家里每个人都会把这形容成网络坏了。把服务设置为失败后自动重启、开机自启,然后主动重启一次 VPS,看着它自己恢复过来。第二:备用方案。要么在客户端上把某个公共解析器列为备用,并接受故障期间那些查询会脱离你的掌控,要么在另一个堡垒的第二台 VPS 上运行第二个实例。两种做法都站得住脚。但在某个周日晚上 23 点才第一次想到这个问题,就站不住脚了。

systemctl enable unbound AdGuardHome
systemctl show unbound -p Restart -p RestartUSec
reboot        # then, from a client: dig nordbastion.com +short
诚实的部分 · 它做不到什么

它关闭了一条通道。另外四条依然敞开着。

一个私人解析器是实实在在的改善,但它不是隐身斗篷。任何把它当成隐身斗篷来兜售的人,都是在兜售别的什么东西。以下是把上面这一切都做完之后,依然可见的部分,因为搞清楚剩余暴露面的形状,正是真正的防护和自我安慰之间的区别。

连接依然会发生。 查询完成一毫秒之后,你的设备就会向刚刚查到的那个地址发起一条 TCP 连接。任何监视你链路的人,都能看到这个地址。对于拥有独享 IP 的站点来说,这个地址就等于这个站点。加密 DNS 抹去的是那个问题,而不是那个答案。

TLS 握手通常会把站点的名字说出来。 几乎每一次 HTTPS 连接开始时,Server Name Indication 都是以明文发送的,所以一个被动的观察者照样能读到主机名。Encrypted Client Hello 能解决这个问题,但部署得并不均衡——只有当浏览器和站点都支持它时,名称才会被隐藏;只要有一方不支持,名称就不会被隐藏。不要想当然地以为它已经启用。

现在解析器的运营者是你,而你只是一个人。 你的解析器与之对话的每一台权威服务器,看到的都是同一个 IP 地址在询问它所管辖的区。在很多个区里反复出现,这个 IP 就有了一种独特的模式,而这个模式就是一个家庭。这正是第一章里说的匿名集合代价,而唯一能削弱它的,是这个 IP 无法被追溯到某个名字——也就是一台无 KYC、以加密货币支付的 VPS——理想情况下,最好还是你流量本来就已经从中流出的那同一个 IP。

一如既往,服务商能看到这些数据包。 这台 VPS 运行在别人的硬件上、别人的数据中心里。NordBastion 不会检查客户流量,透明度报告和预警金丝雀声明也说明了如果被要求这样做会发生什么——但诚实地从架构角度来说:一台你租来的机器,也是一台别人在物理上能够触及的机器。自建带来的改变在于:不再有产品层面的查询日志,不再有数据分析管道,也不再有一份可以被悄悄修改的隐私政策;有的只是一个把缓存保存在 RAM 里的进程,由你自己配置,运行在一个你刻意选择的法域之内。

你真正买到的东西,精确地说是这样:关于你的行为数据里最丰富、成本又最低的那个来源,不再被一个以收集它为商业模式的一方所收集。这是一个很有限的主张,但它是真实的。当威胁模型需要 Tor 时,这一页上的任何东西都替代不了 Tor——真到那个地步,下面的洋葱服务指南才是接下来诚实的阅读方向。

选型 · 该订购什么

DNS 本身很小。该按机器上还跑着什么别的东西来选配置。

套餐 按月 资源 能轻松承载的内容
Sentinel$3.902 vCPU · 4 GB · 120 GB解析器 + WireGuard,服务一个家庭。正确答案。
Garrison$7.904 vCPU · 8 GB · 240 GB同上,外加一个 Vaultwarden、一个 SearXNG 和一个备份目标
Ravelin$16.908 vCPU · 16 GB · 480 GB一个小团队,二十五个隧道对端,外加其他服务
第二台 Sentinel$3.90在另一个堡垒作为备用解析器,让一次故障不会变成一次网络中断

这张表里的每一档配置,都不是为 DNS 本身选的。一个服务三十台设备的解析器,只需要几百 MB 缓存和不到一个核心的算力,每月产生的流量也就几百 MB。请根据 WireGuard 对端数量、其他服务和磁盘空间来选择档位——解析器搭个顺风车,完全不花额外的钱。

实战笔记 · 六个陷阱

六种翻车的方式。其中五种,都是悄无声息的。

陷阱 01 · 喧闹

开放解析器,以及一周后随之而来的空路由

多放行了一条防火墙规则,或者某一行 access-control 被漏改成了 allow。扫描器几个小时内就能发现这个端口,放大流量随即开始,滥用投诉接踵而至,这个 IP 就此沉寂。默认拒绝,只放行隧道,并从网络之外测试一遍。

陷阱 02 · 徒劳

一个私人解析器,却转发给了一个公共解析器

AdGuard Home 装好了,上游还留在默认列表上,一切运行得漂漂亮亮——而查询日志依旧在某家公司那里持续累积,它的名字就写在设置框里。只设一个上游,并且要是你自己的 Unbound。

陷阱 03 · 隐形

隧道通了,设备却压根没问过你

wg-quick 因为没装 resolvconf 而没能把 DNS 设置推送下去,又或者某个浏览器正在自行其是地使用 DNS-over-HTTPS。一切看起来都正确无误,但每一次查询依然发往你当前所在的那个网络。请从解析器一侧去验证,而不是看客户端的设置界面。

陷阱 04 · 迷惑

屏蔽列表弄坏了银行 App,而这是三周之后才发现的

没有任何故障会主动告诉你这是个 DNS 问题——页面只加载一半,按钮按下去毫无反应。第一天就开启六份激进的列表,会让这种问题彻底没法追查。从一份开始,慢慢再加,留一份允许列表,并保留足够的查询日志,好回答这个问题。

陷阱 05 · 讽刺

九十天的查询日志,默认就在保留

搭建这一整套东西,本来就是为了阻止别人保留这样一份清单,而过滤这一层却开箱即用地把它在磁盘上留存了三个月。请刻意设置保留期限——调试期间设为 24 小时,之后设为零——并记住它覆盖的是网络上的每一个人,不只是你自己。

陷阱 06 · 突然

只是一次重启,全家就没了网络

一旦有别人开始依赖它,解析器就成了基础设施。把两个服务都设为开机自启,设置 Restart=on-failure,主动重启一次来验证,并在真正需要备用方案之前就把它定下来,而不是等到需要的当下才想。

常见问题 · 私有 DNS

问题,已解答。

搭建过程中会遇到的十一个问题——其中两个,决定了这套东西究竟值不值得搭建。

自己搭建解析器,真的比使用 Quad9 或 Cloudflare 更私密吗?

不一样,而且在最重要的那个层面上更好——但诚实的答案分为两半。你得到的:使用递归解析器时,没有任何一方能看到你完整的查询流。根服务器只知道有人查询了 .com,.com 服务器只知道有人查询了 example.com,只有 example.com 的运营者才能知道完整的域名——而它们之中没有一个能看到你接下来查询的上千个域名。相比之下,公共解析器会看到每一条查询,按顺序、带时间戳,永久保存,唯一挡在这份记录和外人之间的,只有它的隐私政策。你失去的:个人解析器的匿名集合只有一个人。从大型公共解析器发出的查询,会和数百万其他人的查询混在一起;而从你自己的解析器发出的查询,毫无疑问就是你的。当解析器的 IP 与你的身份没有关联时,这笔交易就值得做——而一台无 KYC、以加密货币支付的 VPS,给你的正是这个;一个留有你银行卡信息的大型云服务商,给不了你这个。把解析器放在你 VPN 出口所在的同一台 VPS 后面,匿名集合根本不会缩小,因为这个 IP 本来就已经承载着你的浏览流量。

自建 DNS 会让我的网页浏览更快吗?

首次查询一个域名时会略慢一点,之后每次重复查询都会明显更快,总体上大致打平。递归解析器在冷启动时必须走完整条委派链——根、TLD、权威服务器——也就是三到四次往返,而不是查询一个已经缓存了别人查询结果的公共解析器所需的一次往返。一旦缓存预热完成(对一个家庭来说大约需要一天),常见域名会直接从 RAM 中在远低于一毫秒的时间内返回,这时你就领先了。开启 prefetch,让热门记录在过期之前就被刷新,冷启动的情况基本就会消失。唯一真正可能损失性能的地方是 CDN 的地理定位:大型公共解析器会把你网络位置的一个截断版本传给权威服务器(EDNS Client Subnet),以便 CDN 挑选一个就近的边缘节点。Unbound 在设计上不会发送这个信息。如果你的解析器和你在同一地区,这一点不会带来任何可测量的变化。但如果解析器在 Reykjavík,而你在新加坡,有些 CDN 会把你导向欧洲的边缘节点,你会感觉到明显差异。

这件事需要域名吗?

只有在使用加密传输时才需要。通过 WireGuard 隧道传输明文 DNS 完全不需要域名、证书,也不需要任何公网暴露——解析器只监听像 10.8.0.1 这样的私有地址,只回应隧道上的对端。这是本指南中最简单、也最私密的配置。一旦你想要 DNS-over-TLS 或 DNS-over-HTTPS,就需要一个域名,因为这两者都靠证书完成身份验证,而证书是为某个名称签发的。当设备无法一直保持在 VPN 上时——比如一台工作笔记本电脑、一部所在网络屏蔽 UDP 的手机,或是一个不愿维护隧道的家人,就应该选择这种配置。如果你确实要注册域名,请选一个不需要你在别处小心保护的那个身份信息的注册商,并保持 WHOIS 隐私保护开启。

Unbound 还是 AdGuard Home——两个都需要吗?

它们解决的是不同的问题,两者搭配并不是重复。Unbound 是解析器:它从根开始遍历 DNS,校验 DNSSEC 签名,并缓存结果。AdGuard Home 根本不是解析器——它是一个带网页界面和加密监听端口的过滤前端,会把自己不拦截的一切请求转发给上游。单独运行 AdGuard Home,它的上游就是某个公共解析器,这又把你整条查询流放回了别人的日志里。单独运行 Unbound,你得到的是一个真正私密的解析器,但没有屏蔽列表,没有按设备的规则,也没有界面。把两者串联起来——AdGuard 占用 53 端口,Unbound 监听 127.0.0.1:5335 作为它唯一的上游——你就同时拥有了过滤、加密传输,以及背后一个从根开始应答的解析器。如果你想要尽可能小的攻击面,又不在乎广告拦截,单独使用 Unbound 完全是站得住脚的选择,而且它才是整套方案中真正带来隐私价值的那一半。

什么是开放解析器?为什么它是个大问题?

开放解析器是指会回应互联网上任意来源地址查询的递归解析器。这是个大问题,因为 DNS 跑在 UDP 之上,UDP 的源地址极易伪造,而一个很小的查询可以换来一个很大的应答。攻击者向你的服务器发送一个 60 字节的查询,把受害者的地址伪造成来源地址;你的服务器就会老老实实地把大出许多倍的应答发给那个受害者。把这个动作乘以成千上万个开放解析器,就是一次反射放大式 DDoS 攻击,你的服务器就是其中的一支枪。实际情况的发展总是又快又可预测:全网扫描器会在端口 53 开放后的几小时内发现它,攻击流量随即开始,滥用投诉接踵而至,服务商会为了保护自身网络而对这个 IP 实施空路由。这不是纸上谈兵,也并不罕见——这是第一台 DNS 服务器最终出问题最常见的原因,没有之一。修复它只需要一条防火墙规则和一行配置:默认拒绝,只放行你的隧道。

加密 DNS 能不能对我的 ISP 隐藏我访问了哪些网站?

它隐藏的是查询本身,而不是访问行为。DNS-over-TLS 和 DNS-over-HTTPS 意味着你的 ISP 再也看不到一条写着 example.com 的明文查询。但它依然能看到你在一毫秒之后发起的连接:目的 IP 地址,以及——除非两端都支持 Encrypted Client Hello——TLS 握手中以明文出现的服务器名称。对于使用独享 IP 的站点,仅凭地址就能识别出它;对于藏在大型 CDN 后面的站点,地址是共享的,这时 SNI 就成了泄露信息的关键。所以加密 DNS 提高了被动记录的成本,消除了成本最低、最容易利用的监控面,但它并不会让你的浏览行为变得不可见。如果威胁模型是你的 ISP 或某个恶意网络能看到一切,那么应对它的工具是隧道——加密 DNS 堵上的,是当操作系统在隧道之外仍然使用 ISP 解析器时,仅靠 VPN 依然可能存在的那个泄露点。

VPS 需要多大?

比大多数人想的要小得多。DNS 应答只有几百字节,每次查询的工作量不过是一次哈希查找,所以对一个家庭或小团队来说,解析器几乎一直处于空闲状态。一台 Sentinel——2 vCPU、4 GB、每月 $3.90——就能服务几十台设备,负载均值都不会离开零;而这 4 GB 内存里的大部分都可以用来做缓存,这也是唯一真正能提升体验的资源。给 Unbound 分配几百 MB 的消息缓存和 RRset 缓存,命中率就能维持在百分之八十到九十之间。需要加大配置的原因从来都不是 DNS 本身:而是同一台机器可能还要承载 WireGuard 隧道,运行一个 Vaultwarden,或者存放一个备份仓库。带宽完全不是问题——一个繁忙的家庭解析器每月产生的流量也就几百 MB。

VPS 重启或解析器挂掉时会发生什么?

每一台配置成使用它的设备都会停止解析域名,用的人会把这形容成“网络断了”。这是自建 DNS 真正的运维代价,应该是一个事先做出的决定,而不是事后才发现的意外。三种防护手段,按投入的力气递增。在客户端上配置一个备用解析器——比如 Quad9 或 dns0.eu 这类公共解析器——并接受在故障期间这些查询会脱离你的掌控;这是大多数人应该做的。或者,在另一个堡垒的第二台 VPS 上运行第二个实例,同时列出两者,代价是再花一台 $3.90 服务器的钱,把一切都留在自己手里。又或者,在隧道上让每个客户端都运行一个很小的本地缓存,让短暂的故障变得无感。无论选哪一种,都要在该 unit 上设置 Restart=on-failure,让它开机自启,并主动测试一次重启——一个不能自行恢复的解析器,总会挑最糟糕的那个时刻来证明这一点。

屏蔽列表会不会把东西弄坏?

是的,偶尔会,而你如何应对,决定了这套方案能用下去,还是两周后就被你拆掉。屏蔽列表是由人做出判断维护的,时不时总会有一次判断误伤了你需要的东西:银行的反欺诈接口、一个物流追踪小组件、一个要跳转经过某个分析域名的登录流程。这种故障很让人困惑,因为它看起来根本不像是 DNS 问题——页面只加载了一半,或者按钮按下去毫无反应。三个习惯可以解决它。从一份保守的列表开始,而不是一次性上六份激进的列表。留一份允许列表,需要就加,不必有负罪感。并且让查询日志保持开启,久到足以回答“这台设备刚才请求了什么,我们又拒绝了什么?”——而这也正是你会发现的一个事实:一台过滤解析器本身就是一台会记录一切的设备,它的保留期限设置,其实是你在替网络上的每一个人做出的一项隐私决定。

我能把这个解析器分享给家人或朋友用吗?

可以,而且这正是搭建它的最佳理由之一——一台 $3.90 的机器就能让整个家庭或几个朋友的 DNS 都变得干净。两条规则能让这样做保持安全。第一,只通过隧道或经过身份验证的加密端点来分享,绝不要向全世界开放 53 端口;使用 DNS-over-HTTPS,你可以给每个人分配一个不同的路径,这样访问权限就能单独撤销,AdGuard Home 也就能对每个客户端应用不同的过滤策略。第二,要想清楚自己接下的是什么:现在你手上握着每一个使用它的人的查询日志。把保留期限设为零或一天,并把这个设置告诉他们,同时记住:如果你保护的终究是你自己的隐私,那么一个被五个人共用的解析器,也意味着其中任何一条查询都更难归到某一个具体的人身上。

在 VPS 上运行 DNS 解析器,NordBastion 会介意吗?

完全不会——一个私有的、受访问控制的解析器是很正常的负载,可接受使用政策对此没有任何异议。这项政策真正在意的,也是每一个负责任的网络都在意的,是一个开放解析器被用作针对第三方的放大反射器,因为攻击流量会离开我们的网络,落到别人头上。按照步骤 02 去做,这种情况就永远不会发生:默认拒绝,只放行隧道,其余一律限流。如果你想有意运行一个面向公众的解析器,作为提供给他人的服务,这是一场值得提前进行的对话,而不是等第一份滥用投诉到来之后才去谈——相关的技术要求(响应速率限制、禁用 ANY、监控)都已经很成熟,问题从来不是有意运行一个公共解析器,而是无意中运行了一个。

获取这台机器

一个属于你自己的解析器,架在一个不是你名字的 IP 上。无 KYC,加密货币付款。

Sentinel——2 vCPU、4 GB,每月 $3.90,可选 Helsinki、Stockholm、Oslo 或 Reykjavík。足够运行解析器、它应该藏身其后的 WireGuard 隧道,以及任何你决定不再花钱租用的其他东西。注册无需邮箱,无需身份证件,也不会有任何产品层面的记录,去记下你查询过什么。

最后审核 · 2026-10-12 · 来源 · Unbound 与 AdGuard Home 文档,RFC 9156(QNAME 最小化),RFC 7858(DNS-over-TLS),RFC 8484(DNS-over-HTTPS),systemd-resolved(8) · 周期 · 每年

私密托管,源自北欧。