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

BIND DNS 服务器搭建完全教程 / 第 06 章:DNSSEC 签名与验证

本章概述

DNSSEC(DNS Security Extensions)为 DNS 响应提供数据完整性和来源验证,防止缓存投毒和中间人攻击。本章讲解 DNSSEC 的原理、密钥生成、区域签名、DS 记录配置和故障排查。


6.1 DNSSEC 原理

6.1.1 DNSSEC 解决什么问题

传统 DNS 存在严重的安全缺陷:

问题场景(DNS 缓存投毒):
1. 客户端向递归服务器查询 evil.com
2. 攻击者抢先回复伪造的 IP 地址
3. 递归服务器缓存了伪造结果
4. 后续所有查询都被误导到恶意服务器

DNSSEC 通过数字签名确保:

  • 数据完整性:响应未被篡改
  • 来源验证:数据确实来自权威服务器
  • 不存在性验证:可以证明某个域名不存在

6.1.2 DNSSEC 记录类型

记录类型 全称 用途
RRSIG Resource Record Signature 资源记录的数字签名
DNSKEY DNS Key 公钥,用于验证签名
DS Delegation Signer 父区域存储的子区域密钥哈希
NSEC Next Secure 证明域名不存在(按序遍历)
NSEC3 Next Secure v3 证明域名不存在(哈希隐藏)

6.1.3 信任链

根 (.)                        ← 根区 KSK(全球信任锚)
  └─ DS for .com              ← 根区存储的 .com DS
    └─ .com                   ← .com 区的 KSK/ZSK
      └─ DS for example.com   ← .com 存储的 example.com DS
        └─ example.com        ← example.com 的 KSK/ZSK
          └─ www.example.com  ← 签名的 A 记录

递归服务器从根开始逐级验证,直到找到可信的 DNSKEY。

6.1.4 KSK vs ZSK

密钥类型 全称 用途 密钥长度 轮转周期
KSK Key Signing Key 签名 DNSKEY 记录,用于父区域 DS 2048 bit 1-2 年
ZSK Zone Signing Key 签名其他资源记录 1024 bit 1-3 月

6.2 生成 DNSSEC 密钥

6.2.1 安装工具

# Ubuntu/Debian
sudo apt install -y bind9utils dnsutils

# RHEL/CentOS
sudo dnf install -y bind-utils

6.2.2 生成 ZSK(Zone Signing Key)

# 进入区域文件目录
cd /var/cache/bind/primary
# 或 RHEL: cd /var/named/primary

# 生成 ZSK(1024 位,推荐)
dnssec-keygen -a ECDSAP256SHA256 -n ZONE example.com

# 输出示例:Kexample.com.+013+12345
# 生成两个文件:
# Kexample.com.+013+12345.key    (公钥)
# Kexample.com.+013+12345.private(私钥)

6.2.3 生成 KSK(Key Signing Key)

# 生成 KSK(2048 位,推荐使用 ECDSA)
dnssec-keygen -a ECDSAP256SHA256 -n ZONE -f KSK example.com

# 输出示例:Kexample.com.+013+67890

6.2.4 推荐算法

算法编号 算法名称 密钥长度 说明
13 ECDSAP256SHA256 256 bit (等效 128 位安全) 推荐,性能好,广泛支持
14 ECDSAP384SHA384 384 bit (等效 192 位安全) 高安全需求
8 RSASHA256 2048 bit 传统,兼容性最好
15 ED25519 256 bit 新算法,BIND 9.14+ 支持

💡 推荐使用 ECDSA (算法 13):密钥短、签名快、安全性高。

6.2.5 设置密钥文件权限

# 密钥文件权限(非常重要!)
sudo chmod 600 Kexample.com.+013+*.private
sudo chmod 644 Kexample.com.+013+*.key
sudo chown bind:bind Kexample.com.+013+*

6.3 签名区域文件

6.3.1 自动签名(inline signing)

BIND 9.9+ 支持自动签名,无需手动操作:

// named.conf
zone "example.com" {
    type primary;
    file "primary/example.com.zone";
    
    // 启用 DNSSEC 自动签名
    inline-signing yes;
    auto-dnssec maintain;    // 自动维护密钥轮转
    
    // 密钥目录
    key-directory "primary/keys";
};
# 创建密钥目录
sudo mkdir -p /var/cache/bind/primary/keys

# 将密钥文件放入目录
sudo mv Kexample.com.* /var/cache/bind/primary/keys/

# 设置权限
sudo chown -R bind:bind /var/cache/bind/primary/keys
sudo chmod 700 /var/cache/bind/primary/keys

# 重载配置
sudo rndc reload example.com

# 验证签名状态
sudo rndc signing -list example.com

6.3.2 手动签名

# 1. 生成区域签名用的临时文件
cd /var/cache/bind/primary

# 2. 签名区域文件
dnssec-signzone -A -3 $(head -c 1024 /dev/urandom | sha1sum | cut -b 1-16) \
    -N INCREMENT -o example.com -t \
    -K keys/ \
    example.com.zone

# 参数说明:
# -A          区域签名时包含所有记录
# -3 <salt>   使用 NSEC3(替代 NSEC,隐藏区域内容)
# -N INCREMENT 自动递增 Serial
# -o example.com  区域名称
# -t          显示统计信息
# -K keys/    密钥文件目录

# 3. 输出文件
# example.com.zone.signed   → 签名后的区域文件
# dsset-example.com.        → DS 记录(需提交给父区域)
# keyset-example.com.       → 密钥集

6.3.3 使用签名后的文件

// 修改 named.conf
zone "example.com" {
    type primary;
    file "primary/example.com.zone.signed";  // 使用签名文件
    // 不需要手动更新,rndc reload 后 BIND 自动读取
};

6.4 DS 记录配置

6.4.1 什么是 DS 记录

DS(Delegation Signer)记录存储在父区域中,包含子区域 KSK 的哈希值,用于建立信任链。

6.4.2 提取 DS 记录

# 从签名输出中获取
cat dsset-example.com.

# 或者从 DNSKEY 记录生成
dnssec-dsfromkey -2 Kexample.com.+013+67890.key

# 输出示例:
# example.com. IN DS 12345 13 2 AABBCCDDEEFF...

6.4.3 在父区域中添加 DS 记录

方式一:通过域名注册商

登录域名注册商 → DNS 管理 → DS 记录 → 添加

算法:ECDSAP256SHA256 (13)
摘要类型:SHA-256 (2)
密钥标签:12345
摘要:AABBCCDDEEFF00112233...

方式二:在父区域文件中添加

; 父区域(.com)文件中添加
example.com.  IN DS  12345 13 2 AABBCCDDEEFF00112233...

6.4.4 DS 记录字段说明

字段 含义
Key Tag 密钥标签(指纹) 数值
Algorithm 算法 13 (ECDSAP256SHA256)
Digest Type 摘要类型 1 (SHA-1) 或 2 (SHA-256)
Digest 哈希值 十六进制字符串

⚠️ 重要:SHA-1(Digest Type 1)已不安全,强烈建议使用 SHA-256(Digest Type 2)。


6.5 DNSSEC 验证配置

6.5.1 递归服务器启用验证

options {
    // 自动 DNSSEC 验证(推荐)
    dnssec-validation auto;
    
    // 信任锚文件
    bindkeys-file "/etc/bind/bind.keys";
    managed-keys-directory "/var/cache/bind/managed-keys";
};

6.5.2 手动指定信任锚

// 如果自动更新不可用,手动指定根区信任锚
options {
    dnssec-validation yes;
};

// 根区 KSK 信任锚(需要定期更新)
trust-anchors {
    . initial-key 257 3 8 "
        AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTO
        iH7vDe3CRnJjTGMKRQFOxyzKhF6j8M0t7RpU6N7LCK8Y
        d8D5lL9Lf7Gt5v2Lb27pZ5H8y8R5G3Zh2OF3Ei0CZ7pCR
        ... (完整密钥)
    ";
};

6.5.3 获取当前根区信任锚

# 从 IANA 获取根区信任锚
wget https://data.iana.org/root-anchors/root-anchors.xml

# 查看内容
cat root-anchors.xml

6.6 DNSSEC 签名状态检查

# 查看区域签名状态
sudo rndc signing -list example.com

# 预期输出示例:
# Done signing with key 12345/ECDSAP256SHA256
# Done signing with key 67890/ECDSAP256SHA256

# 使用 dig 验证签名
dig @127.0.0.1 example.com A +dnssec

# 预期输出:
# ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2
# ;; ANSWER SECTION:
# example.com.    3600  IN  A      93.184.216.34
# example.com.    3600  IN  RRSIG  A 13 2 3600 (
#                 20260610120000 20260510120000 12345 example.com.
#                 AABBCCDD...)

# 查看 DNSKEY
dig @127.0.0.1 example.com DNSKEY +dnssec +multiline

# 验证 DS 记录
dig @127.0.0.1 example.com DS

# 使用 delv 工具验证 DNSSEC 链
delv @127.0.0.1 example.com A +rtrace

6.6.1 dig 输出中的 DNSSEC 标志

标志 含义
aa 权威答案(Authoritative Answer)
ad 已验证(Authenticated Data)= DNSSEC 验证通过
cd 检查禁用(Checking Disabled)= 不验证 DNSSEC
# 检查 ad 标志(DNSSEC 验证成功)
dig @127.0.0.1 example.com A | grep "flags:"
# ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1
#                               ^^ ad = 验证通过

# 检查未签名域名(无 ad 标志)
dig @127.0.0.1 unsigned-domain.com A | grep "flags:"
# ;; flags: qr rd ra; QUERY: 1, ANSWER: 1
#                               没有 ad = 未签名

6.7 密钥轮转(Key Rollover)

6.7.1 ZSK 轮转(Pre-Publish)

# 1. 生成新 ZSK
cd /var/cache/bind/primary/keys
dnssec-keygen -a ECDSAP256SHA256 -n ZONE example.com

# 2. BIND 自动轮转(auto-dnssec maintain 模式下)
# BIND 会在适当时间激活新密钥、停用旧密钥

# 手动触发轮转
sudo rndc dnssec -status example.com

6.7.2 KSK 轮转(Double-DS)

# 1. 生成新 KSK
dnssec-keygen -a ECDSAP256SHA256 -n ZONE -f KSK example.com

# 2. 获取新 DS 记录
dnssec-dsfromkey -2 Kexample.com.+013+NEWKEYID.key

# 3. 向父区域添加新 DS(保留旧 DS)

# 4. 等待 DNS 缓存过期(通常 24-48 小时)

# 5. 从父区域删除旧 DS

# 6. 删除旧 KSK 密钥文件

6.7.3 密钥轮转时间线

ZSK 轮转(约 30 天周期):
Day 0:  新 ZSK 发布(pre-publish)
Day 7:  新 ZSK 激活,旧 ZSK 停用签名
Day 14: 旧 ZSK 移除(撤回)
Day 30: 完成

KSK 轮转(约 6 个月周期):
Day 0:    新 KSK 发布
Day 30:   新 KSK DS 提交给父区域
Day 60:   新 KSK 激活
Day 90:   旧 KSK DS 从父区域移除
Day 120:  旧 KSK 移除

6.8 NSEC3(带盐值的否定存在证明)

6.8.1 NSEC vs NSEC3

特性 NSEC NSEC3
区域遍历 可以遍历所有域名 不可遍历(哈希隐藏)
安全性 区域内容可被枚举 防止枚举
性能 较好 稍差
推荐场景 小型内部区域 公网权威服务器

6.8.2 使用 NSEC3 签名

# 手动签名时使用 -3 参数
dnssec-signzone -A -3 $(head -c 1024 /dev/urandom | sha1sum | cut -b 1-16) \
    -N INCREMENT -o example.com -t \
    -K keys/ \
    example.com.zone

6.9 DNSSEC 故障排查

6.9.1 常见错误

错误 原因 解决方案
SERVFAIL + ad 标志缺失 DNSSEC 验证失败 检查签名是否过期
RRSIG expired 签名已过期 重新签名
no valid RRSIG 签名数据损坏 重新签名
DS not found 父区域缺少 DS 记录 向注册商添加 DS
DNSKEY not found 区域缺少 DNSKEY 生成并发布密钥

6.9.2 诊断命令

# 1. 检查区域签名状态
sudo rndc signing -list example.com

# 2. 使用 delv 验证
delv @127.0.0.1 example.com A +rtrace

# 3. 在线验证工具
# https://dnsviz.net/d/example.com/dnssec/
# https://dnssec-analyzer.verisignlabs.com/example.com

# 4. 检查 RRSIG 过期时间
dig @127.0.0.1 example.com RRSIG +dnssec | grep "RRSIG"
# 查看最后一行的过期时间

# 5. 检查密钥状态
sudo rndc dnssec -status example.com

6.10 完整 DNSSEC 配置示例

// /etc/bind/named.conf —— 带 DNSSEC 的权威服务器

options {
    directory "/var/cache/bind";
    listen-on port 53 { any; };
    listen-on-v6 port 53 { any; };
    
    recursion no;
    allow-query { any; };
    
    // DNSSEC 验证(权威服务器通常不需要)
    dnssec-validation no;
    
    pid-file "/run/named/named.pid";
};

zone "example.com" {
    type primary;
    file "primary/example.com.zone";
    
    // 自动 DNSSEC 签名
    inline-signing yes;
    auto-dnssec maintain;
    key-directory "primary/keys";
    
    // 允许区域传输
    allow-transfer { key transfer-key; };
};

6.11 本章小结

概念 要点
DNSSEC 为 DNS 提供数据完整性和来源验证
ZSK 签名区域记录,短密钥,频繁轮转
KSK 签名 DNSKEY,长密钥,不频繁轮转
DS 记录 父区域存储的子区域密钥哈希,建立信任链
RRSIG 资源记录的数字签名
inline-signing BIND 自动签名,推荐方式
auto-dnssec BIND 自动维护密钥轮转

💡 小技巧

  1. 使用 ECDSA 算法 13:密钥短、签名快、安全性高。
  2. inline-signing + auto-dnssec 是生产环境推荐方案。
  3. 密钥文件权限必须 600:私钥泄露 = 安全全毁。
  4. 定期检查签名过期时间:避免签名过期导致解析失败。
  5. KSK 轮转前先提交新 DS:否则会中断域名解析。

📖 扩展阅读