BIND DNS 服务器搭建完全教程 / 第 05 章:转发与递归查询
本章概述
本章深入讲解 BIND 的转发(Forwarding)、条件转发(Conditional Forwarding)、根提示(Root Hints)和递归查询配置。这些配置决定了 DNS 服务器如何解析它不直接负责的域名。
5.1 递归查询基础
5.1.1 递归 vs 迭代查询
| 类型 | 行为 | 举例 |
|---|---|---|
| 递归查询(Recursive) | 客户端要求 DNS 服务器"帮我查到底" | 客户端 → 递归服务器 → … → 最终结果 |
| 迭代查询(Iterative) | DNS 服务器返回"下一级去哪问" | 根 → “去问 .com” → “去问 example.com” |
递归查询流程(客户端视角):
客户端 → 递归服务器:"www.example.com 的 IP?"
递归服务器 → 客户端:"是 93.184.216.34"
(中间过程对客户端不可见)
迭代查询流程(服务器之间):
递归服务器 → 根服务器:"www.example.com?"
根服务器 → 递归服务器:"我不知道,去问 .com 的服务器"
递归服务器 → .com 服务器:"www.example.com?"
.com 服务器 → 递归服务器:"我不知道,去问 example.com 的权威服务器"
递归服务器 → 权威服务器:"www.example.com?"
权威服务器 → 递归服务器:"93.184.216.34"
5.1.2 递归服务器配置
options {
// 开启递归
recursion yes;
// 允许使用递归的客户端(安全限制!)
allow-recursion {
localhost;
localnets;
192.168.0.0/16;
10.0.0.0/8;
};
// 允许查询缓存的客户端
allow-query-cache {
localhost;
localnets;
192.168.0.0/16;
};
};
⚠️ 安全警告:
allow-recursion { any; };会让你的服务器成为开放递归服务器,极易被用于 DNS 放大攻击。生产环境必须限制递归客户端范围。
5.1.3 禁用递归(纯权威服务器)
options {
recursion no;
allow-recursion { none; };
allow-query { any; }; // 权威数据仍允许任何人查询
};
5.2 转发(Forwarding)
转发是指将查询请求发送给上游 DNS 服务器,而不是自己从根服务器开始递归查询。
5.2.1 基本转发配置
options {
// 指定上游转发器
forwarders {
8.8.8.8; // Google Public DNS
8.8.4.4; // Google Public DNS
1.1.1.1; // Cloudflare DNS
223.5.5.5; // 阿里 DNS
};
// 转发策略
forward first; // 先尝试转发,失败后自行递归
// forward only; // 仅转发,失败返回 SERVFAIL
};
5.2.2 转发策略对比
| 策略 | 行为 | 优点 | 缺点 |
|---|---|---|---|
first | 先转发,失败后自行递归 | 兼顾速度和可靠性 | 转发失败时有额外延迟 |
only | 仅转发,不自行递归 | 配置简单,依赖上游 | 上游故障则完全不可用 |
| 不设 | 不转发,完全自行递归 | 完全自主 | 需要访问根服务器,初始查询慢 |
5.2.3 企业网络转发典型架构
┌──────────────────────────────────────────────┐
│ 企业内网 │
│ │
│ 客户端 → 内部递归 DNS → 防火墙 → 外部转发器 │
│ ↓ │
│ 8.8.8.8 / 1.1.1.1 │
└──────────────────────────────────────────────┘
内部 DNS 服务器:
- 处理内部域名解析(.internal, .corp)
- 将外部域名转发到上游 DNS
- 缓存结果减少外部查询
// 内部递归 DNS 服务器配置
options {
recursion yes;
allow-recursion { localnets; 10.0.0.0/8; };
forwarders {
10.0.0.1; // DMZ 中的转发 DNS
};
forward first;
// 如果转发失败,回退到根递归
// (forward first 的默认行为)
};
5.3 条件转发(Conditional Forwarding)
条件转发允许对不同域名使用不同的转发器。
5.3.1 语法
// 将 partner.com 的查询转发到合作伙伴的 DNS
zone "partner.com" {
type forward;
forwarders {
172.16.1.100; // 合作伙伴的 DNS 服务器
172.16.1.101;
};
forward only; // 仅转发
};
// 将 internal.corp 的查询转发到 AD 域控
zone "internal.corp" {
type forward;
forwarders {
10.1.1.1; // Active Directory DNS
10.1.1.2;
};
forward only;
};
// 将 cloud.example.com 转发到云环境 DNS
zone "cloud.example.com" {
type forward;
forwarders {
172.31.0.2; // AWS VPC DNS
};
forward only;
};
5.3.2 条件转发的业务场景
| 场景 | 说明 | 转发目标 |
|---|---|---|
| 企业并购 | 两个公司的 DNS 互转 | 对方 DNS 服务器 |
| 混合云 | 公有云内网域名解析 | 云 VPC DNS |
| Active Directory | AD 域名解析 | 域控制器 DNS |
| 合作伙伴 | 联合域名解析 | 对方 DNS |
| VPN 联通 | 跨网络域名解析 | VPN 对端 DNS |
5.3.3 多级转发示例
// 场景:总部 DNS → 分部 DNS → 内网域名
// 分部 DNS 配置
options {
forwarders {
10.0.0.1; // 总部 DNS
};
forward first;
};
// 分部 DNS 仍可自行解析公网域名(forward first)
// 内部域名(如 .internal)通过总部 DNS 解析
5.4 根提示(Root Hints)
根提示文件告诉递归服务器根 DNS 服务器的地址。
5.4.1 根提示文件
# 查看当前根提示文件
cat /var/cache/bind/db.root
# 或
cat /var/named/named.ca
; /var/cache/bind/db.root
; 根服务器提示文件
; 来源: https://www.internic.net/domain/named.root
;
. 3600000 IN NS A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET. 3600000 IN A 198.41.0.4
A.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:503:ba3e::2:30
;
. 3600000 IN NS B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET. 3600000 IN A 199.9.14.201
B.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:500:200::b
;
. 3600000 IN NS C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET. 3600000 IN A 192.33.4.12
C.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:500:2::c
;
; ... 共 13 个根服务器(A-M)
5.4.2 更新根提示文件
# 从 Internic 下载最新根提示
wget https://www.internic.net/domain/named.root -O /tmp/named.root
# 验证内容
head -20 /tmp/named.root
# 替换(Ubuntu/Debian)
sudo cp /tmp/named.root /var/cache/bind/db.root
sudo chown bind:bind /var/cache/bind/db.root
# 替换(RHEL/CentOS)
sudo cp /tmp/named.root /var/named/named.ca
sudo chown named:named /var/named/named.ca
# 重载配置
sudo rndc reload
💡 根提示文件很少变化,但建议每年检查一次。最近一次更新是 2023 年。
5.4.3 在配置中引用根提示
// 根区域定义(告诉 BIND 使用根提示进行递归)
zone "." {
type hint;
file "db.root"; // Ubuntu/Debian
// file "named.ca"; // RHEL/CentOS
};
5.5 转发与递归的优先级
BIND 处理查询的决策流程:
查询到达 →
1. 是否有匹配的权威区域? → 返回权威答案
2. 是否有匹配的转发区域(type forward)? → 转发到指定服务器
3. 是否有全局转发器(options.forwarders)? → 转发到全局转发器
4. 是否允许递归? → 从根服务器开始递归查询
5. 以上都不是 → 返回 REFUSED
5.5.1 forward first vs forward only 的实际行为
// 场景:转发器不可用时的行为
// forward first 的行为:
// 1. 转发到 8.8.8.8 → 超时
// 2. 回退到根递归 → 成功获取结果
// 3. 总耗时:转发超时 + 递归时间(较慢但能解析)
// forward only 的行为:
// 1. 转发到 8.8.8.8 → 超时
// 2. 返回 SERVFAIL
// 3. 客户端收到错误
5.6 根据域名配置不同的转发策略
5.6.1 混合配置示例
// 全局选项:默认不转发,自行递归
options {
recursion yes;
allow-recursion { trusted; };
// 不设 forwarders,自行递归
};
// 内部域名 → 转发到内部 DNS
zone "internal.corp" {
type forward;
forwarders { 10.1.1.1; 10.1.1.2; };
forward only;
};
// 合作伙伴域名 → 转发到对方 DNS
zone "partner-a.com" {
type forward;
forwarders { 172.16.1.100; };
forward only;
};
zone "partner-b.com" {
type forward;
forwarders { 172.16.2.100; };
forward only;
};
// 云环境域名 → 转发到云 DNS
zone "amazonaws.com" {
type forward;
forwarders { 172.31.0.2; };
forward first; // 允许回退
};
// 其他域名 → 自行递归(无 forwarders)
// 使用根提示从根开始查询
5.7 转发器的健康检查
BIND 会自动对转发器进行健康检查:
5.7.1 转发器选择算法
options {
forwarders {
8.8.8.8; // 优先级 1
1.1.1.1; // 优先级 2
223.5.5.5; // 优先级 3
};
};
BIND 的行为:
- 优先使用列表中的第一个转发器
- 如果第一个转发器持续超时,自动切换到下一个
- 定期探测已跳过的转发器,恢复后自动切回
5.7.2 转发超时设置
options {
// 转发超时(秒),默认 10
// 不是直接配置项,由 BIND 内部算法控制
// 通常不需要手动调整
// 但可以通过 max-udp-size 和 edns-udp-size 影响
max-udp-size 1232;
edns-udp-size 1232;
};
5.8 负缓存(Negative Caching)
当查询返回 NXDOMAIN(域名不存在)时,BIND 会缓存这个结果。
options {
// 负缓存最大 TTL(秒)
max-ncache-ttl 900; // 默认 15 分钟
// 正缓存最大 TTL
max-cache-ttl 3600; // 默认 1 小时
};
SOA 的 Minimum 字段
; SOA 最后一个字段控制 NXDOMAIN 的缓存时间
@ IN SOA ns1.example.com. admin.example.com. (
2026051001 ; Serial
3600 ; Refresh
900 ; Retry
1209600 ; Expire
86400 ; Minimum (Negative Cache TTL)
)
注意:
max-ncache-ttl会覆盖 SOA Minimum 字段,取两者的较小值。
5.9 DNS 预取(Prefetch)
预取功能在缓存 TTL 即将过期时主动重新查询,减少用户感知的延迟。
options {
// prefetch <ttl-threshold> <queries-threshold>
// 当记录的 TTL 低于 ttl-threshold 且查询次数超过 queries-threshold 时预取
prefetch 2 9; // TTL < 2秒 且 查询 >= 9次 时预取
};
| 参数 | 默认值 | 说明 |
|---|---|---|
| TTL 阈值 | 2 | 当缓存 TTL 低于此值时触发预取 |
| 查询阈值 | 9 | 当查询次数达到此值时触发预取 |
适用场景:高流量的递归服务器,减少热点域名的解析延迟。
5.10 EDNS 与 DNSSEC 验证
5.10.1 EDNS(Extension Mechanisms for DNS)
options {
// EDNS UDP 缓冲区大小
edns-udp-size 1232; // 推荐值(避免分片)
max-udp-size 1232; // 接收的最大 UDP 响应大小
};
| 值 | 说明 |
|---|---|
| 512 | 传统 DNS 最大值 |
| 1232 | 推荐值(避免 IPv6 分片问题) |
| 4096 | 较大值(可能引起分片) |
5.10.2 DNSSEC 验证
options {
// 开启 DNSSEC 验证(默认 auto)
dnssec-validation auto;
// 或使用手动信任锚
// dnssec-validation yes;
// bindkeys-file "/etc/bind/bind.keys";
};
详细配置见 第 06 章 DNSSEC。
5.11 常见问题排查
5.11.1 转发不生效
# 检查是否有更具体的区域配置覆盖了转发
# 例如:如果有 zone "example.com" { type primary; ... }
# 则不会触发全局转发
# 查看查询日志
sudo rndc querylog on
dig @127.0.0.1 test.example.com
# 检查日志中是否显示 "forward" 行为
5.11.2 递归查询失败
# 检查是否允许递归
dig @127.0.0.1 example.com A
# 如果返回 REFUSED → 检查 allow-recursion
# 检查根提示文件
dig @127.0.0.1 . NS
# 应返回 13 个根服务器
# 测试根服务器连通性
dig @198.41.0.4 . NS
5.11.3 条件转发不工作
# 检查转发区域配置
named-checkconf
# 测试转发
dig @127.0.0.1 test.partner.com
# 应该被转发到 172.16.1.100
# 检查上游 DNS 是否可达
dig @172.16.1.100 test.partner.com
5.12 本章小结
| 配置方式 | 适用场景 | 关键配置 |
|---|---|---|
| 全局转发 | 所有外部查询都转发 | options.forwarders |
| 条件转发 | 特定域名转发到特定服务器 | zone { type forward; forwarders {...}; } |
| 根递归 | 完全自主解析 | zone "." { type hint; } |
forward first | 兼顾速度和可靠性 | 转发失败后自行递归 |
forward only | 依赖上游 DNS | 转发失败返回 SERVFAIL |
💡 小技巧
- 企业环境推荐
forward first:转发加速解析,失败时有后备。 - 内部域名用条件转发:比 stub zone 更灵活,不需要区域传输。
- 定期更新根提示文件:虽然很少变化,但保持最新是好习惯。
allow-recursion必须限制范围:开放递归服务器是巨大的安全风险。- 负缓存可以大幅减少无效查询:
max-ncache-ttl 900是合理默认值。