跳转至

DNSSEC 支持(试验中)

仅在装有bind9-utils的linux环境下可用

DNSBuilder 自动支持 DNSSEC 签名和密钥管理

功能特性

  • 自动签名链构建:自动建立从根到叶的信任链
  • 密钥自动生成:KSK 和 ZSK 自动生成和管理
  • DS 记录自动传播:子区 DS 记录自动添加到父区
  • 透明集成:无需手动配置,自动处理所有 DNSSEC 相关事务
  • DNSSEC Hooks:支持在签名过程中注入自定义脚本,用于漏洞复现场景
  • 预生成密钥支持:支持使用预先生成的密钥,便于控制 key tag 和密钥复用

使用预生成密钥

通过 dnssec.include 字段指定包含预生成密钥的目录:

builds:
  root:
    image: bind
    dnssec:
      enable: true
      include: "resource:/keys/root"  # 密钥目录

密钥文件命名

系统会扫描目录中以下格式的密钥文件:

推荐格式:

<zone>.ksk.key       # KSK 公钥
<zone>.ksk.private   # KSK 私钥
<zone>.zsk.key       # ZSK 公钥
<zone>.zsk.private   # ZSK 私钥

BIND 标准格式:

K<zone>.+<alg>+<keytag>.key
K<zone>.+<alg>+<keytag>.private

系统会自动识别 KSK(flags: 257)和 ZSK(flags: 256)。

使用场景

1. 控制特定 Key Tag

# 暴力生成特定 key tag 的密钥
while true; do
  dnssec-keygen -a ECDSAP256SHA256 -n ZONE example.com
  # 检查生成的 key tag 是否符合要求
done

2. 密钥复用

多次构建使用同一套密钥,保持 DS 记录不变:

builds:
  root:
    image: bind
    dnssec:
      enable: true
      include: "resource:/persistent_keys/root"

3. 模拟密钥泄露

使用已知的"泄露"密钥进行漏洞复现:

builds:
  compromised:
    image: bind
    dnssec:
      enable: true
      include: "resource:/leaked_keys/compromised"

Fallback 行为

如果 include 目录不存在或没有找到有效密钥,系统会: 1. 输出警告日志 2. 自动 fallback 到密钥生成 3. 继续正常签名流程

工作原理

签名流程

  1. 密钥生成阶段

    • 为每个区域生成 KSK(Key Signing Key)
    • 为每个区域生成 ZSK(Zone Signing Key)
    • 密钥存储在key:/ 文件系统中
  2. 区域签名阶段

    • 使用 ZSK 签名区域数据
    • 使用 KSK 签名 DNSKEY 记录集
    • 生成签名区域文件(.signed
  3. 信任链建立

    • 从子区 KSK 生成 DS 记录
    • DS 记录自动添加到父区
    • 根区配置信任锚点(Trust Anchor)

DNSSEC Hooks

DNSSEC Hooks 允许在签名过程的关键节点注入自定义 Python 脚本,主要用于 DNS 漏洞复现场景。

签名流程

┌─────────────────────────────────────────────────────────────────┐
│                        DNSSEC 签名流程                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 首次签名阶段 (每个 zone 独立执行)                            │
│     ┌──────────────┐                                            │
│     │ 生成 zone 文件 │                                           │
│     └──────┬───────┘                                            │
│            ↓                                                    │
│     ┌──────────────┐                                            │
│     │写入 unsigned │  → temp:/services/.../db.<zone>.unsigned   │
│     └──────┬───────┘                                            │
│            ↓                                                    │
│     ┌──────────────┐                                            │
│     │   pre hook   │  ← 可修改: temp:/.../db.<zone>.unsigned    │
│     └──────┬───────┘                                            │
│            ↓                                                    │
│     ┌──────────────┐                                            │
│     │  DNSSEC 签名  │                                            │
│     └──────┬───────┘                                            │
│            ↓                                                    │
│     ┌──────────────┐                                            │
│     │ 写入 key:/   │  ← 密钥、DS 记录写入 key:/ 文件系统          │
│     │ 写入 signed  │  → temp:/services/.../db.<zone>            │
│     └──────────────┘                                            │
│                                                                 │
│  2. Re-signing 阶段 (建立信任链,父 zone 签名子 zone 的 DS)       │
│     ┌──────────────┐                                            │
│     │   mid hook   │  ← 可修改: key:/ (注入伪造 DS、修改密钥)     │
│     └──────┬───────┘                                            │
│            ↓                                                    │
│     ┌──────────────┐                                            │
│     │  Re-signing  │  ← 父 zone 重新签名,包含子 zone 的 DS       │
│     └──────┬───────┘                                            │
│            ↓                                                    │
│     ┌──────────────┐                                            │
│     │  post hook   │  ← 可修改: temp:/services/... (最终签名结果) │
│     └──────────────┘                                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

配置格式

builds:
  root:
    image: bind
    ref: std:auth
    dnssec:
      enable: true
      hooks:
        pre: |
          # 修改未签名的 zone 内容
          print(f"Zone: {zone.fqdn}")
          # unsigned_content = "modified..."

        mid: |
          # 注入伪造的 DS 记录到 key:/
          fake_ds = "malicious.com. IN DS 99999 13 2 DEADBEEF..."
          fs.write_text("key:/root/malicious.ds", fake_ds)

        post: |
          # 修改最终的签名结果
          signed_path = f"temp:/services/{service_name}/zones/{zone.filename}"
          content = fs.read_text(signed_path)
          # fs.write_text(signed_path, modified_content)

Hooks 详细说明

Hook 执行时机 可修改内容 执行次数
pre 单个 zone 签名前 temp:/services/.../db.<zone>.unsigned (未签名的 zone 文件) 每个 zone 一次
mid 所有 zone 签名完成后,re-signing 前 key:/ 文件系统 (DS 记录、密钥) 每个 zone 一次
post Re-signing 完成后 temp:/services/... 文件系统 (最终签名结果) 每个 zone 一次

可用变量

所有 hooks 都可以访问以下变量:

变量名 类型 说明
zone ZoneName 区域名对象,可通过 zone.fqdn 获取 example.com.zone.label 获取 example.com
service_name str 服务名称
fs FileSystem 文件系统对象
workdir DNSBPath 工作目录路径
config dict 当前服务的完整配置

mid hook 额外变量: | 变量名 | 类型 | 说明 | |--------|------|------| | zone_graph | dict | 所有 zone 的依赖关系图 |

post hook 额外变量: | 变量名 | 类型 | 说明 | |--------|------|------| | zone_graph | dict | 所有 zone 的依赖关系图 |

文件系统路径

密钥存储(key:/) — 用于 DNSSEC re-signing 时读取:

路径 用途 可用阶段
key:/<service>/<zone>.ksk.key KSK 公钥 mid, post
key:/<service>/<zone>.ksk.private KSK 私钥 mid, post
key:/<service>/<zone>.zsk.key ZSK 公钥 mid, post
key:/<service>/<zone>.zsk.private ZSK 私钥 mid, post
key:/<service>/<zone>.ds DS 记录 mid, post

服务目录(temp:/services/) — zone 文件存储:

路径 用途 可用阶段
temp:/services/<service>/zones/db.<zone>.unsigned 未签名的 zone 文件 pre, post
temp:/services/<service>/zones/db.<zone> 签名后的 zone 文件 post

使用场景

1. 修改签名前的 zone 内容 (pre)

hooks:
  pre: |
    # 直接操作文件系统修改未签名的 zone 文件
    unsigned_path = f"temp:/services/{service_name}/zones/{zone.filename}.unsigned"
    content = fs.read_text(unsigned_path)
    # 缩短所有 TTL 值
    modified = content.replace('3600', '300')
    fs.write_text(unsigned_path, modified)

2. 注入伪造的 DS 记录 (mid)

hooks:
  mid: |
    # 写入伪造的 DS 记录
    fake_ds = "malicious.com. IN DS 99999 13 2 DEADBEEF123456..."
    fs.write_text("key:/root/malicious.ds", fake_ds)

    # 或修改已有的 DS 文件
    # existing = fs.read_text("key:/tld/com.ds")
    # fs.write_text("key:/tld/com.ds", existing + "\n" + fake_ds)

3. 修改最终签名结果 (post)

hooks:
  post: |
    # 读取签名后的 zone 文件
    signed_path = f"temp:/services/{service_name}/zones/{zone.filename}"
    content = fs.read_text(signed_path)

    # 进行修改(例如添加额外的记录)
    # modified = content + "\nextra.example.com. IN A 1.2.3.4"
    # fs.write_text(signed_path, modified)

4. 密钥泄露模拟 (mid)

hooks:
  mid: |
    # 将私钥复制到临时目录,模拟泄露
    ksk_private = fs.read_text(f"key:/{service_name}/{zone.label}.ksk.private")
    fs.write_text(f"temp:/leaked_keys/{zone.label}.ksk.private", ksk_private)
    print(f"[WARNING] Key leaked to temp:/leaked_keys/")

注意事项

  1. 执行顺序:Hooks 按照 pre → mid → post 的顺序执行
  2. 错误处理:如果 hook 执行失败,整个签名过程会停止
  3. 安全性:Hooks 具有完整的文件系统访问权限,请谨慎使用

相关文档

参考资源