强曰为道

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

第 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 记录类型

记录类型全称用途
RRSIGResource Record Signature资源记录的数字签名
DNSKEYDNS Key公钥,用于验证签名
DSDelegation Signer父区域存储的子区域密钥哈希
NSECNext Secure证明域名不存在(按序遍历)
NSEC3Next 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

密钥类型全称用途密钥长度轮转周期
KSKKey Signing Key签名 DNSKEY 记录,用于父区域 DS2048 bit1-2 年
ZSKZone Signing Key签名其他资源记录1024 bit1-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 推荐算法

算法编号算法名称密钥长度说明
13ECDSAP256SHA256256 bit (等效 128 位安全)推荐,性能好,广泛支持
14ECDSAP384SHA384384 bit (等效 192 位安全)高安全需求
8RSASHA2562048 bit传统,兼容性最好
15ED25519256 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

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

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-signingBIND 自动签名,推荐方式
auto-dnssecBIND 自动维护密钥轮转

💡 小技巧

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

📖 扩展阅读