安装 DNS 安全扩展(DNSSEC)

DNSSEC 是一组 DNS 安全扩展,用于验证 DNS 数据的真实性和完整性。本指南介绍如何在已有的 BIND9 DNS 服务器部署中,为现有区域启用 DNSSEC:通过为已签名区域提供 DS、DNSKEY、RRSIG 等附加记录来实现。

起点

本指南以已经部署了权威区域的 BIND9 DNS 服务器为基础。部署方法见 DNS 操作指南。不过有一个关键区别:区域文件必须位于服务器能够写入的目录,例如 /var/lib/bind,而不是 /etc/bind。

以下简要步骤使用示例域名 example.internal,将服务器配置到这一状态。首先安装 bind9:

sudo apt install bind9 -y

编辑 /etc/bind/named.conf.local,添加以下区域定义:

zone "example.internal" {
    type master;
    file "/var/lib/bind/db.example.internal";
};

创建 /var/lib/bind/db.example.internal,内容如下:

$TTL 86400      ; 1 day
example.internal.       IN SOA  example.internal. root.example.internal. (
                                1          ; serial
                                43200      ; refresh (12 hours)
                                900        ; retry (15 minutes)
                                1814400    ; expire (3 weeks)
                                7200       ; minimum (2 hours)
                                )
example.internal.       IN  NS      ns.example.internal.
ns                      IN  A       192.168.1.10
noble                   IN  A       192.168.1.11

重新启动服务:

sudo systemctl restart named

检查服务能否解析 noble.example.internal:

dig @127.0.0.1 +short noble.example.internal
192.168.1.11

启用 DNSSEC

为区域启用 DNSSEC 涉及多个步骤。BIND9 默认会自动处理这些工作,需要手工完成的很少。转换区域至少需要生成新密钥,并为该区域的全部资源记录签名。但这只是第一天的工作:之后还必须正确维护区域,包括轮换密钥、设置过期时间等。当前版本的 BIND9 使用 DNSSEC 策略处理这些事务,并提供可以直接使用的默认策略。

要将 example.internal 迁移到 DNSSEC,只需在 /etc/bind/named.conf.local 中为区域定义添加两行:

zone "example.internal" {
    type master;
    file "/var/lib/bind/db.example.internal";
    dnssec-policy default;
    inline-signing yes;
};

新增设置的含义:

  • dnssec-policy default:使用默认 DNSSEC 策略。策略包含密钥轮换、默认 TTL 等多项设置。
  • inline-signing yes:为已签名区域保留单独的文件。

修改后不需要重启服务,但需要通知服务重新加载配置,可以使用 rndc:

sudo rndc reconfig

服务器会立即发现新配置,开始为 example.internal 区域签名。日志会显示进度,可以这样查看:

sudo journalctl -u named.service -f

日志类似:

named[3246]: zone example.internal/IN (unsigned): loaded serial 1
named[3246]: zone example.internal/IN (signed): loaded serial 1
named[3246]: zone example.internal/IN (signed): receive_secure_serial: unchanged
named[3246]: zone example.internal/IN (signed): sending notifies (serial 1)
named[3246]: zone example.internal/IN (signed): reconfiguring zone keys
named[3246]: keymgr: DNSKEY example.internal/ECDSAP256SHA256/44911 (CSK) created for policy default
named[3246]: Fetching example.internal/ECDSAP256SHA256/44911 (CSK) from key repository.
named[3246]: DNSKEY example.internal/ECDSAP256SHA256/44911 (CSK) is now published
named[3246]: DNSKEY example.internal/ECDSAP256SHA256/44911 (CSK) is now active
named[3246]: zone example.internal/IN (signed): next key event: 23-Oct-2024 22:47:12.544
named[3246]: any newly configured zones are now loaded
named[3246]: running
named[3246]: resolver priming query complete: success
named[3246]: managed-keys-zone: Key 20326 for zone . is now trusted (acceptance timer complete)
named[3246]: zone example.internal/IN (signed): sending notifies (serial 3)

区域越大,为所有记录签名所需的时间可能越长。

上述日志包含几项值得注意的事件:

  • 为 example.internal 生成密钥。
  • 该区域已签名。
  • 安排了下一次密钥事件,说明 BIND9 同时负责已签名区域及密钥的维护。
  • 因为区域发生变化,序列号递增:从 1 变为 3。

DNSSEC 密钥保存在 /var/cache/bind:

-rw-r--r-- 1 bind bind  413 Oct 23 20:50 Kexample.internal.+013+48112.key
-rw------- 1 bind bind  215 Oct 23 20:50 Kexample.internal.+013+48112.private
-rw-r--r-- 1 bind bind  647 Oct 23 20:50 Kexample.internal.+013+48112.state

主要工作已经完成。该区域现已签名,BIND9 会按 default DNSSEC 策略自动维护它。

验证

刚签名的区域已经基本具备提供 DNSSEC 的条件,接下来进行验证。

当前 example.internal 与父区域尚未建立关联。虽然域名是指南虚构的,但即便它是真实域名,也仍然缺少到父区域的连接。DNSSEC 依靠信任链,父区域必须能够为子区域背书。

建立这条连接之前,先验证其他部分是否正常,尤其是执行 DNSSEC 查询并验证结果。delv 很适合这项工作:它类似 dig,但会使用与 BIND9 服务器相同的内部解析器和验证器逻辑验证查询结果。

由于区域尚未连接到父区域,需要告诉 delv 将该区域生成的公钥作为信任锚,并且不要尝试访问互联网根服务器。

首先把区域公钥复制到其他位置,以便编辑:

cp /var/cache/bind/Kexample.internal.+013+48112.key /tmp/example.key

文件顶部有注释,随后是一行以区域名开头的记录。下面为简洁起见截断了密钥:

example.internal. 3600 IN DNSKEY 257 3 13 jkmS5hfyY3nSww....

需要作以下修改:

  1. 删除顶部注释行。
  2. 把 3600 IN DNSKEY 替换为 static-key。
  3. 将数字 13 后的密钥内容放入双引号中。
  4. 以分号结束这一行。
  5. 将这一行包在 trust-anchors 块中。

最终 /tmp/example.key 应类似:

trust-anchors {
    example.internal. static-key 257 3 13 "jkmS5hfyY3nSww....";
};

现在可以使用 delv 查询该区域并执行 DNSSEC 验证:

delv @127.0.0.1 -a /tmp/example.key +root=example.internal noble.example.internal +multiline
; fully validated
noble.example.internal. 86400 IN A 192.168.1.11
noble.example.internal. 86400 IN RRSIG A 13 3 86400 (
                                20241106131533 20241023195023 48112 example.internal.
                                5fL4apIwCD9kt4XbzzlLxMXY3mj8Li1WZu3qzlcBpERp
                                lXPgLODbRrWyp7L81xEFnfhecKtEYv+6Y0Xa5iVRug== )

输出体现了两项重要信息:

  • ; fully validated:DNSSEC 验证已成功完成,返回的数据经过认证。
  • RRSIG:随结果中的 A 记录一同返回的签名资源记录。

连接信任链:父区域

要完成信任链,父区域必须能够为刚签名的子区域背书。具体操作因父区域管理员而异,可能只需将 DS 或 DNSKEY 记录粘贴到表单中。

通常需要 DS 或 DNSKEY 记录中的一种。下面说明如何生成并提交这些记录。

DS 记录格式

可以使用 dnssec-dsfromkey 从区域公钥生成 DS 记录:

dnssec-dsfromkey /var/cache/bind/Kexample.internal.+013+48112.key
example.internal. IN DS 48112 13 2 1212DE7DA534556F1E11898F2C7A66736D5107962A19A0BFE1C2A67D6841962A

DNSKEY 记录格式

DNSKEY 不需要额外工具。它就在区域公钥文件中,位于以分号开头的注释行之后:

cat /var/cache/bind/Kexample.internal.+013+48112.key
; This is a key-signing key, keyid 48112, for example.internal.
; Created: 20241023205023 (Wed Oct 23 20:50:23 2024)
; Publish: 20241023205023 (Wed Oct 23 20:50:23 2024)
; Activate: 20241023205023 (Wed Oct 23 20:50:23 2024)
; SyncPublish: 20241024215523 (Thu Oct 24 21:55:23 2024)
example.internal. 3600 IN DNSKEY 257 3 13 jkmS5hfyY3nSww4tD9Fy5d+GGc3A/zR1CFUxmN8T2TKTkgGWp8dusllM 7TrIZTEg6wZxmMs754/ftoTA6jmM1g==

取出这一行,略作格式调整,实际提交给父区域的记录应类似:

example.internal. 3600 IN DNSKEY 257 3 13 (jkmS5hfyY3nSww4tD9Fy5d+GGc3A/zR1CFUxmN8T2TKTkgGWp8dusllM 7TrIZTEg6wZxmMs754/ftoTA6jmM1g==);

延伸阅读

  • BIND9 的 DNSSEC 指南。
  • 权威区域签名快速入门指南。
  • 创建自定义 DNSSEC 策略。
  • BIND9 文档的详细 DNSSEC 章节。
  • delv(1) 手册。
  • 与父区域协作。
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容