土法炼钢兴趣小组的算法知识备份

【网络工程】DNSSEC 工程:签名、验证与信任链

文章导航

分类入口
network
标签入口
#dns#dnssec#rrsig#dnskey#trust-chain#ksk#zsk

目录

DNS 协议在设计时没有任何安全机制——查询和应答都是明文传输,没有签名、没有认证、没有加密。任何能拦截 DNS 流量的攻击者都可以伪造应答,将用户导向恶意服务器。这就是 DNS 缓存投毒(Cache Poisoning)和 DNS 劫持的根本原因。

DNSSEC(DNS Security Extensions)通过数字签名解决了 DNS 应答的完整性和真实性问题。从 2005 年 RFC 4033–4035 发布至今已经 20 年,但全球 DNSSEC 签名域名的比例仍然不到 30%,启用验证的递归解析器也仅占约 30%。

为什么?因为 DNSSEC 的工程成本远超预期——密钥管理、签名轮换、响应大小膨胀、调试困难,每一个都是实实在在的运维负担。

本文从工程视角完整拆解 DNSSEC:它怎么工作、怎么部署、怎么排查、以及为什么你可能不需要它。

一、DNS 的安全威胁

1.1 DNS 缓存投毒

DNS 缓存投毒(Cache Poisoning)是 DNSSEC 要解决的核心威胁:

正常解析:
客户端 → 递归解析器 → 权威服务器
                   ← A 记录: 93.184.216.34(真实 IP)

缓存投毒攻击:
                          攻击者
                            │
                            ↓ 伪造应答(抢先于权威服务器)
客户端 → 递归解析器 ←──────┘
                   ← A 记录: 6.6.6.6(恶意 IP)

攻击条件:
1. 猜中查询的 Transaction ID(16 位,65536 种可能)
2. 猜中源端口(如果端口固定则更容易)
3. 在真实应答到达之前发送伪造应答

2008 年 Dan Kaminsky 发现的攻击方法让缓存投毒变得极其高效:通过查询不存在的子域名(如 random123.example.com),攻击者可以在短时间内发送大量伪造的权威应答(包含针对 example.com 的恶意 NS 记录),而不受已缓存记录 TTL 的限制。

1.2 DNSSEC 不解决什么

DNSSEC 解决:
✓ 数据完整性 — 应答内容未被篡改
✓ 来源认证 — 应答确实来自权威服务器
✓ 否定存在证明 — 确认某个域名确实不存在

DNSSEC 不解决:
✗ 保密性 — DNS 查询和应答仍然是明文(这是 DoH/DoT 的工作)
✗ DDoS 防护 — DNSSEC 不防止 DNS 放大攻击
✗ 域名注册安全 — 不防止域名被盗或注册商账户被入侵
✗ 最后一英里安全 — 客户端到递归解析器的链路不受保护

二、DNSSEC 核心记录类型

2.1 RRSIG(Resource Record Signature)

RRSIG 是对一组 DNS 记录的数字签名:

example.com.  300  IN  A      93.184.216.34
example.com.  300  IN  RRSIG  A 13 2 300 (
                20250815000000  ; 签名过期时间
                20250715000000  ; 签名生效时间
                12345           ; Key Tag(标识使用的 DNSKEY)
                example.com.    ; 签名者域名
                base64_encoded_signature... )

RRSIG 字段解析:
- A           : 被签名的记录类型
- 13          : 算法编号(13 = ECDSAP256SHA256)
- 2           : 标签数量(example.com 有 2 个标签)
- 300         : 原始 TTL
- 20250815... : 签名过期时间
- 20250715... : 签名生效时间
- 12345       : Key Tag(快速定位对应的 DNSKEY)
- example.com.: 签名者的域名
- base64...   : 实际的数字签名

2.2 DNSKEY(DNS Public Key)

DNSKEY 记录发布用于验证签名的公钥:

example.com.  86400  IN  DNSKEY  257 3 13 (
                base64_encoded_public_key_KSK... )

example.com.  86400  IN  DNSKEY  256 3 13 (
                base64_encoded_public_key_ZSK... )

字段解析:
- 257 : flags,bit 7 = 1(Zone Key)+ bit 15 = 1(SEP/KSK)
        257 = KSK(Key Signing Key)
- 256 : flags,bit 7 = 1(Zone Key)+ bit 15 = 0
        256 = ZSK(Zone Signing Key)
- 3   : 协议(固定为 3 = DNSSEC)
- 13  : 算法编号(13 = ECDSAP256SHA256)

2.3 DS(Delegation Signer)

DS 记录存放在父域(Parent Zone),是子域 KSK 公钥的哈希值,用于构建信任链:

父域(.com)中的 DS 记录:
example.com.  86400  IN  DS  12345 13 2 (
                sha256_hash_of_ksk_dnskey... )

字段解析:
- 12345 : Key Tag(与 RRSIG 中的 Key Tag 对应)
- 13    : 算法编号
- 2     : 摘要类型(2 = SHA-256)
- sha256... : KSK DNSKEY 记录的 SHA-256 哈希

2.4 NSEC / NSEC3(否定存在证明)

当查询的域名不存在时,DNSSEC 需要证明”这个域名确实不存在”而不是应答被篡改了:

NSEC(Next Secure):
  按字母顺序排列所有域名,每条 NSEC 指向"下一个"存在的域名。
  如果查询的域名在两条 NSEC 之间的间隙中,证明不存在。

  alpha.example.com. NSEC gamma.example.com. A RRSIG NSEC
  → 证明 beta.example.com 不存在(它在 alpha 和 gamma 之间)

  问题: NSEC 允许"Zone Walking"——攻击者可以通过
       遍历 NSEC 链枚举出整个域的所有记录名。

NSEC3(带哈希的否定存在证明):
  不暴露原始域名,而是使用域名的哈希值排序。

  ABC123HASH.example.com. NSEC3 1 0 10 AABB (
      DEF456HASH A RRSIG )
  → 使用哈希后的域名,无法直接推断原始域名

  NSEC3 参数:
  - 1    : 哈希算法(1 = SHA-1)
  - 0    : Opt-Out 标志
  - 10   : 迭代次数
  - AABB : Salt(加盐)

三、信任链(Chain of Trust)

3.1 从根到叶的信任传递

DNSSEC 的信任从根域(Root Zone)开始,逐级向下传递:

信任链:

┌─────────────┐
│  Root Zone  │  DNSKEY(根的 KSK + ZSK)
│   (.)       │  由"信任锚"(Trust Anchor)预置在解析器中
└──────┬──────┘
       │ DS 记录(.com KSK 的哈希)
       │ + RRSIG 签名(用根的 ZSK 签)
       ↓
┌─────────────┐
│  .com TLD   │  DNSKEY(.com 的 KSK + ZSK)
│             │  KSK 的哈希 = 根域中的 DS 记录 → 验证通过
└──────┬──────┘
       │ DS 记录(example.com KSK 的哈希)
       │ + RRSIG 签名(用 .com 的 ZSK 签)
       ↓
┌─────────────┐
│ example.com │  DNSKEY(example.com 的 KSK + ZSK)
│             │  KSK 的哈希 = .com 中的 DS 记录 → 验证通过
└──────┬──────┘
       │ A 记录: 93.184.216.34
       │ + RRSIG 签名(用 example.com 的 ZSK 签)
       ↓
     验证通过 ✓

3.2 KSK 与 ZSK 的分工

为什么需要两把密钥?

如果只用一把密钥:
  - 这把密钥既签名所有记录,又提交 DS 到父域
  - 需要轮换时,必须同时更新本域所有签名 + 父域的 DS 记录
  - 父域的 DS 更新需要跨组织协调(域名注册商 → 注册局)
  - 轮换周期长、操作复杂、风险高

KSK/ZSK 双密钥体系:
  KSK(Key Signing Key):
    - 只签名 DNSKEY 记录集
    - 提交 DS 到父域
    - 密钥强度高,轮换周期长(1–2 年)
    - 可以离线存储

  ZSK(Zone Signing Key):
    - 签名域中的所有其他记录(A/AAAA/MX/CNAME 等)
    - 不需要父域配合
    - 轮换频率高(1–3 个月),不影响信任链
    - 需要在线(签名服务需要使用)
特性 KSK ZSK
签名范围 仅 DNSKEY 记录集 其他所有记录
父域关联 DS 记录引用 KSK 无直接关联
轮换周期 1–2 年 1–3 个月
轮换复杂度 高(需更新父域 DS) 低(仅本域操作)
密钥强度 较强(RSA 2048+ / ECDSA P-256) 可稍弱
存储要求 可离线(HSM) 需在线

3.3 信任锚(Trust Anchor)

整个信任链的起点是预置在递归解析器中的根域 KSK 公钥:

# 查看 BIND 的信任锚配置
cat /etc/bind/bind.keys

# 或查看 Unbound 的信任锚
cat /var/lib/unbound/root.key

# 根域当前的 KSK(2017 年轮换后的 KSK-2017)
# Key Tag: 20326
# Algorithm: 8 (RSA/SHA-256)
# 这个密钥预置在所有支持 DNSSEC 的解析器中

# 查看根域的 DNSKEY
dig . DNSKEY +dnssec +short
# 257 3 8 AwEAAaz/tAm8... (KSK,flags=257)
# 256 3 8 AwEAAeJPf9a... (ZSK,flags=256)

2018 年 10 月的根域 KSK 轮换是互联网历史上最大规模的密钥轮换操作——需要全球所有支持 DNSSEC 的递归解析器更新信任锚。

四、DNSSEC 部署实战

4.1 使用 BIND 签名域

# 环境: BIND 9.16+ 自动 DNSSEC 签名

# 1. 生成密钥对
cd /etc/bind/keys/

# 生成 KSK(ECDSAP256SHA256,推荐)
dnssec-keygen -a ECDSAP256SHA256 -f KSK example.com
# 输出: Kexample.com.+013+12345

# 生成 ZSK
dnssec-keygen -a ECDSAP256SHA256 example.com
# 输出: Kexample.com.+013+67890
# named.conf 配置
zone "example.com" {
    type primary;
    file "/etc/bind/zones/example.com.zone";

    # 启用自动 DNSSEC 签名
    dnssec-policy "default";

    # 或使用自定义策略
    dnssec-policy "custom-policy";

    # 密钥目录
    key-directory "/etc/bind/keys/";

    # 启用内联签名(推荐)
    inline-signing yes;
    auto-dnssec maintain;
};

# 自定义 DNSSEC 策略
dnssec-policy "custom-policy" {
    keys {
        ksk key-directory lifetime P1Y algorithm ecdsap256sha256;
        zsk key-directory lifetime P3M algorithm ecdsap256sha256;
    };
    max-zone-ttl P1D;
    zone-propagation-delay PT5M;
    parent-ds-ttl P1D;
    parent-propagation-delay PT1H;

    # 签名有效期
    signatures-validity P14D;           # 签名有效 14 天
    signatures-refresh P5D;             # 在过期前 5 天重新签名

    # NSEC3 配置
    nsec3param iterations 0 optout no salt-length 0;
};

4.2 使用 dnssec-signzone 手动签名

# 手动签名流程(适合理解原理)

# 1. 准备未签名的区域文件
cat > example.com.zone << 'EOF'
$TTL 300
@   IN  SOA   ns1.example.com. admin.example.com. (
            2025073001  ; serial
            3600        ; refresh
            900         ; retry
            604800      ; expire
            300         ; minimum
)
    IN  NS    ns1.example.com.
    IN  NS    ns2.example.com.
    IN  A     93.184.216.34
    IN  MX    10 mail.example.com.

ns1 IN  A     93.184.216.1
ns2 IN  A     93.184.216.2
www IN  A     93.184.216.34
mail IN A     93.184.216.10
EOF

# 2. 签名
dnssec-signzone \
  -A \                              # 自动生成 NSEC 记录
  -3 $(head -c 8 /dev/urandom | od -A n -t x1 | tr -d ' ') \  # NSEC3 带随机 salt
  -N INCREMENT \                    # 自动递增 serial
  -o example.com \                  # 域名
  -t \                              # 打印签名统计
  -K /etc/bind/keys/ \              # 密钥目录
  example.com.zone

# 输出:
# Verifying the zone using the following algorithms: ECDSAP256SHA256.
# Zone fully signed:
# Algorithm: ECDSAP256SHA256: KSKs: 1 active, 0 stand-by, 0 revoked
#                             ZSKs: 1 active, 0 stand-by, 0 revoked
# example.com.zone.signed

# 3. 签名后的文件会包含 RRSIG、DNSKEY、NSEC3 等记录
head -30 example.com.zone.signed

4.3 向注册商提交 DS 记录

签名完成后,需要向域名注册商提交 DS 记录:

# 生成 DS 记录
dnssec-dsfromkey -2 Kexample.com.+013+12345.key
# example.com. IN DS 12345 13 2 E2D3C916...(SHA-256 摘要)

# 需要提交的信息:
# Key Tag:   12345
# Algorithm: 13 (ECDSAP256SHA256)
# Digest Type: 2 (SHA-256)
# Digest:    E2D3C916...

# 通过注册商管理面板或 API 提交
# 各注册商界面不同,通常在 DNS 管理 → DNSSEC 设置中

4.4 验证部署结果

# 1. 基本验证
dig example.com +dnssec +short
# 93.184.216.34
# A 13 2 300 20250815000000 20250715000000 67890 example.com. signature...

# 2. 查看所有 DNSSEC 记录
dig example.com DNSKEY +dnssec
dig example.com DS +trace
dig example.com A +dnssec +cd  # cd = Checking Disabled

# 3. 验证信任链
# 使用 delv(BIND 自带的 DNSSEC 验证工具)
delv @8.8.8.8 example.com A +rtrace
# ;; fetch: example.com/A
# ;; fetch: example.com/DNSKEY
# ;; fetch: example.com/DS
# ;; fetch: com/DNSKEY
# ;; fetch: com/DS
# ;; fetch: ./DNSKEY
# ; fully validated
# example.com.    300  IN  A  93.184.216.34

# 4. 使用 drill 验证(ldns 工具链)
drill -DT example.com
# ;; Chase successful

# 5. 使用 unbound-host 验证
unbound-host -D -v example.com
# example.com has address 93.184.216.34 (secure)

# 6. 在线验证工具
# https://dnsviz.net/        — 可视化信任链
# https://dnssec-analyzer.verisignlabs.com/  — 详细分析

五、密钥轮换(Key Rollover)

5.1 ZSK 轮换(Pre-Publication 方法)

ZSK 轮换不需要更新父域的 DS 记录,可以在本域独立完成:

ZSK 轮换时间线(Pre-Publication 方法):

阶段 1: 预发布(Pre-Publish)
  T=0: 生成新 ZSK,发布到 DNSKEY 记录集
       但仍用旧 ZSK 签名所有记录
  ┌──────────────────┐
  │ DNSKEY:          │
  │   KSK (不变)     │
  │   ZSK-old (签名) │
  │   ZSK-new (待用) │  ← 新 ZSK 已发布但未使用
  │                  │
  │ RRSIG: ZSK-old   │  ← 仍用旧 ZSK 签名
  └──────────────────┘
  等待时间: ≥ DNSKEY TTL(让所有缓存获取到新 DNSKEY)

阶段 2: 切换签名
  T=DNSKEY_TTL: 使用新 ZSK 重新签名所有记录
  ┌──────────────────┐
  │ DNSKEY:          │
  │   KSK (不变)     │
  │   ZSK-old (保留) │  ← 旧 ZSK 仍然发布
  │   ZSK-new (签名) │  ← 新 ZSK 开始签名
  │                  │
  │ RRSIG: ZSK-new   │  ← 新签名
  └──────────────────┘
  等待时间: ≥ 最大 RRSIG 有效期(让旧签名全部过期)

阶段 3: 移除旧密钥
  T=RRSIG_lifetime: 从 DNSKEY 记录集中移除旧 ZSK
  ┌──────────────────┐
  │ DNSKEY:          │
  │   KSK (不变)     │
  │   ZSK-new (签名) │  ← 只剩新 ZSK
  │                  │
  │ RRSIG: ZSK-new   │
  └──────────────────┘

5.2 KSK 轮换(Double-DS 方法)

KSK 轮换需要与父域协调更新 DS 记录:

KSK 轮换时间线(Double-DS 方法):

阶段 1: 新 KSK 生成 + DS 预发布
  T=0: 生成新 KSK,向父域提交新 KSK 的 DS
  ┌──────────────────────┐    ┌─────────────────────┐
  │ 本域 DNSKEY:         │    │ 父域 DS:            │
  │   KSK-old (签名)     │    │   DS(KSK-old)       │
  │   KSK-new (待用)     │    │   DS(KSK-new)  ←新  │
  │   ZSK (不变)         │    └─────────────────────┘
  └──────────────────────┘
  等待: 父域 DS 的 TTL(让所有缓存获取到两个 DS)

阶段 2: 切换 KSK 签名
  T=parent_DS_TTL: 用新 KSK 签名 DNSKEY 记录集
  ┌──────────────────────┐    ┌─────────────────────┐
  │ 本域 DNSKEY:         │    │ 父域 DS:            │
  │   KSK-old (保留)     │    │   DS(KSK-old)       │
  │   KSK-new (签名) ←   │    │   DS(KSK-new)       │
  │   ZSK (不变)         │    └─────────────────────┘
  └──────────────────────┘
  等待: DNSKEY TTL

阶段 3: 移除旧 KSK 和旧 DS
  T=final: 从本域移除 KSK-old,从父域移除 DS(KSK-old)
  ┌──────────────────────┐    ┌─────────────────────┐
  │ 本域 DNSKEY:         │    │ 父域 DS:            │
  │   KSK-new (签名)     │    │   DS(KSK-new)       │
  │   ZSK (不变)         │    └─────────────────────┘
  └──────────────────────┘

5.3 算法轮换

当需要从旧算法(如 RSA/SHA-256)迁移到新算法(如 ECDSA P-256)时,轮换更复杂:

# 算法轮换要求: 在转换期间必须用两套算法同时签名所有记录
# 因为旧验证器可能不支持新算法

# 1. 生成新算法的 KSK 和 ZSK
dnssec-keygen -a ECDSAP256SHA256 -f KSK example.com
dnssec-keygen -a ECDSAP256SHA256 example.com

# 2. 用新旧两套密钥同时签名
# BIND inline-signing 会自动处理

# 3. 向父域提交新算法的 DS
# 等待传播

# 4. 确认新算法验证通过后,移除旧算法密钥

# 注意: 绝不能在移除旧算法签名之前移除旧 DS
#       否则使用旧缓存的解析器会验证失败

六、DNSSEC 验证失败排查

6.1 常见故障模式

DNSSEC 验证失败的常见原因:

1. 签名过期(RRSIG Expired)
   - 签名的有效期已过
   - 原因: 签名服务故障,未按时重新签名
   - 诊断: dig +dnssec,检查 RRSIG 的过期时间

2. DS 与 DNSKEY 不匹配
   - 父域的 DS 记录与本域 DNSKEY 的哈希不一致
   - 原因: KSK 轮换不完整,或 DS 记录输入错误
   - 诊断: 手动计算 DS 哈希并与父域的 DS 比较

3. 信任链断裂
   - 中间某一级没有 DS 或 DNSKEY
   - 原因: 注册商不支持 DNSSEC,或 DS 被意外删除
   - 诊断: dig +trace +dnssec

4. 时钟偏差
   - 验证器的系统时间在 RRSIG 的 inception/expiration 窗口外
   - 原因: NTP 故障或虚拟机时钟漂移
   - 诊断: 检查系统时间和 RRSIG 时间窗口

5. 算法不支持
   - 验证器不支持签名使用的算法
   - 原因: 使用了太新的算法(如 Ed25519 在旧解析器上)
   - 诊断: 检查验证器支持的算法列表

6.2 诊断命令

# 1. 检查 RRSIG 签名时间
dig example.com A +dnssec +noall +answer +comments
# 查看 RRSIG 中的 inception 和 expiration 时间
# 如果 expiration 已过去 → 签名过期

# 2. 追踪信任链
dig example.com A +trace +dnssec
# 逐级显示从根到目标域的解析过程和 DNSSEC 记录

# 3. 使用 delv 做完整验证
delv @8.8.8.8 example.com A +rtrace +vtrace
# +rtrace: 显示查询追踪
# +vtrace: 显示验证追踪(详细显示每一步验证逻辑)

# 4. 验证 DS 与 DNSKEY 匹配
# 获取 DNSKEY
dig example.com DNSKEY +short > dnskey.txt
# 手动计算 DS
dnssec-dsfromkey -f dnskey.txt -2 example.com
# 与父域的 DS 比较
dig example.com DS +short

# 5. 用 Google DNS 的 DNSSEC 调试
dig @8.8.8.8 example.com A +cd  # Checking Disabled
# 如果 +cd 能解析但不加 +cd 返回 SERVFAIL
# → 确认是 DNSSEC 验证失败

# 6. 检查否定应答
dig @8.8.8.8 nonexist.example.com A +dnssec
# 查看 NSEC/NSEC3 记录是否正确

6.3 紧急恢复

当 DNSSEC 验证失败导致域名全球不可解析时:

# 紧急情况: DNSSEC 验证失败 → 域名对启用 DNSSEC 验证的
#           递归解析器完全不可解析(SERVFAIL)

# 方案一(快速但不推荐): 从注册商删除 DS 记录
#   效果: 域名变为"非 DNSSEC 签名"状态
#   风险: 降级攻击窗口
#   生效时间: 取决于父域 DS 的 TTL(通常 24-48 小时)
#   这在紧急情况下太慢了

# 方案二(推荐): 修复签名
#   1. 如果签名过期: 立即重新签名
rndc sign example.com    # BIND 命令
#   2. 如果 DS 不匹配: 更新 DS 记录
#      生成正确的 DS 并提交给注册商

# 方案三: 降低影响范围
#   在修复期间,通过 CDN 或备用域名维持服务
#   通知用户使用不验证 DNSSEC 的解析器(如临时方案)

# 预防措施:
# 1. 监控签名过期时间(至少在过期前 7 天告警)
# 2. 自动化重签名(BIND dnssec-policy 自动处理)
# 3. 测试环境定期模拟密钥轮换

七、DNSSEC 的工程成本

7.1 响应大小膨胀

DNSSEC 显著增大 DNS 响应:

记录类型 无 DNSSEC 有 DNSSEC 增幅
A 记录查询 ~60 字节 ~400 字节 6.7x
DNSKEY 查询 N/A ~800 字节
否定应答 ~100 字节 ~600 字节 6x
DS 查询 N/A ~300 字节

工程影响:

1. UDP 512 字节限制
   DNSSEC 应答经常超过 512 字节 → 需要 EDNS0 支持更大 UDP 包
   或回退到 TCP → 增加延迟和服务器负载

2. DNS 放大攻击
   DNSSEC 域名的放大倍数更高
   攻击者发送 60 字节请求 → 反射 800 字节响应
   放大倍数: ~13x(无 DNSSEC 时约 4x)

3. 带宽消耗
   权威服务器的出站带宽增加 5–10 倍
   高 QPS 域名需要更多带宽预算

4. 分片风险
   超过 MTU(通常 1500 字节)的 UDP 响应会被分片
   某些防火墙/NAT 会丢弃 DNS 分片包
   → 导致解析失败

7.2 运维复杂度

DNSSEC 带来的额外运维工作:

密钥管理:
  - 生成、存储、备份密钥对
  - 定期轮换 ZSK(每 1–3 个月)
  - 定期轮换 KSK(每 1–2 年)
  - 跨组织协调 DS 记录更新
  - KSK 私钥的安全存储(理想情况下用 HSM)

签名管理:
  - 确保签名在过期前重新生成
  - 监控签名有效期
  - 区域文件变更后自动重签名

监控:
  - 签名过期告警
  - 验证成功率监控
  - DS 记录一致性检查
  - 信任链完整性验证

故障恢复:
  - DNSSEC 故障的影响比普通 DNS 故障更严重
    (域名对验证解析器完全不可解析)
  - 恢复时间受限于 TTL 和父域更新速度

7.3 部署决策

应该部署 DNSSEC 的场景:
  ✓ 金融、政府等高安全要求的域名
  ✓ 域名注册商和 DNS 服务商(基础设施责任)
  ✓ 使用托管 DNS 服务(CloudFlare/Route53 自动管理)
  ✓ 已有自动化 DNS 管理基础设施
  ✓ 需要 DANE(基于 DNS 的证书认证)

可以暂缓的场景:
  ✗ 小型网站(投入产出比低)
  ✗ DNS 基础设施由第三方管理且不支持 DNSSEC
  ✗ 运维团队没有 DNSSEC 经验且无培训计划
  ✗ 已经使用 DoH/DoT(部分安全需求已被覆盖)

八、托管 DNS 的 DNSSEC 支持

8.1 主流 DNS 服务商的 DNSSEC 支持

服务商 DNSSEC 支持 自动密钥管理 自动 DS 发布
Cloudflare ✓(一键启用) 需手动提交 DS
AWS Route 53 ✓(KMS 集成) 需手动提交 DS
Google Cloud DNS 需手动提交 DS
Azure DNS ✗(截至 2025)
NS1 需手动提交 DS

8.2 Cloudflare 一键部署

# Cloudflare 的 DNSSEC 部署最简单:

# 1. 在 Cloudflare Dashboard → DNS → DNSSEC → 点击"Enable DNSSEC"
# Cloudflare 自动:
#   - 生成 KSK 和 ZSK(ECDSAP256SHA256)
#   - 签名所有记录
#   - 自动轮换 ZSK
#   - 自动重签名

# 2. Cloudflare 提供 DS 记录信息
# 需要你手动到域名注册商处提交:
#   Key Tag:   2371
#   Algorithm: 13
#   Digest Type: 2
#   Digest:    ...

# 3. 验证
dig example.com +dnssec
# 应该能看到 RRSIG 记录

# Cloudflare 的优势:
# - 完全自动化的密钥管理
# - 在线签名(不需要预签名整个区域)
# - NSEC3 + Aggressive NSEC 缓存
# - 无额外费用

九、DNSSEC 算法选型

9.1 算法对比

算法 编号 密钥大小 签名大小 性能 状态
RSA/SHA-1 5 1024–4096 bit 128–512 字节 已废弃
RSA/SHA-256 8 2048–4096 bit 256–512 字节 仍在使用
RSA/SHA-512 10 2048–4096 bit 256–512 字节 更慢 较少使用
ECDSA P-256 13 256 bit 64 字节 推荐
ECDSA P-384 14 384 bit 96 字节 可用
Ed25519 15 256 bit 64 字节 最快 支持有限
Ed448 16 448 bit 114 字节 极少使用

9.2 推荐选型

2025 年推荐:
  首选: ECDSAP256SHA256(算法 13)
    ✓ 签名小(64 字节 vs RSA 的 256+ 字节)
    ✓ 验证快
    ✓ 所有主流解析器都支持
    ✓ 减少响应大小膨胀

  次选: Ed25519(算法 15)
    ✓ 性能最好
    ✓ 签名同样小(64 字节)
    ✗ 部分旧解析器不支持(约 5% 不支持)
    → 如果你的用户群主要使用现代解析器,可以选择

  避免: RSA/SHA-256(算法 8)
    ✗ 签名大(256+ 字节),增加响应大小
    ✗ 验证慢
    ✓ 唯一优势: 兼容性最好
    → 仅在需要兼容非常旧的解析器时使用

十、DNSSEC 监控

10.1 签名过期监控脚本

#!/bin/bash
# check_dnssec_expiry.sh — 监控 DNSSEC 签名过期
# 在签名过期前 7 天、3 天、1 天分别告警

DOMAIN="${1:-example.com}"
WARNING_DAYS=7
CRITICAL_DAYS=3
EMERGENCY_DAYS=1

# 获取最早过期的 RRSIG
EXPIRY=$(dig "$DOMAIN" A +dnssec +noall +answer | \
  grep RRSIG | \
  awk '{print $9}' | \
  sort | head -1)

if [ -z "$EXPIRY" ]; then
  echo "UNKNOWN: No RRSIG found for $DOMAIN"
  exit 3
fi

# 解析过期时间 (YYYYMMDDHHMMSS)
EXPIRY_EPOCH=$(date -d "${EXPIRY:0:8} ${EXPIRY:8:2}:${EXPIRY:10:2}:${EXPIRY:12:2}" +%s 2>/dev/null)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))

if [ "$DAYS_LEFT" -le "$EMERGENCY_DAYS" ]; then
  echo "EMERGENCY: $DOMAIN RRSIG expires in $DAYS_LEFT days ($EXPIRY)"
  exit 2
elif [ "$DAYS_LEFT" -le "$CRITICAL_DAYS" ]; then
  echo "CRITICAL: $DOMAIN RRSIG expires in $DAYS_LEFT days ($EXPIRY)"
  exit 2
elif [ "$DAYS_LEFT" -le "$WARNING_DAYS" ]; then
  echo "WARNING: $DOMAIN RRSIG expires in $DAYS_LEFT days ($EXPIRY)"
  exit 1
else
  echo "OK: $DOMAIN RRSIG expires in $DAYS_LEFT days ($EXPIRY)"
  exit 0
fi

10.2 Prometheus 指标

# 使用 Blackbox Exporter 监控 DNSSEC 验证
modules:
  dns_dnssec:
    prober: dns
    timeout: 10s
    dns:
      query_name: "example.com"
      query_type: "A"
      valid_rcodes:
        - NOERROR
      validate_answer_rrs:
        fail_if_not_matches_regexp:
          - ".*RRSIG.*"   # 确保应答包含 RRSIG

# 告警规则
# groups:
# - name: dnssec
#   rules:
#   - alert: DnssecValidationFailed
#     expr: probe_dns_lookup_time_seconds{module="dns_dnssec"} == 0
#            and probe_success{module="dns_dnssec"} == 0
#     for: 5m
#     labels:
#       severity: critical
#     annotations:
#       summary: "DNSSEC validation failed for {{ $labels.instance }}"

十一、总结

DNSSEC 从技术上优雅地解决了 DNS 数据完整性问题,但它的工程成本让大多数组织望而却步:

  1. 理解威胁模型再做决策。 DNSSEC 防的是 DNS 缓存投毒和应答篡改,不防窃听(那是 DoH/DoT 的事)。如果你的主要威胁是中间人攻击和 DNS 劫持,DNSSEC 是正确的解决方案。如果主要是隐私问题,DoH/DoT 更合适。

  2. KSK/ZSK 双密钥体系是运维的核心。 ZSK 频繁轮换不需要跨组织协调,KSK 轮换需要与注册商/注册局协调更新 DS 记录。理解这个分工才能设计出可维护的密钥管理流程。

  3. 响应大小膨胀是最大的技术代价。 ECDSA(算法 13)的签名只有 64 字节,比 RSA 的 256+ 字节小得多。选择 ECDSA P-256 不仅是性能考虑,更是减少 DNSSEC 对 DNS 基础设施影响的关键选择。

  4. 如果用托管 DNS,DNSSEC 几乎免费。 Cloudflare 等服务商提供一键部署和全自动密钥管理,将运维成本从”需要 DNSSEC 专家”降低到”点一下按钮”。唯一的手动步骤是向注册商提交 DS 记录。

  5. 签名过期是最危险的故障。 一旦 RRSIG 过期,域名对所有启用 DNSSEC 验证的解析器完全不可解析——比普通 DNS 故障更严重。自动化重签名和过期监控是部署 DNSSEC 的硬性前提。

下一篇我们讨论加密 DNS——DoH、DoT 和 DoQ 如何解决 DNSSEC 不覆盖的 DNS 隐私问题,以及它们在企业环境中的工程部署。


上一篇:DNS 性能优化:预取、TTL 策略与本地缓存

下一篇:加密 DNS:DoH、DoT 与 DoQ 的工程部署

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-22 · network

网络工程索引

汇总本站网络工程系列文章,覆盖分层模型、以太网、IP、TCP、DNS、TLS、HTTP/2/3、CDN、BGP 与故障诊断。

2025-08-22 · network

【网络工程】全局负载均衡:GSLB、DNS 调度与 Anycast

系统讲解全局负载均衡的工程实现:GeoDNS 的原理与精度问题、Anycast 的路由收敛与故障转移、BGP 社区在流量调度中的应用、多活架构的流量切换策略,建立跨地域流量调度的完整知识体系。

2025-07-28 · network

【网络工程】DNS 协议解剖:查询格式、记录类型与响应码

DNS 是互联网最基础的目录服务,也是最脆弱的单点之一。本文从 wire format 出发逐字段解析 DNS 报文结构,详解 A/AAAA/CNAME/MX/SRV/TXT/NS/SOA 等记录类型的工程用途,分析 EDNS0 扩展与 DNS over TCP 的触发条件,结合 dig +trace 完整实操展示 DNS 解析的真实链路。


By .