BIND 9 私有 DNS 服务搭建与生产实践:从 named.conf 到主从、递归、安全与排障

欢迎你来读这篇博客。这篇文章会从 BIND 9 是什么、私有 DNS 该怎么设计、如何在 RHEL/Rocky/Alma/CentOS 系统上落地、如何配置正向区/反向区/主从同步/递归解析、安全加固和排障 这些角度完整讲一遍。

很多旧教程能跑,但不一定适合今天直接照抄。比如 CentOS 7 时代常见的 dnssec-enable yes;、随手打开递归、只配置 allow-query 不配置 allow-recursion、把 allow-transfer 写在全局并放得过宽,这些在新版本和生产环境里都容易埋坑。DNS 是基础设施,配置错了不是“解析慢一点”,而是全公司开始进入玄学网络时代。

序言

BIND,全称 Berkeley Internet Name Domain,是历史最悠久、使用最广泛的 DNS 软件之一。它既可以做:

  • 权威 DNS:回答某个区域的数据,比如 gitlab.corp.example -> 192.168.9.21
  • 递归/缓存 DNS:帮客户端递归解析公网域名,比如 www.baidu.comgithub.com
  • 转发 DNS:把非本地区域请求转发到上游 DNS,比如运营商、公司网关、公共解析器。
  • 主从 DNS:主服务器维护 zone 文件,从服务器通过 zone transfer 同步,提升可用性。
  • Split DNS / View:同一个域名,内网和外网看到不同结果。

这篇文章的目标不是“让 named 能启动就完事”,而是写一套能长期维护的工程化配置。毕竟 DNS 配置文件一旦写乱,后续排查就像在 /etc 目录里考古,越挖越像自己上辈子写的。

正文

1. DNS 与 BIND 的核心概念

在动手前,先把几个词分清楚。

1.1 解析器 Resolver

客户端上的解析逻辑叫 resolver,比如 Linux 的 glibc resolver、systemd-resolved、macOS resolver。客户端一般不会自己从根 DNS 一路递归查到权威服务器,它会把请求交给配置好的 DNS 服务器。

1.2 递归 DNS Recursive Resolver

递归 DNS 接收客户端请求后,如果自己没有缓存,就从根服务器、顶级域服务器、权威服务器一路查下去,再把最终结果返回给客户端。

在内网里,BIND 可以充当递归 DNS:

1
Client -> BIND -> Root/TLD/Authoritative DNS -> BIND Cache -> Client

1.3 权威 DNS Authoritative DNS

权威 DNS 只负责自己管理的 zone。比如你在 BIND 中配置:

1
2
3
4
zone "corp.example" IN {
type primary;
file "zones/db.corp.example";
};

那么这台 BIND 就是 corp.example 这个 zone 的权威服务器。客户端查询 gitlab.corp.example 时,BIND 会直接根据 zone 文件回答。

1.4 Zone 与 Record

一个 zone 是一段命名空间的管理边界,例如:

  • 正向区:corp.example
  • 反向区:9.168.192.in-addr.arpa

常见记录如下:

类型 作用 示例
SOA zone 起始授权记录,包含主 DNS、负责人、序列号等 @ IN SOA ns1.corp.example. hostmaster.corp.example.
NS 指定 zone 的名称服务器 @ IN NS ns1.corp.example.
A 域名到 IPv4 gitlab IN A 192.168.9.21
AAAA 域名到 IPv6 www IN AAAA 2001:db8::30
CNAME 别名 api IN CNAME gitlab
PTR IP 到域名,反向解析 21 IN PTR gitlab.corp.example.
MX 邮件服务器 @ IN MX 10 mail.corp.example.
TXT 文本记录,常用于验证、SPF 等 @ IN TXT "v=spf1 ..."
SRV 服务发现 _ldap._tcp IN SRV 0 5 389 ldap01.corp.example.

几个非常容易踩坑的小点:

  1. zone 文件中的完整域名最好写尾部点号,比如 ns1.corp.example.。没有尾部点号时,BIND 会把当前 zone 自动拼上去。
  2. CNAME 记录所在的名称一般不要再同时挂其他记录。
  3. 修改 zone 文件后必须增加 SOA serial,否则从服务器可能不会同步。
  4. 反向解析不是自动生成的,需要单独配置反向区和 PTR 记录。

2. 私有 DNS 的域名选择

内网域名不要随便拍脑袋。

推荐优先级:

  1. 企业生产环境:使用自己真实拥有的域名的子域,例如 corp.example.comlan.example.comidc.example.com
  2. 家庭网络:可以考虑 home.arpa
  3. 教程或实验环境:使用 corp.exampleexample.test 这类示例域。

不建议:

  • 不建议使用 .local 做普通单播 DNS,因为 .local 常被 mDNS 使用,可能和 macOS、Linux、Windows 的本地发现机制冲突。
  • 不建议编造 .lan.home.company 这类未注册 TLD,容易泄漏到上游 DNS,也可能未来和真实 TLD 冲突。

本文使用 corp.example 作为演示域名,IP 网段使用 192.168.9.0/24。真实环境请替换成自己的域名和网段。

3. 本文目标架构

我们搭建一套主从私有 DNS:

flowchart LR
    C1[Client: 192.168.9.101] --> NS1[ns1.corp.example\n192.168.9.10\nPrimary BIND]
    C1 --> NS2[ns2.corp.example\n192.168.9.11\nSecondary BIND]
    NS1 -- AXFR/IXFR + TSIG --> NS2
    NS1 --> U[Upstream DNS]
    NS2 --> U

角色规划:

主机 角色 IP 说明
ns1.corp.example Primary DNS 192.168.9.10 维护 zone 文件
ns2.corp.example Secondary DNS 192.168.9.11 从 ns1 同步 zone
gitlab.corp.example 应用服务 192.168.9.21 示例 A 记录
api.corp.example 应用别名 CNAME -> gitlab 示例 CNAME

功能目标:

  • 内网主机可以解析 *.corp.example
  • 内网主机可以通过 BIND 递归解析公网域名。
  • ns2 自动从 ns1 同步 zone。
  • 限制递归只允许内网客户端使用。
  • 限制 zone transfer 只允许 ns2,并使用 TSIG 签名。
  • 提供常用排障命令与生产加固建议。

4. 版本与系统差异

本文主要以 RHEL 9 / Rocky Linux 9 / AlmaLinux 9 兼容系为主,CentOS 7 差异会单独说明。Debian/Ubuntu 路径也会给出对照。

项目 RHEL/Rocky/Alma/CentOS Debian/Ubuntu
服务名 named bind9
主配置 /etc/named.conf /etc/bind/named.conf
options 配置 通常直接在 /etc/named.conf /etc/bind/named.conf.options
本地区域配置 /etc/named.rfc1912.zones 或自定义 include /etc/bind/named.conf.local
zone 目录 /var/named /var/cache/bind/etc/bind/zones
运行用户 named bind
工具包 bind-utils bind9utilsdnsutils

生产建议:

  • 新项目优先用系统发行版维护的安全更新包,或者使用 ISC 官方支持分支。
  • 如果你追求稳定,不要随便上 development branch。
  • 配置从旧版本迁移到 BIND 9.18/9.20 时,一定跑 named-checkconfnamed-checkzone
  • 不要盲目复制旧教程中的所有参数。旧配置里有些参数在新版本里已经默认、废弃或行为改变。

5. 安装 BIND

5.1 RHEL 9 / Rocky Linux 9 / AlmaLinux 9

1
2
sudo dnf install -y bind bind-utils
named -v

启动并设置开机自启:

1
2
sudo systemctl enable --now named
sudo systemctl status named

开放防火墙:

1
2
3
sudo firewall-cmd --permanent --add-service=dns
sudo firewall-cmd --reload
sudo firewall-cmd --list-services

DNS 使用 TCP/UDP 53。很多人只开 UDP 53,结果小响应没问题,大响应、zone transfer、部分 DNSSEC 场景开始抽风。记住:UDP 53 和 TCP 53 都要考虑

5.2 CentOS 7

1
2
3
4
sudo yum install -y bind bind-utils
sudo systemctl enable --now named
sudo firewall-cmd --permanent --add-service=dns
sudo firewall-cmd --reload

CentOS 7 自带 BIND 版本较老,很多教程也是围绕它写的。能跑不代表适合长期生产。公网暴露、递归控制、DNSSEC、TSIG 这些地方要格外谨慎。

5.3 Debian / Ubuntu

1
2
3
4
sudo apt update
sudo apt install -y bind9 bind9utils dnsutils
sudo systemctl enable --now bind9
sudo systemctl status bind9

Ubuntu 使用 UFW 时:

1
2
3
sudo ufw allow 53/tcp
sudo ufw allow 53/udp
sudo ufw reload

6. Primary DNS 配置

下面以 ns1 = 192.168.9.10 为例。

6.1 目录准备

1
2
3
4
sudo mkdir -p /var/named/zones
sudo mkdir -p /etc/named.keys
sudo chown root:named /var/named/zones /etc/named.keys
sudo chmod 750 /var/named/zones /etc/named.keys

如果 SELinux 开启,并且你使用了自定义目录,建议设置上下文:

1
2
3
sudo dnf install -y policycoreutils-python-utils
sudo semanage fcontext -a -t named_zone_t "/var/named/zones(/.*)?"
sudo restorecon -Rv /var/named/zones

如果只是放在 /var/named 标准目录下,通常系统策略已经处理好了。

6.2 生成 TSIG Key

主从同步建议使用 TSIG。不要只靠 IP 放行。IP 放行是门卫看脸,TSIG 至少还要验身份证。

在 ns1 上执行:

1
2
3
sudo tsig-keygen -a hmac-sha256 ns1-ns2. | sudo tee /etc/named.keys/ns1-ns2.key
sudo chown root:named /etc/named.keys/ns1-ns2.key
sudo chmod 640 /etc/named.keys/ns1-ns2.key

/etc/named.keys/ns1-ns2.key 安全复制到 ns2 的同一路径:

1
sudo scp /etc/named.keys/ns1-ns2.key root@192.168.9.11:/etc/named.keys/ns1-ns2.key

注意:TSIG key 是共享密钥,不能放到 Git 仓库里,更不能贴到工单截图里。

6.3 /etc/named.conf 主配置

编辑:

1
sudo vi /etc/named.conf

示例配置如下:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
acl "trusted" {
127.0.0.1;
localhost;
192.168.9.0/24;
};

include "/etc/named.keys/ns1-ns2.key";

options {
listen-on port 53 { 127.0.0.1; 192.168.9.10; };
// 如果你已经规划了 IPv6,可以显式写 IPv6 地址;不用 IPv6 时可关闭监听。
listen-on-v6 port 53 { none; };

directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
secroots-file "/var/named/data/named.secroots";
recursing-file "/var/named/data/named.recursing";

// 权威查询:本例只服务内网。如果是公网权威 DNS,allow-query 通常会放开到 any。
allow-query { trusted; };

// 递归解析:只允许可信内网,避免变成 Open Resolver。
recursion yes;
allow-recursion { trusted; };
allow-query-cache { trusted; };

// 主从传输默认先关闭,在具体 zone 中再精确放开。
allow-transfer { none; };

// 上游 DNS,可替换为公司出口 DNS、运营商 DNS 或可信公共 DNS。
// 如果希望 BIND 自己从 root 递归,可删除 forwarders。
forwarders {
223.5.5.5;
119.29.29.29;
1.1.1.1;
8.8.8.8;
};
// 打开后,如果 forwarder 不响应,不再回退到根递归。
// 内网出口策略严格时建议开启;实验环境可按需关闭。
forward only;

// BIND 9.20 推荐大多数 resolver 使用默认 auto。
dnssec-validation auto;

// 隐藏真实版本,降低被按版本打洞的概率。
version "not available";

// 视内存情况设置,避免缓存吃掉过多内存。
max-cache-size 512M;

// 若使用自定义私有 TLD 且遇到 DNSSEC 验证问题,可谨慎使用 validate-except。
// validate-except { "corp.example"; };
};

logging {
channel named_default {
file "/var/named/data/named.default.log" versions 7 size 20m;
severity info;
print-time yes;
print-severity yes;
print-category yes;
};

category default { named_default; };
category general { named_default; };
category security { named_default; };
category config { named_default; };
};

zone "." IN {
type hint;
file "named.ca";
};

include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
include "/etc/named/named.conf.local";

创建本地 zone 配置文件:

1
2
sudo mkdir -p /etc/named
sudo vi /etc/named/named.conf.local

写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
zone "corp.example" IN {
type primary;
file "zones/db.corp.example";
allow-query { trusted; };
allow-transfer { key ns1-ns2.; 192.168.9.11; };
notify yes;
also-notify { 192.168.9.11 key ns1-ns2.; };
allow-update { none; };
};

zone "9.168.192.in-addr.arpa" IN {
type primary;
file "zones/db.192.168.9";
allow-query { trusted; };
allow-transfer { key ns1-ns2.; 192.168.9.11; };
notify yes;
also-notify { 192.168.9.11 key ns1-ns2.; };
allow-update { none; };
};

这里有几个设计选择:

  • allow-transfer 放在 zone 里,而不是全局放开。
  • allow-transfer 同时限制 TSIG key 和 ns2 IP。
  • notify yes 让主服务器 zone 更新后通知从服务器。
  • allow-update { none; }; 禁止动态更新,避免客户端乱写 DNS。

7. 正向 Zone 文件

创建正向解析文件:

1
sudo vi /var/named/zones/db.corp.example

内容如下:

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
$TTL 1h
@ IN SOA ns1.corp.example. hostmaster.corp.example. (
2026061601 ; serial,建议使用 YYYYMMDDNN
1h ; refresh,从服务器多久检查一次
15m ; retry,失败后多久重试
7d ; expire,多久联系不上主服务器后认为 zone 过期
10m ; negative cache TTL
)

; Name servers
@ IN NS ns1.corp.example.
@ IN NS ns2.corp.example.

; DNS servers
ns1 IN A 192.168.9.10
ns2 IN A 192.168.9.11

; Application hosts
gitlab IN A 192.168.9.21
jenkins IN A 192.168.9.22
harbor IN A 192.168.9.23

; Alias
api IN CNAME gitlab.corp.example.

; Optional mail example
mail IN A 192.168.9.30
@ IN MX 10 mail.corp.example.

; Optional text record
@ IN TXT "private dns zone for corp.example"

设置权限:

1
2
3
sudo chown root:named /var/named/zones/db.corp.example
sudo chmod 640 /var/named/zones/db.corp.example
sudo restorecon -v /var/named/zones/db.corp.example

检查:

1
sudo named-checkzone corp.example /var/named/zones/db.corp.example

看到 OK 才继续。

8. 反向 Zone 文件

192.168.9.0/24 的反向 zone 是:

1
9.168.192.in-addr.arpa

创建:

1
sudo vi /var/named/zones/db.192.168.9

内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$TTL 1h
@ IN SOA ns1.corp.example. hostmaster.corp.example. (
2026061601 ; serial
1h ; refresh
15m ; retry
7d ; expire
10m ; negative cache TTL
)

@ IN NS ns1.corp.example.
@ IN NS ns2.corp.example.

10 IN PTR ns1.corp.example.
11 IN PTR ns2.corp.example.
21 IN PTR gitlab.corp.example.
22 IN PTR jenkins.corp.example.
23 IN PTR harbor.corp.example.
30 IN PTR mail.corp.example.

设置权限并检查:

1
2
3
4
5
sudo chown root:named /var/named/zones/db.192.168.9
sudo chmod 640 /var/named/zones/db.192.168.9
sudo restorecon -v /var/named/zones/db.192.168.9

sudo named-checkzone 9.168.192.in-addr.arpa /var/named/zones/db.192.168.9

9. 检查并启动 Primary

检查主配置:

1
sudo named-checkconf

如果希望连 zone 一起加载检查:

1
sudo named-checkconf -z

重启:

1
2
sudo systemctl restart named
sudo systemctl status named

看日志:

1
2
sudo journalctl -u named -xe
sudo tail -f /var/named/data/named.default.log

测试正向解析:

1
2
3
dig @192.168.9.10 gitlab.corp.example A +short
dig @192.168.9.10 api.corp.example CNAME +short
dig @192.168.9.10 corp.example SOA +short

测试反向解析:

1
dig @192.168.9.10 -x 192.168.9.21 +short

测试公网递归:

1
2
dig @192.168.9.10 www.baidu.com A +short
dig @192.168.9.10 github.com A +short

测试是否隐藏版本:

1
dig @192.168.9.10 version.bind chaos txt

10. Secondary DNS 配置

下面以 ns2 = 192.168.9.11 为例。

10.1 安装与目录

1
2
3
4
5
6
7
sudo dnf install -y bind bind-utils
sudo mkdir -p /var/named/slaves
sudo mkdir -p /etc/named.keys
sudo chown named:named /var/named/slaves
sudo chmod 750 /var/named/slaves
sudo chown root:named /etc/named.keys
sudo chmod 750 /etc/named.keys

把 ns1 上的 TSIG key 复制到 ns2:

1
2
sudo chown root:named /etc/named.keys/ns1-ns2.key
sudo chmod 640 /etc/named.keys/ns1-ns2.key

10.2 ns2 的 /etc/named.conf

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
56
57
58
59
60
acl "trusted" {
127.0.0.1;
localhost;
192.168.9.0/24;
};

include "/etc/named.keys/ns1-ns2.key";

options {
listen-on port 53 { 127.0.0.1; 192.168.9.11; };
listen-on-v6 port 53 { none; };

directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
secroots-file "/var/named/data/named.secroots";
recursing-file "/var/named/data/named.recursing";

allow-query { trusted; };
recursion yes;
allow-recursion { trusted; };
allow-query-cache { trusted; };
allow-transfer { none; };

forwarders {
223.5.5.5;
119.29.29.29;
1.1.1.1;
8.8.8.8;
};
forward only;

dnssec-validation auto;
version "not available";
max-cache-size 512M;
};

logging {
channel named_default {
file "/var/named/data/named.default.log" versions 7 size 20m;
severity info;
print-time yes;
print-severity yes;
print-category yes;
};
category default { named_default; };
category general { named_default; };
category security { named_default; };
category config { named_default; };
};

zone "." IN {
type hint;
file "named.ca";
};

include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
include "/etc/named/named.conf.local";

创建 /etc/named/named.conf.local

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server 192.168.9.10 {
keys { ns1-ns2.; };
};

zone "corp.example" IN {
type secondary;
file "slaves/db.corp.example";
primaries { 192.168.9.10 key ns1-ns2.; };
allow-query { trusted; };
};

zone "9.168.192.in-addr.arpa" IN {
type secondary;
file "slaves/db.192.168.9";
primaries { 192.168.9.10 key ns1-ns2.; };
allow-query { trusted; };
};

检查并启动:

1
2
3
4
5
sudo named-checkconf
sudo named-checkconf -z
sudo systemctl enable --now named
sudo systemctl restart named
sudo journalctl -u named -xe

检查从服务器是否拉取成功:

1
2
sudo ls -l /var/named/slaves
sudo journalctl -u named | grep -i transfer

测试:

1
2
3
dig @192.168.9.11 gitlab.corp.example A +short
dig @192.168.9.11 -x 192.168.9.21 +short
dig @192.168.9.11 corp.example SOA +short

11. 客户端配置

11.1 通过 NetworkManager 配置 Linux 客户端

假设网卡连接名是 System eth0,先查看:

1
nmcli con show

设置 DNS:

1
2
3
4
sudo nmcli con mod "System eth0" ipv4.dns "192.168.9.10 192.168.9.11"
sudo nmcli con mod "System eth0" ipv4.dns-search "corp.example"
sudo nmcli con mod "System eth0" ipv4.ignore-auto-dns yes
sudo nmcli con up "System eth0"

查看:

1
2
cat /etc/resolv.conf
resolvectl status 2>/dev/null || true

测试:

1
2
dig gitlab.corp.example +short
ping -c 3 gitlab.corp.example

11.2 macOS 客户端

查看网络服务名:

1
networksetup -listallnetworkservices

设置 Wi-Fi DNS:

1
sudo networksetup -setdnsservers Wi-Fi 192.168.9.10 192.168.9.11

清理缓存:

1
2
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

测试:

1
dig gitlab.corp.example +short

11.3 推荐通过 DHCP 下发

生产环境不要让每台机器手工改 DNS。建议在 DHCP 中下发:

  • Option 6:DNS Server,填 192.168.9.10, 192.168.9.11
  • Option 15:DNS Search Domain,填 corp.example

这样新增机器自动接入 DNS,减少“这台能访问,那台不能访问”的薛定谔式排障。

12. 日常维护流程

12.1 添加一条 A 记录

编辑 ns1 的正向 zone:

1
sudo vi /var/named/zones/db.corp.example

添加:

1
nexus   IN  A   192.168.9.24

同时编辑反向 zone:

1
sudo vi /var/named/zones/db.192.168.9

添加:

1
24      IN  PTR nexus.corp.example.

两个 zone 的 SOA serial 都要递增,例如:

1
2026061602 ; serial

检查:

1
2
sudo named-checkzone corp.example /var/named/zones/db.corp.example
sudo named-checkzone 9.168.192.in-addr.arpa /var/named/zones/db.192.168.9

重载:

1
2
sudo rndc reload corp.example
sudo rndc reload 9.168.192.in-addr.arpa

验证:

1
2
3
4
dig @192.168.9.10 nexus.corp.example +short
dig @192.168.9.11 nexus.corp.example +short
dig @192.168.9.10 -x 192.168.9.24 +short
dig @192.168.9.11 -x 192.168.9.24 +short

12.2 常用 rndc 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 查看服务状态
sudo rndc status

# 重载所有配置和 zone
sudo rndc reload

# 只重载某个 zone
sudo rndc reload corp.example

# 重新读取 named.conf,不一定重载 zone
sudo rndc reconfig

# 清空全部缓存
sudo rndc flush

# 清空某个名字的缓存
sudo rndc flushname github.com

# 临时打开/关闭查询日志,不建议长期开启
sudo rndc querylog on
sudo rndc querylog off

13. 递归、转发与 Open Resolver

recursion yes; 不是洪水猛兽,问题在于你让谁用。

安全写法:

1
2
3
4
5
6
7
8
9
10
11
acl "trusted" {
127.0.0.1;
localhost;
192.168.9.0/24;
};

options {
recursion yes;
allow-recursion { trusted; };
allow-query-cache { trusted; };
};

危险写法:

1
2
3
4
5
options {
recursion yes;
allow-recursion { any; };
allow-query-cache { any; };
};

后者会让你的 DNS 变成 Open Resolver,有机会被用于 DNS 放大攻击。公网服务器尤其要避免。

如果你只做权威 DNS,不提供递归:

1
2
3
4
options {
recursion no;
allow-query-cache { none; };
};

更推荐的生产架构是:

1
2
内网客户端 -> 递归/缓存 DNS 集群
业务域名 -> 权威 DNS 主从集群

也就是说,递归和权威最好分开。小团队或实验环境可以放在一台机器上,但要通过 ACL 严格限制。

14. Split DNS / View 配置思路

有些场景需要同一个域名内外解析不同 IP:

  • 内网:app.example.com -> 192.168.9.50
  • 外网:app.example.com -> 203.0.113.50

这时可以使用 BIND 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
acl "internal" {
192.168.9.0/24;
127.0.0.1;
};

view "internal-view" {
match-clients { internal; };
recursion yes;
allow-recursion { internal; };
allow-query-cache { internal; };

zone "example.com" IN {
type primary;
file "zones/internal/db.example.com";
};
};

view "external-view" {
match-clients { any; };
recursion no;
allow-query-cache { none; };

zone "example.com" IN {
type primary;
file "zones/external/db.example.com";
};
};

使用 view 时注意:

  1. 一旦启用 view,相关 zone 通常都要放进 view 中。
  2. view 的顺序很重要,先匹配先命中。
  3. Split DNS 很强,但也会提高维护复杂度。能用清晰子域解决的,不一定非要上 view。

15. 日志配置建议

默认日志已经够用。只有排障时才建议打开查询日志。

更细的日志配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
logging {
channel default_file {
file "/var/named/data/default.log" versions 7 size 50m;
severity info;
print-time yes;
print-severity yes;
print-category yes;
};

channel query_file {
file "/var/named/data/query.log" versions 5 size 100m;
severity info;
print-time yes;
};

category default { default_file; };
category general { default_file; };
category security { default_file; };

// 生产环境慎开,查询量大时会明显增加 IO 和 CPU 压力。
// category queries { query_file; };
};

如果是高流量 DNS,长期记录查询建议考虑 dnstap,而不是传统 query log。

16. 安全加固清单

16.1 严格限制递归

1
2
3
recursion yes;
allow-recursion { trusted; };
allow-query-cache { trusted; };

不要把递归开放给公网。

16.2 严格限制 zone transfer

1
allow-transfer { none; };

在具体 zone 中放开:

1
allow-transfer { key ns1-ns2.; 192.168.9.11; };

16.3 主从同步使用 TSIG

1
include "/etc/named.keys/ns1-ns2.key";

从服务器:

1
primaries { 192.168.9.10 key ns1-ns2.; };

16.4 不暴露真实版本

1
version "not available";

16.5 最小化监听地址

1
listen-on port 53 { 127.0.0.1; 192.168.9.10; };

不要为了省事写:

1
listen-on port 53 { any; };

除非你明确知道所有接口都应该提供 DNS 服务。

16.6 保持 SELinux Enforcing

RHEL 系系统默认 SELinux enforcing。不要一遇到权限问题就 setenforce 0。先检查文件属主、权限、SELinux context。

常用命令:

1
2
3
ls -Z /var/named/zones
sudo restorecon -Rv /var/named/zones
sudo ausearch -m avc -ts recent

16.7 定期更新

BIND 是基础设施软件,要关注安全公告。至少做到:

1
2
sudo dnf update bind bind-utils
named -v

并订阅发行版安全公告或 ISC BIND announce。

16.8 谨慎启用动态更新

如果不需要 DDNS,不要开:

1
allow-update { none; };

如果要开,优先使用 TSIG 或 update-policy,不要只靠 IP。

16.9 可选:响应速率限制 RRL

公网权威 DNS 可以考虑:

1
2
3
4
5
6
options {
rate-limit {
responses-per-second 10;
window 5;
};
};

RRL 不是万能药,生产上要结合业务流量测试。不要为了“看起来安全”直接拍脑袋上参数。

17. 常见故障排查

17.1 named-checkconf 没输出是什么意思?

没输出通常就是语法正确。

1
sudo named-checkconf

想让它检查 zone 加载:

1
sudo named-checkconf -z

17.2 zone 文件检查 OK,但解析不到

按顺序检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. zone 是否在 named.conf 中 include 了
sudo named-checkconf -z

# 2. 服务是否监听 53
sudo ss -lntup | grep ':53'

# 3. 防火墙是否放行
sudo firewall-cmd --list-services

# 4. 客户端是否真的用这台 DNS
cat /etc/resolv.conf

# 5. 指定 DNS 直接查
dig @192.168.9.10 gitlab.corp.example A

17.3 从服务器不同步

检查这些点:

1
2
3
4
5
6
7
8
9
10
11
# ns1 上检查 zone serial 是否递增
dig @192.168.9.10 corp.example SOA +short

# ns2 上看日志
sudo journalctl -u named | grep -iE 'transfer|xfer|notauth|badkey|badsig'

# ns2 是否能访问 ns1 TCP 53
nc -vz 192.168.9.10 53

# TSIG key 是否两边完全一致
sudo sha256sum /etc/named.keys/ns1-ns2.key

常见原因:

现象 可能原因 处理
Transfer status: failed 防火墙没开 TCP 53 放行 TCP/UDP 53
NOTAUTH TSIG key 不一致或 zone 不允许 transfer 对比 key,检查 allow-transfer
ns2 一直旧数据 SOA serial 没递增 修改 zone 后递增 serial
ns2 没收到通知 notify/also-notify 配置问题 检查主服务器 zone 配置

17.4 SERVFAIL

SERVFAIL 是 DNS 世界的“你猜”。常见原因:

  • DNSSEC 验证失败。
  • 上游 forwarder 不可用。
  • zone 文件格式错误。
  • 权限或 SELinux 阻止 named 读取文件。
  • 递归被 ACL 拒绝。

排查:

1
2
3
4
dig @192.168.9.10 github.com A +dnssec
sudo journalctl -u named -xe
sudo named-checkconf -z
sudo rndc status

如果只有公网域名 SERVFAIL,重点看 forwarders、DNSSEC、出口网络。如果只有内网域名失败,重点看 zone、include、权限、ACL。

17.5 REFUSED

REFUSED 多数是 ACL 问题。

检查:

1
2
3
allow-query { trusted; };
allow-recursion { trusted; };
allow-query-cache { trusted; };

确认客户端 IP 是否在 trusted ACL 里。

17.6 connection timed out; no servers could be reached

一般不是 DNS 记录问题,而是网络连不上:

1
2
3
4
5
ping 192.168.9.10
nc -vz 192.168.9.10 53
dig @192.168.9.10 corp.example SOA
sudo firewall-cmd --list-all
sudo ss -lntup | grep ':53'

17.7 修改 zone 后客户端仍然拿到旧结果

可能是 TTL 缓存。

处理方式:

  • 降低记录 TTL,等旧 TTL 过期后再切换。
  • 在 BIND 上执行:
1
sudo rndc flushname gitlab.corp.example
  • 客户端也可能有缓存,Linux/macOS/浏览器都可能缓存。

18. 权威 DNS 与递归 DNS 是否应该分开?

生产建议:尽量分开

推荐架构:

1
2
3
客户端 -> 内网递归 DNS / 缓存 DNS
业务服务 -> 内网权威 DNS 主从
公网域名 -> 公网权威 DNS 服务商或独立集群

为什么?

  1. 权限模型不同:递归 DNS 只应该服务可信客户端,权威 DNS 可能需要对更多来源提供查询。
  2. 风险不同:递归 DNS 容易成为放大攻击组件;权威 DNS 更关注 zone 数据安全和可用性。
  3. 性能模型不同:递归 DNS 重缓存命中率和上游性能;权威 DNS 重 zone 查询和传输。
  4. 排障更清晰:出了问题知道是“本地域数据错了”还是“外部递归解析错了”。

但在小型内网、实验室、个人机房中,一台 BIND 同时做权威和递归也很常见。关键是:用 ACL 把递归锁死在内网

19. Debian/Ubuntu 配置映射

如果你在 Debian/Ubuntu 上配置,结构通常是:

1
2
3
4
5
/etc/bind/named.conf
/etc/bind/named.conf.options
/etc/bind/named.conf.local
/etc/bind/zones/db.corp.example
/etc/bind/zones/db.192.168.9

安装:

1
sudo apt install -y bind9 bind9utils dnsutils

named.conf.options 可写:

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
acl "trusted" {
127.0.0.1;
localhost;
192.168.9.0/24;
};

options {
directory "/var/cache/bind";

listen-on { 127.0.0.1; 192.168.9.10; };
listen-on-v6 { none; };

allow-query { trusted; };
recursion yes;
allow-recursion { trusted; };
allow-query-cache { trusted; };

forwarders {
223.5.5.5;
119.29.29.29;
1.1.1.1;
8.8.8.8;
};
forward only;

dnssec-validation auto;
};

named.conf.local 写 zone:

1
2
3
4
5
6
7
8
9
10
11
12
13
include "/etc/bind/keys/ns1-ns2.key";

zone "corp.example" {
type primary;
file "/etc/bind/zones/db.corp.example";
allow-transfer { key ns1-ns2.; 192.168.9.11; };
};

zone "9.168.192.in-addr.arpa" {
type primary;
file "/etc/bind/zones/db.192.168.9";
allow-transfer { key ns1-ns2.; 192.168.9.11; };
};

检查与重启:

1
2
3
4
sudo named-checkconf
sudo named-checkconf -z
sudo named-checkzone corp.example /etc/bind/zones/db.corp.example
sudo systemctl restart bind9

20. 生产实践建议

20.1 Zone 文件纳入版本管理,但密钥不要入库

建议:

1
2
3
4
5
6
dns-config/
named.conf.local
zones/
db.corp.example
db.192.168.9
README.md

不要提交:

1
2
3
*.key
rndc.key
ns1-ns2.key

可以在 Git 中写模板:

1
include "/etc/named.keys/ns1-ns2.key";

密钥由部署系统或人工安全下发。

20.2 变更流程

推荐流程:

1
修改 zone -> 递增 serial -> named-checkzone -> named-checkconf -z -> rndc reload zone -> dig 验证 ns1/ns2 -> 合并变更记录

不要直接 systemctl restart named 解决一切。rndc reload zone 更温和,也更适合生产。

20.3 TTL 策略

常见设置:

场景 TTL 建议
频繁切换的服务 30s - 5m
普通内网服务 5m - 1h
基础设施记录 NS/MX 1h - 1d
迁移前准备 先提前降低 TTL,迁移后再调回

不要全局无脑 TTL 1s。低 TTL 会增加 DNS 压力,也不一定能绕开所有客户端缓存。

20.4 命名规范

建议:

1
服务名.环境.区域.公司域名

例如:

1
2
3
gitlab.infra.sg.corp.example
api.prod.sg.corp.example
db01.prod.sg.corp.example

小团队可以简单一点:

1
2
3
gitlab.corp.example
jenkins.corp.example
harbor.corp.example

关键是稳定、一致、能看懂。DNS 名字是基础设施的门牌号,别写成脑筋急转弯。

21. 一份最小可用配置清单

如果你只想快速落地,至少需要这些文件。

Primary:

1
2
3
4
5
/etc/named.conf
/etc/named/named.conf.local
/etc/named.keys/ns1-ns2.key
/var/named/zones/db.corp.example
/var/named/zones/db.192.168.9

Secondary:

1
2
3
4
/etc/named.conf
/etc/named/named.conf.local
/etc/named.keys/ns1-ns2.key
/var/named/slaves/

最小检查命令:

1
2
3
4
5
sudo named-checkconf
sudo named-checkconf -z
sudo named-checkzone corp.example /var/named/zones/db.corp.example
sudo named-checkzone 9.168.192.in-addr.arpa /var/named/zones/db.192.168.9
sudo systemctl restart named

最小验证命令:

1
2
3
4
5
dig @192.168.9.10 corp.example SOA +short
dig @192.168.9.10 gitlab.corp.example A +short
dig @192.168.9.10 -x 192.168.9.21 +short
dig @192.168.9.11 gitlab.corp.example A +short
dig @192.168.9.10 github.com A +short

22. 一页速查表

目标 命令
检查主配置 named-checkconf
检查配置并加载 zone named-checkconf -z
检查正向 zone named-checkzone corp.example /var/named/zones/db.corp.example
检查反向 zone named-checkzone 9.168.192.in-addr.arpa /var/named/zones/db.192.168.9
重载全部 rndc reload
重载单个 zone rndc reload corp.example
查看状态 rndc status
清空缓存 rndc flush
清空指定域名缓存 rndc flushname example.com
查 A 记录 dig @192.168.9.10 gitlab.corp.example A
查 PTR dig @192.168.9.10 -x 192.168.9.21
查 SOA dig @192.168.9.10 corp.example SOA
查 NS dig @192.168.9.10 corp.example NS
看日志 journalctl -u named -xe
看监听 `ss -lntup \

结论

BIND 的配置并不难,真正难的是把它做成一套可维护、可审计、可排障的基础设施。私有 DNS 推荐至少做到下面几点:

  1. 正向区和反向区都配置,别只做单向解析。
  2. 修改 zone 必须递增 SOA serial。
  3. 主从同步使用 TSIG,不要只靠 IP 放行。
  4. 递归只允许可信内网,避免 Open Resolver。
  5. zone transfer 默认关闭,在具体 zone 精确放开。
  6. 开启 SELinux 时优先修权限和 context,不要直接关闭 SELinux。
  7. 日常使用 named-checkconfnamed-checkzonerndc reloaddig,少用“重启大法”。
  8. 新版本 BIND 的默认行为和旧教程不完全一样,迁移时一定看版本说明。

如果只是个人小网络,BIND 可能显得重;CoreDNS、dnsmasq、Unbound 也都能解决部分问题。但如果你需要权威 DNS、主从同步、TSIG、View、复杂 zone 管理,BIND 仍然是非常稳的选择。它有点老派,但老派不等于落后;老派的好处是,坑大多已经有人替你踩过了。

参考资料

启示录

基础设施最怕“能跑就行”,DNS 尤其如此。能解析只是第一步,能安全、稳定、可回滚地解析,才算真正交付。

富贵岂由人,时会高志须酬。


BIND 9 私有 DNS 服务搭建与生产实践:从 named.conf 到主从、递归、安全与排障
https://allendericdalexander.github.io/2026/06/16/devops/infra/bind9-private-dns-hexo-blog/
作者
AtLuoFu
发布于
2026年6月16日
许可协议