Canary Workshop

Whatever is worth doing at all is worth doing well

Debian 9下使用Bind9的分地区解析功能

自从去年CloudXNS翻车后,就逐渐将DNS挪到了HE.net,后来又迁移到自建的DNS上。但按国家、大洲甚至时区等解析的功能始终无法实现,于是就折腾了一下Bind9的GeoIP功能,总结了一下其中的坑,在这里记录详细的配置过程。

期望的目标:

  • 对于来自亚洲、欧洲、北美洲的请求分别返回服务器A、B、C的地址
  • 对于来自中国的请求无视上面的亚洲设置,直接返回服务器D的地址
  • 对于来自其他地区的请求,返回服务器E的地址
  • 措施一定的抗攻击措施

以下步骤在全新的Debian 9 x64上测试通过。

预设

在此使用example.com作为示范域名,该域名本身已做好Glue Record且指向下面设置的DNS服务器。下面使用的配置都是最精简的设置,也没有设置主从,仅供示范。

IP数据库使用Debian的geoip-database-contrib包亦即MaxMind的IP数据库,也可以使用IPIP的库替换。另外事实上Bind9还支持使用ACL格式的IP数据库,这样可以使用更多提供商的数据库(如果他们不提供dat格式的话)。但设置略有不同也稍微麻烦一些,没有特殊需求并用不到,因此这里不再提及。

安装

很简单,直接使用源中的:

1
2
sudo apt update
sudo apt install bind9 geoip-database-contrib

可以确认一下是否有GeoIP模块:

1
named -V | grep geoip #事实上用不到,源中的已编译该模块

配置

首先编辑/etc/bind/named.conf.options文件写入IP数据库信息以及配置分区解析规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#在options {}节以外的地方:
acl "A" { geoip continent "AS"; }; #亚洲
acl "B" { geoip continent "EU"; }; #欧洲
acl "C" { geoip continent "NA"; }; #北美洲
acl "D" { geoip country "CN"; }; #中国
#其他地区解析到E服务器不需要在此设置
#在options {}节中:
...
options {
...
geoip-directory "/usr/share/GeoIP";
...
};
...

事实上这里还可以使用地区/城市/时区等其他MaxMind库支持的标准进行分类。

接着编辑相应的zonefile。这里,对于每个服务器单独编写一个zonefile,除了要分区解析的域名外的内容都保持一致,需要分区解析的域名也正常填写IP地址即可。然后分别保存为db.A~E.example.com

最后是配置启用分区解析,编辑/etc/bind/named.conf.default-zones。需要提醒的是,下面使用了view {}块,则该文件中所有的记录都必须被列入view {}块。下面直接给出完整示例(注释行都去掉了):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
view "CN" {
match-clients { CN; };
zone "example.com" {
type master;
file "/etc/bind/zones/db.E.example.com";
};
};
view "A" {
match-clients { A; };
zone "example.com" {
type master;
file "/etc/bind/zones/db.A.example.com";
};
};
view "B" {
match-clients { B; };
zone "example.com" {
type master;
file "/etc/bind/zones/db.B.example.com";
};
};
view "C" {
match-clients { C; };
zone "example.com" {
type master;
file "/etc/bind/zones/db.C.example.com";
};
};
view "other" {
zone "example.com" {
type master;
file "/etc/bind/zones/db.E.example.com";
};
};
zone "." {
type hint;
file "/etc/bind/db.root";
};
zone "localhost" {
type master;
file "/etc/bind/db.local";
};
zone "127.in-addr.arpa" {
type master;
file "/etc/bind/db.127";
};
zone "0.in-addr.arpa" {
type master;
file "/etc/bind/db.0";
};
zone "255.in-addr.arpa" {
type master;
file "/etc/bind/db.255";
};
};

其中,1~7行是中国地区的设置;8~28行分别是亚洲,欧洲,北美洲的设置;29~34行是其他地区的缺省设置,再向下是自带的一些本地域。

尤其需要的是,Bind9在匹配规则时,是按照这个文件中的顺序匹配的,也就是,先判断是否符合中国地区,如果符合则直接使用,不符合继续向下直到缺省。这意味着,如果亚洲地区设置在中国地区之前,那么中国地区的设置完全不会生效。

安全设置

众所周知DNS系统经常是各类攻击的目标,也经常被利用来执行反射攻击。这里做两点设置来阻止洪水攻击和反射攻击(当然,无法做到完全防御,以下设置只能提供一定的防御效果):

编辑/etc/bind/named.conf.options文件

1
2
3
4
5
6
7
8
9
10
#在options {}部分:
...
options {
...
recursion no; #拒绝递归解析请求
rate-limit {
responses-per-second 10; #设置频率限制
};
...
};

rate-limit模块还有更多配置参数,具体可以参阅官方文档

最后:

1
2
named-checkconf #检查配置文件
sudo bind9 reload #重新载入配置文件

至此,分区解析的Bind9就配置完毕了。可以实际测试一下效果确认运行正常。