欢迎你来读这篇博客。这篇文章会从 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.com、github.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.
几个非常容易踩坑的小点:
zone 文件中的完整域名最好写尾部点号,比如 ns1.corp.example.。没有尾部点号时,BIND 会把当前 zone 自动拼上去。
CNAME 记录所在的名称一般不要再同时挂其他记录。
修改 zone 文件后必须增加 SOA serial,否则从服务器可能不会同步。
反向解析不是自动生成的,需要单独配置反向区和 PTR 记录。
2. 私有 DNS 的域名选择 内网域名不要随便拍脑袋。
推荐优先级:
企业生产环境 :使用自己真实拥有的域名的子域,例如 corp.example.com、lan.example.com、idc.example.com。
家庭网络 :可以考虑 home.arpa。
教程或实验环境 :使用 corp.example、example.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
bind9utils、dnsutils
生产建议:
新项目优先用系统发行版维护的安全更新包,或者使用 ISC 官方支持分支。
如果你追求稳定,不要随便上 development branch。
配置从旧版本迁移到 BIND 9.18/9.20 时,一定跑 named-checkconf 和 named-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 namedsudo systemctl status named
开放防火墙:
1 2 3 sudo firewall-cmd --permanent --add-service=dnssudo firewall-cmd --reloadsudo 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-utilssudo systemctl enable --now namedsudo firewall-cmd --permanent --add-service=dnssudo firewall-cmd --reload
CentOS 7 自带 BIND 版本较老,很多教程也是围绕它写的。能跑不代表适合长期生产。公网暴露、递归控制、DNSSEC、TSIG 这些地方要格外谨慎。
5.3 Debian / Ubuntu 1 2 3 4 sudo apt updatesudo apt install -y bind9 bind9utils dnsutilssudo systemctl enable --now bind9sudo systemctl status bind9
Ubuntu 使用 UFW 时:
1 2 3 sudo ufw allow 53/tcpsudo ufw allow 53/udpsudo ufw reload
6. Primary DNS 配置 下面以 ns1 = 192.168.9.10 为例。
6.1 目录准备 1 2 3 4 sudo mkdir -p /var/named/zonessudo mkdir -p /etc/named.keyssudo chown root:named /var/named/zones /etc/named.keyssudo chmod 750 /var/named/zones /etc/named.keys
如果 SELinux 开启,并且你使用了自定义目录,建议设置上下文:
1 2 3 sudo dnf install -y policycoreutils-python-utilssudo 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.keysudo chown root:named /etc/named.keys/ns1-ns2.keysudo 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 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/namedsudo 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 1h 15m 7d 10m ) @ IN NS ns1.corp.example. @ IN NS ns2.corp.example. ns1 IN A 192.168.9.10 ns2 IN A 192.168.9.11 gitlab IN A 192.168.9.21 jenkins IN A 192.168.9.22 harbor IN A 192.168.9.23 api IN CNAME gitlab.corp.example. mail IN A 192.168.9.30 @ IN MX 10 mail.corp.example. @ IN TXT "private dns zone for corp.example"
设置权限:
1 2 3 sudo chown root:named /var/named/zones/db.corp.examplesudo chmod 640 /var/named/zones/db.corp.examplesudo 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 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 1h 15m 7d 10m ) @ 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.9sudo chmod 640 /var/named/zones/db.192.168.9sudo restorecon -v /var/named/zones/db.192.168.9sudo named-checkzone 9.168.192.in-addr.arpa /var/named/zones/db.192.168.9
9. 检查并启动 Primary 检查主配置:
如果希望连 zone 一起加载检查:
重启:
1 2 sudo systemctl restart namedsudo systemctl status named
看日志:
1 2 sudo journalctl -u named -xesudo 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-utilssudo mkdir -p /var/named/slavessudo mkdir -p /etc/named.keyssudo chown named:named /var/named/slavessudo chmod 750 /var/named/slavessudo chown root:named /etc/named.keyssudo chmod 750 /etc/named.keys
把 ns1 上的 TSIG key 复制到 ns2:
1 2 sudo chown root:named /etc/named.keys/ns1-ns2.keysudo 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-checkconfsudo named-checkconf -zsudo systemctl enable --now namedsudo systemctl restart namedsudo journalctl -u named -xe
检查从服务器是否拉取成功:
1 2 sudo ls -l /var/named/slavessudo 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,先查看:
设置 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 -flushcachesudo 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
添加:
同时编辑反向 zone:
1 sudo vi /var/named/zones/db.192.168.9
添加:
1 24 IN PTR nexus.corp.example.
两个 zone 的 SOA serial 都要递增,例如:
检查:
1 2 sudo named-checkzone corp.example /var/named/zones/db.corp.examplesudo named-checkzone 9.168.192.in-addr.arpa /var/named/zones/db.192.168.9
重载:
1 2 sudo rndc reload corp.examplesudo 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 statussudo rndc reloadsudo rndc reload corp.examplesudo rndc reconfigsudo rndc flushsudo rndc flushname github.comsudo rndc querylog onsudo 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 时注意:
一旦启用 view,相关 zone 通常都要放进 view 中。
view 的顺序很重要,先匹配先命中。
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/zonessudo restorecon -Rv /var/named/zonessudo ausearch -m avc -ts recent
16.7 定期更新 BIND 是基础设施软件,要关注安全公告。至少做到:
1 2 sudo dnf update bind bind-utils named -v
并订阅发行版安全公告或 ISC BIND announce。
16.8 谨慎启用动态更新 如果不需要 DDNS,不要开:
如果要开,优先使用 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 没输出是什么意思? 没输出通常就是语法正确。
想让它检查 zone 加载:
17.2 zone 文件检查 OK,但解析不到 按顺序检查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 sudo named-checkconf -zsudo ss -lntup | grep ':53' sudo firewall-cmd --list-servicescat /etc/resolv.conf dig @192.168.9.10 gitlab.corp.example A
17.3 从服务器不同步 检查这些点:
1 2 3 4 5 6 7 8 9 10 11 dig @192.168.9.10 corp.example SOA +shortsudo journalctl -u named | grep -iE 'transfer|xfer|notauth|badkey|badsig' nc -vz 192.168.9.10 53sudo 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 +dnssecsudo journalctl -u named -xesudo named-checkconf -zsudo 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 SOAsudo firewall-cmd --list-allsudo 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 服务商或独立集群
为什么?
权限模型不同:递归 DNS 只应该服务可信客户端,权威 DNS 可能需要对更多来源提供查询。
风险不同:递归 DNS 容易成为放大攻击组件;权威 DNS 更关注 zone 数据安全和可用性。
性能模型不同:递归 DNS 重缓存命中率和上游性能;权威 DNS 重 zone 查询和传输。
排障更清晰:出了问题知道是“本地域数据错了”还是“外部递归解析错了”。
但在小型内网、实验室、个人机房中,一台 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-checkconfsudo named-checkconf -zsudo named-checkzone corp.example /etc/bind/zones/db.corp.examplesudo 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 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-checkconfsudo named-checkconf -zsudo named-checkzone corp.example /var/named/zones/db.corp.examplesudo named-checkzone 9.168.192.in-addr.arpa /var/named/zones/db.192.168.9sudo 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 推荐至少做到下面几点:
正向区和反向区都配置,别只做单向解析。
修改 zone 必须递增 SOA serial。
主从同步使用 TSIG,不要只靠 IP 放行。
递归只允许可信内网,避免 Open Resolver。
zone transfer 默认关闭,在具体 zone 精确放开。
开启 SELinux 时优先修权限和 context,不要直接关闭 SELinux。
日常使用 named-checkconf、named-checkzone、rndc reload、dig,少用“重启大法”。
新版本 BIND 的默认行为和旧教程不完全一样,迁移时一定看版本说明。
如果只是个人小网络,BIND 可能显得重;CoreDNS、dnsmasq、Unbound 也都能解决部分问题。但如果你需要权威 DNS、主从同步、TSIG、View、复杂 zone 管理,BIND 仍然是非常稳的选择。它有点老派,但老派不等于落后;老派的好处是,坑大多已经有人替你踩过了。
参考资料
启示录 基础设施最怕“能跑就行”,DNS 尤其如此。能解析只是第一步,能安全、稳定、可回滚地解析,才算真正交付。
富贵岂由人,时会高志须酬。