Knot DNS:一款温顺且理智的权威 DNS 服务器

作者:Thomas Golden

如何安装并进行最简配置 Knot,使其充当您家庭实验室的本地域名主服务器和从服务器。

如果您是最初《周六夜现场》时代的 регулярный зритель,您会记得 Festrunks,这是一对粗俗但天真的捷克兄弟,他们自称是“狂野而疯狂的家伙!” 对于我来说,Gyorg 和 Yortuk(加上我在当地大学高中数学竞赛中被一位杰出的捷克教授设计的测试击败)是我对捷克共和国的全部了解。

我最近发现了另一件捷克的东西,它一点也不狂野和疯狂,而是非常温顺和理智,开源且易于配置。 Knot DNS 是一款权威 DNS 服务器,由捷克 CZ.NIC 组织于 2011 年编写。他们编写并持续维护它,以服务于他们的国家顶级域名 (TLD),并防止在全球所有 TLD 中进一步扩展全球 BIND9 软件单一文化。 Knot 除了其权威服务器外,还提供了一个单独的快速缓存服务器和解析器库。

权威名称服务器和缓存/递归名称服务器功能的分离是有充分理由的。名称服务器的查询结果缓存可能会被转发到恶意外部服务器的查询“污染”,因此如果您不允许权威名称服务器回答其他域的查询,则它不会被污染,并且其对自身域的回答是可信的。

软件单一文化意味着到处运行相同的软件(如 BIND9),而不是不同的软件提供相同的功能和互操作性。这很糟糕,原因与我们最终将失去当前流行的香蕉品种的原因相同——由于基因相同,各地的香蕉都可能被单一的传染源消灭。与水果一样,关键基础设施中的一些基因多样性是一件好事。

在本文中,我将描述如何安装和最简配置 Knot,使其充当您家庭实验室的本地域名主服务器和从服务器。我将使用事务签名 (TSIG) 来保护区域传输。虽然 Knot 支持 DNSSEC,但我在此不讨论它,因为我喜欢您,并且希望您在我们都老死之前读完这篇文章。我假设您已经知道 DNS 区域文件是什么以及它是什么样子。

您可以从 https://www.knot-dns.cz/download 下载最新版本(我写作时为 2.8.x)的源代码 tarball,并使用通用的 configure; make; make install 命令自行构建。如果您打算让您的解析器面向公共互联网,我建议运行最新版本的 Knot。我发现,要在 Ubuntu 18.04 上构建 Knot,除了标准的必要构建工具包外,还需要安装 pkg-configlibgnutls28liburcu-devlibedit-dev 软件包。 Knot 的网站有进一步的要求,CentOS 7 和 Ubuntu 18.04 似乎都满足这些要求。 tar 解压和构建过程还奇怪地要求创建一些硬文件链接。尽管出现硬链接失败错误,但以非特权用户身份构建仍然成功。

对于我的家庭实验室,Ubuntu “universe” 和 EPEL 仓库中的 2.6.x 软件包就足够了。要在 Ubuntu 18.04 上安装它,我在我的 /etc/apt/sources.list 中启用了 “bionic universe” 仓库,并执行了以下操作


$ sudo apt update
$ sudo apt install knot

在 CentOS 7 上,我将运行以下命令


$ sudo yum install epel-release
$ sudo yum install knot

我现在可以继续配置 Knot 主实例了。我讨厌阅读 BIND9 配置文件,虽然我知道主要的发行版都在努力提供帮助,但我特别讨厌它们提供的切片和切块版本。相比之下,Knot.conf 简直是小菜一碟。 Knot 极其简洁的单文件配置使用 YAML 格式。 Knot 软件包在 /etc/knot.conf 中部署了一个示例配置。我将该文件移到一边,并使用我喜欢的编辑器 vi 创建了一个新的文件


$ sudo mv /etc/knot.conf /etc/knot.conf-save
$ sudo vi /etc/knot.conf

我的家庭网络是 cpu-chow.local;您可以根据需要命名您的网络。由于除了您之外没有人会认为此服务器是权威的,因此您也可以选择您的 TLD。通过配置缓存名称服务器将针对您域名的查询显式转发到您的权威解析器,您可以将此域名提供给您家庭实验室中的缓存名称服务器。

第一部分定义服务器在主机和网络上的存在。节名称从第 1 列开始,条目缩进四个空格


server:
    identity: dns1.cpu-chow.local
    listen: [ 127.0.0.9@53, 172.28.1.22@53 ]
    user: knot:knot
    rundir: /run/knot

在 server 部分,我告诉 Knot 它的主机名,并在本地接口的端口 53 上运行,但地址不同,为 127.0.0.9。由于 Knot 只能解析对 cpu-chow.local 的查询,因此从默认的 127.0.0.1 提供服务几乎没有用处;此外,我的主机的缓存名称服务器已经在那里了。我还指示它监听主机实验室网络地址 172.28.1.22。我的实验室网络是 172.28.1.0/24,我想为实验室中的其他主机提供 cpu-chow.local 的权威名称服务。或者,我可以仅将权威服务器提供给我的主机的缓存服务器,并让该缓存服务器监听实验室网络 IP 并满足所有其他主机的查询需求(如果需要)。

安装 Knot 会创建一个用户 ID knot,我指示 Knot 以该 UID/GID 身份运行,以实现最小权限最佳实践。 rundir 将是 Knot 管理套接字的家。如果您运行多个 Knot 实例,请为每个实例使用唯一的 /run 目录。

rundir 之后,我插入一个空行以表示该节的结束,并开始 key 节以定义我的 TSIG 密钥。请注意,id 在第 3 列中有一个破折号。这是 YAML,这意味着接下来要描述的内容专门与密钥 tsigkey1. 相关。


key:
  - id: tsigkey1.
    Algorithm: hmac-sha256
    secret: base64-keyvalue

要定义另一个密钥,请在第一个 secret 行之后立即添加另一个 id 行。

这是授权的从服务器在请求管理区域传输 (AXFR)(即与主服务器同步的请求)时将提供的密钥。我为我的任意命名的密钥提供了一个完全小写的名称,后跟一个句点,以确保与 PowerDNS 等其他 DNS 服务器软件的互操作性,当我将其作为 Knot 主服务器的从服务器运行时,PowerDNS 似乎总是将我给它的任何密钥名称都转换为小写并附加一个句点。

密钥是一个随机生成的 Base64 值。 Knot 包含 keygen 实用程序,可为您生成密钥部分,您可以将输出粘贴到您的配置文件中


$ keymgr -t tsigkey1.
key:
- id: tsigkey1.
    algorithm: hmac-sha256
    secret: oxRKAUfGN3R6fGjWX/V+i4rjCl1zRuYslX0c4se+GWs=

再一个空行,我继续到 template 部分,该部分定义了可以应用于其他部分的通用预设。您可以像添加多个密钥一样添加多个模板。 template 部分和模板 id default 必须存在于 knot.conf 中,否则 Knot 在您启动它时会发出一条神秘的错误消息


template:
  - id: default
    storage: "/var/lib/knot"

default 是一个特殊的模板,它会自动应用;其他模板必须在节中引用才能在那里应用。我在此声明,默认情况下,所有要读取的文件都在 /var/lib/knot 中。我打算使用 nsupdate 来管理我的区域,因此将它们放在 /etc/knot 中是不合适的(nsupdate 超出了本文的范围。)

现在我使用 remote 部分定义我的授权从服务器。此会话将主机和各种属性与您稍后可以在配置中使用的名称关联起来


remote:
  - id: slave1
    address: 127.0.0.19@53
    key: tsigkey1.
  - id: slave2
    address: 172.28.1.20@53
    key: tsigkey1.

id 是一个任意名称。除了实验室主机 172.28.1.2 上的远程从服务器外,我将在主主机 172.0.0.19 上运行一个从服务器,以阐明一个神秘的 Linux 网络问题。

acl 部分定义服务器的访问控制列表 (ACL)——Knot 将在谁向其发送管理请求时监听谁


acl:
  - id: acl_axfr
   address: [ 127.0.0.0/24, 172.28.1.20 ]
    action: transfer
    key: tsigkey1.
  - id: acl_upme
    address: 127.0.0.0/24
    action: update
    key: tsigkey1.

我配置了两个 ACL,一个用于接收来自从服务器的 AXFR 请求,另一个用于方便源自本地主机的 nsupdate 请求。每个 ACL 都将使用相同的密钥,但它们不必这样做。

那么,为什么我在 acl_xfer 上使用了 /24 掩码?不应该是 127.0.0.19 吗?没错,应该是,但 Linux 网络有一个小怪癖。有时,当我的本地主机从服务器发送请求时,数据似乎来自与 lo 接口关联的主 IP:127.0.0.1。在网络搜索揭示这个怪异之处之前,我难以置信地盯着我的日志,并仔细研究了一段时间的 tcpdump 输出,当时主服务器一直拒绝来自 127.0.0.1 的神秘 AXFR 请求。几个 DNS 服务器软件包都有一个配置选项来指定从服务器传输的源地址。我尚未在 Knot 中找到这样的选项。我在这里引起这个问题是因为我厚颜无耻地使用了 127.0.0.* 地址;如果我使用实验室或公共网络地址,就不会发生这种情况。因此,我很乐意在我的家庭实验室中为 localhost 指定一个范围。

在上述事件中,我是如何让 Knot 告诉我它为什么不工作的?我提高了日志记录级别。 log 部分控制记录什么以及记录到哪里


log:
  - target: syslog
    any: info

此配置发出所有 info 级别及更低级别的日志条目,并将它们发送到 syslog。为了获得调试级别的条目并将其转储到您的屏幕上(在前台运行 Knot),我使用了以下配置


log:
  - target: stdout
    any: debug

target 也可以是 stderr 或文件名。

我现在准备使用 zone 部分声明我的权威区域。对于本文,我只设置一个区域


zone:
  - domain: cpu-chow.local
    file: "cpu-chow-local.zone"
    notify: slave1
    acl: acl_axfr
    acl: acl_upme
    semantic-checks: on
    disable-any: on
    serial-policy: increment

请注意,域名末尾不需要句点。 file 属性不允许路径;默认模板提供 storage 属性 /var/lib/knot,即 cpu-chow.local.zone 的位置。

notify 指定我的主服务器将向哪个 remote id 发送 “notify” 消息,告知他们应立即响应 AXFR 请求。第一个 acl 指定主服务器将监听哪些 IP 地址以接收 AXFR 请求,第二个 acl 指定主服务器将监听哪些 IP 地址以接收 nsupdate 请求。我可以在此处通过在此区域下添加 template: template-id 行来映射非默认模板。

我在区域中包含了一些其他属性,以说明最佳实践: semantic-checks 对区域文件执行额外的语法检查。 disable-any 更改 Knot 对 -t ANY 查询的响应,以防止 DNS 区域反射攻击。 serial-policy 确保动态更新将触发区域文件中的序列号递增。

我保存了文件,将其所有者设置为 root:knot,权限设置为 640。我现在可以启动 Knot,可以通过 systemd


$ sudo systemctl restart knot
$ sudo systemctl status knot

或者通过在前台以非常详细的模式运行它


$ sudo /usr/sbin/knotd -vvv -c /etc/knot/knot.conf

请注意,systemd 可能不会立即报告 Knot 启动失败,因此跟进状态检查是一个好习惯。

在我的本地缓存服务器 127.0.0.1 上,我更新配置以告知它将我的主服务器 127.0.0.9 视为我的区域的权威服务器。由于我正在运行 PowOerDNS Recursor,我编辑文件 /etc/powerdns/recursor.conf


forward-zones-recurse=cpu-chow.local.=127.0.0.9

我在实验室的其他地方也会这样做,而是使用实验室网络地址 172.28.1.22。

让我们看一下 127.0.0.19 上从服务器的配置。我将其称为 /etc/knot/slave.conf,所有者和权限与主服务器配置相同。由于它是第二个 Knot 实例,我需要创建第二个 rundir 和存储位置。由于它是从 Knot,我们将其称为 snot


$ sudo mkdir -p /run/snot /var/lib/snot
$ sudo chown knot:knot /run/snot /var/lib/snot
$ sudo chmod 755 /run/snot /var/lib/snot

这是 /etc/knot/slave.conf


server:
    identity: dns2.cpu-chow.local
    listen: 127.0.0.19@53
    user: knot:knot
    rundir: /run/snot

key:
  - id: tsigkey1.
    algorithm: hmac-sha256
    secret: oxRKAUfGN3R6fGjWX/V+i4rjCl1zRuYslX0c4se+GWs=

template:
  - id: default
    storage: /var/lib/snot

remote:
  - id: master1
    address: 127.0.0.9@53
    key: tsigkey1.

acl:
  - id: acl_axfrnfy
    address: 127.0.0.0/24
    action: notify
    key: tsigkey1.

log:
  - target: stdout
    any: debug

zone:
  - domain: cpu-chow.local
    master: master1
    acl: acl_axfrnfy

它看起来与主服务器非常相似。请注意, acl 允许从服务器接受来自主服务器的 notify 消息。我将日志记录设置为 debug-level 和标准输出,因为我打算在前台运行它。

您可以创建一个 systemd 单元文件来启动从服务器(如果需要)(超出本文范围)。我在这里手动运行它


$ sudo /usr/sbin/knotd -vvv -c /etc/knot/slave.conf

我可以使用控制程序 /usr/sbin/knotc 控制正在运行的 knotd 实例,如下所示


$ sudo /usr/sbin/knotc -c config-file [ command ]

指定配置文件会告诉 knotc 哪个 rundir 包含要连接的套接字。在主服务器上使用 knotc 命令 zone-notify 可以使其通知从服务器请求 AXFR,或使用 zone-flush 来刷新缓存。还有许多其他命令可用,请使用 --help 查看它们。

我只是稍微了解了 Knot 的功能;它实际上是为轻松处理完整的 TLD 而设计的,因此它可以处理您的负载。考虑将其与您现有的 DNS 软件一起使用,以避免组织中出现 DNS 软件单一文化。

现在您已经了解了 Knot DNS 并了解了主服务器和从服务器配置示例,您可以自行安装、配置和评估它。您可能会非常喜欢它,以至于它会让您的胸毛都变得酥脆,就像 Festrunk 兄弟一样。

Thomas Golden (tomg16.lj@cpu-chow.com) 担任 Linux 系统管理员超过 20 年,并且在此之前曾担任 OpenVMS 系统程序员和管理员。

加载 Disqus 评论