;

林师傅的贼窝 每天都被折磨的想死

递归DNS服务器bind9

前言

DNS 想必大家并不陌生,但是在主机上设置好 DNS 服务器后,比如 223.5.5.5,那么 223.5.5.5 是怎么找到目标域名的 ip 的呢?全世界那么多网站,要是全依赖 223.5.5.5 的话,那岂不是要炸了?所以肯定是分布式的存储,那么问题来了,DNS 的分布式存储结构是什么样的呢?我们先来看一幅图:

dns访问流程图

注意到这张图要访问的www.baidu.com. 注意这个"." 很重要,其实在所有的网站后面都会有这个"." ,这个其实就是根域名,根域名指向的服务器其实就是根服务器,他是 13 个固定的 ip 地址,在 DNS 请求的时候 223.5.5.5 首先会询问根服务器,根服务器会告知.com. 服务器的 ip,之后 223.5.5.5 会问.com. 服务器,.com. 服务器存储着诸如 baidu.com. 的 DNS 服务器的 ip,之后 223.5.5.5 会问存有 baidu.com. 的 DNS 服务器,这些服务器最终返回存储有 www.baidu.com. 的 ip 的 DNS 服务器,最终 223.5.5.5 去问这个服务器得到 www.baidu.com. 的 ip,这就可以完成域名解析了.

在这个过程中,PC 发给 223.5.5.5 的请求叫做转发,而 223.5.5.5 的查询过程叫做递归,其中最终查到的www.baidu.com. 的 DNS 服务器就叫做权威 DNS 服务器.

递归服务器的优势

其实从上面的结果中不难看出,递归查询需要查询多轮,时间上肯定会比转发要长,那我们搭建递归 DNS 服务器有什么意义呢?我们不妨退一步,换个问题,都用公共 DNS 的话,选择哪个 DNS 有什么区别吗?首先我们不考虑延迟这个问题,就返回的解析结果来考虑,不同的 DNS 服务器可能返回不一样的结果。一个常见的场景是,很多域名使用了 CDN,解析结果根据所在地 IP 来返回,比如你是广东电信,返回在广东电信的服务器地址,而在北京联通就返回北京联通的地址,就结果而言,在东南沿海的你显然不希望查询到了一个新疆 CDN 的 ip。虽然有 ECS 这种协议存在(DNS ECS 是 DNS 协议的一个扩展,它允许递归 DNS 解析器在发送给权威 DNS 服务器的请求中包含终端用户 IP 地址数据的部分),但首先它是用在递归 DNS 上的,也就是部署在 114 这种公共服务器上的,其次你请求的域名所在的权威 DNS 要支持 ECS 协议才可以,说白了这个协议对局域网用户来说没什么用,因为你什么都做不了,甚至还可能有隐私泄露的问题。那如果使用自己的递归 DNS 服务器,即把 114 这样的 DNS 服务器装你家里。这样你的每个请求都非常的原生地到达了权威 DNS 服务器,权威 DNS 根据你自建的递归 DNS 的 ip 返回离你最近的 CDN 的 ip,其结果可谓是准确中的准确。你再也不需要对 DNS 服务器进行收集和测速,也不需要对解析结果进行测速,本来就是权威 DNS 根据你家里递归 DNS 发起递归请求的 ip 返回的最优结果,测了也没意义。

还有一个不太常见的问题就是,公共 DNS 服务器值得信任吗?虽然有 DNSSEC 这种东西存在,但它并不能解决 DNS 劫持和污染,你除了知道它结果可能是错的之外什么都做不了。即使 DNS 结果值得信任,但你的查询记录可能会被公共 DNS 服务器记录日志,也就是可能会造成一定的隐私问题。一个更加离谱的可能是,如果公共 DNS 服务器被攻击,那你的查询结果可能被引导到恶意网站,就算这种可能性比较低,公共 DNS 服务器也有故障的时候,你可能又开始考虑:我能保证谁的公共 DNS 服务器 100%稳定性?是的,不能保证。但当你拥有一台属于自己的递归 DNS 服务器的时候,稳定性和隐私问题将由你自己掌控。

搭建内网递归 DNS 服务器

这里选用 bind9 作为递归服务器,因为 apt 软件源中就有 bind9,而且他还可以配置为权威 DNS 服务器,所以非常方便。

apt-get install -y bind9*
# 安装好之后配置文件在 /etc/bind,目录结构如下
/etc/bind
|-- bind.keys
|-- db.0
|-- db.127
|-- db.255
|-- db.empty
|-- db.local
|-- named.conf
|-- named.conf.default-zones
|-- named.conf.local
|-- named.conf.options
|-- rndc.key
`-- zones.rfc1918

主文件为name.conf ,其配置文件格式为 C 风格

// This is the primary configuration file for the BIND DNS server named.
//
// Please read /usr/share/doc/bind9/README.Debian for information on the
// structure of BIND configuration files in Debian, *BEFORE* you customize
// this configuration file.
//
// If you are just adding zones, please do that in /etc/bind/named.conf.local

include "/etc/bind/named.conf.options"; // 监听配置文件
include "/etc/bind/named.conf.local"; // 自定义域名文件
include "/etc/bind/named.conf.default-zones"; // 存储根服务器IP以及回环地址内网网段的信息

配置监听文件

options {
    // 缓存文件夹
	directory "/var/cache/bind";

	// If there is a firewall between you and nameservers you want
	// to talk to, you may need to fix the firewall to allow multiple
	// ports to talk.  See http://www.kb.cert.org/vuls/id/800113

	// If your ISP provided one or more IP addresses for stable
	// nameservers, you probably want to use them as forwarders.
	// Uncomment the following block, and insert the addresses replacing
	// the all-0's placeholder.

	// forwarders {
	//  	223.5.5.5;
	// };

	//========================================================================
	// If BIND logs error messages about the root key being expired,
	// you will need to update your keys.  See https://www.isc.org/bind-keys
	//========================================================================

    // 开启DNSSEC验证,讲道理在内网里面自己用的话这个没啥意义
	dnssec-validation auto;
	listen-on { localhost; }; // 监听本地所有IP
	allow-query { any; }; // 允许所有IP的DNS请求
	recursion yes; // 开启递归查询
	allow-recursion { any; }; // 允许所有IP都使用递归查询
	recursive-clients 1024; // 最大递归查询客户端数量
};

配置自定义域名文件

如果有一些特殊需求,比如内网很多服务,其实是可以直接用内网 DNS 服务器避免回环问题的,而且诸如谷歌 TV 激活的 ntp 服务器也可以很方便用内网 DNS 服务器进行 host 劫持,我在这里举一个例子 在name.conf.local 添加如下

// rfc1918主要包含是内网的ip地址段,也就是如果需要由IP解析域名的时候,通过配置这个文件可以马上得到IP所对应的域名而不需要到公网上去反向解析域名,本来就是内网部署,到公网去反向解析简直抽象,这个根据情况可以放开注释
// include "/etc/bind/zones.rfc1918";

zone "lzhlovelcl.top" {
	type master;
	file "/etc/bind/db.example.com";
};

添加好之后,创建配置文件db.example.com

touch /etc/bind/db.example.com

添加如下内容,各参数意义如下

  1. SOA: 起始记录
  2. NS: 名称服务器记录
  3. A: ipv4 记录
  4. IN: 网络记录
  5. TTL: 存活时间
$TTL	604800 # 设置变量
# 注意邮箱的@的符用.代替,@在这里表示所有域名起始记录的解析.
@	IN	SOA	lzhlovelcl.top. 1986.qq.com. (
			      2		; Serial
			 604800		; Refresh
			  86400		; Retry
			2419200		; Expire
			 604800 )	; Negative Cache TTL
;
@	        IN	NS	dns.example.com. # 指定example.com由dns.example.com解析
dns         IN  A   192.168.50.6 # 指定dns.example.com的IP
@	        IN	A	192.168.50.4 # 指定example.com的IP
plex	    IN	A	192.168.50.4 # 指定plex.example.com的IP
lucky	    IN	A	192.168.50.4
portainer	IN	A	192.168.50.4
jellyfin	IN	A	192.168.50.4
alist	    IN	A	192.168.50.4
aria2	    IN	A	192.168.50.4

这里只是简单示范,更具体的欢迎看这个大佬的文章

# 检测配置文件是否正确
named-checkconf
# 默认监听53端口,确保没有被占用
systemctl enable --now named
nslookup whoami.03k.org 223.5.5.5 # 测试223.5.5.5换成你装了bind9的主机的IP
# Server:         223.5.5.5
# Address:        223.5.5.5#53

# Non-authoritative answer:
# Name:   whoami.03k.org
# Address: 47.103.54.202 这个就是递归服务器连接到权威DNS的ip,可以复制进浏览器看看ip属地
# ;; Got SERVFAIL reply from 223.5.5.5

至此部署完毕,这个项目其实也可以和 singbox 联动形成一个国内国外通吃的超级 dns 服务器,有空再说吧

DNS 递归DNS Bind9

Bind9教程