强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 DirectoryAD 域名解析域控制器 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 的行为:

  1. 优先使用列表中的第一个转发器
  2. 如果第一个转发器持续超时,自动切换到下一个
  3. 定期探测已跳过的转发器,恢复后自动切回

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

💡 小技巧

  1. 企业环境推荐 forward first:转发加速解析,失败时有后备。
  2. 内部域名用条件转发:比 stub zone 更灵活,不需要区域传输。
  3. 定期更新根提示文件:虽然很少变化,但保持最新是好习惯。
  4. allow-recursion 必须限制范围:开放递归服务器是巨大的安全风险。
  5. 负缓存可以大幅减少无效查询max-ncache-ttl 900 是合理默认值。

📖 扩展阅读