隔离网络中的名字与时间
一句话总结
隔离网络中也必须有名称和时间。内部 DNS 必须在没有上游的情况下,以权威身份应答内部区域,这样遇到不认识的名称时才不必等待超时;内部时间服务器必须声明即使没有上游也作为基准,各服务器的时钟才不会相互离散。
为什么需要它
要用 pypi.airgap.internal 访问内部镜像,就必须有能解析该名称的 DNS。逐台服务器修改 /etc/hosts,服务器一超过十台就会出错。但是,如果像平常那样搭建内部 DNS,它会把不认识的名称转发给上游,而隔离网络的上游不会应答,客户端每次查询都要等待超时。时间会以更隐蔽的方式出问题。连不上互联网时间服务器的服务器,会按自己的时钟走下去,一个月内,每台服务器之间会相差几十秒到几分钟。这样一来,证书就会因“尚未生效”而被拒绝,多台服务器的日志也无法按时刻对照了。
工作原理
用 dnsmasq 提供内部区域。 根据 dnsmasq 手册,listen-address 和 bind-interfaces 只在指定的地址上打开套接字(Ubuntu 的 systemd-resolved 存根使用 127.0.0.53 和 127.0.0.54 的 53 端口,所以没有这两项就会冲突)。no-resolv 让它不从 resolv.conf 读取上游,local=/airgap.internal/ 让该区域的查询不转发到外部,只通过 /etc/hosts 和 DHCP 应答(--local 是 --server 的另一个名称)。domain= 确定要附加到短名称上的区域,addn-hosts= 则确定除 /etc/hosts 之外还要读取的主机名对照文件。address=/도메인/주소(占位符依次为域名和地址)会把子名称也用同一个地址应答,除非要把整个内部区域绑到一个地址上,否则用主机名对照文件更容易管理。
无需重启即可生效。 dnsmasq 收到 SIGHUP 后,会清空缓存,并重新读取 /etc/hosts 和 addn-hosts 文件。配置文件本身不会重新读取。所以把经常变动的主机名对照表与配置分开,运维起来更方便。
客户端一侧。 如果有 /etc/netns/<이름>/resolv.conf(占位符为命名空间名称),ip netns exec 会在该命名空间内把它放到 /etc/resolv.conf 的位置上。对于真实的服务器,相当于在 /etc/resolv.conf(或 systemd-resolved 的 DNS 设置)中写入内部 DNS 和 search 域。
用 chrony 提供内部时间。 chrony 文档写明,默认情况下它不接受任何客户端,只作为纯粹的 NTP 客户端工作。必须有 allow <대역>(占位符为网段)才会提供时间。local stratum N(1–15,默认 10)会让它在没有上游同步的情况下,也以自己的时钟作为该层级的基准对外提供。要让多台服务器相互以对方为基准,就使用 orphan 选项。在客户端上,想不改动时钟、只做确认时,chronyd -Q 只打印差值就结束(-q 则是校准一次后结束),在服务器上则用 chronyc tracking 和 chronyc clients 查看状态和收到的查询。
单一基准时钟的局限。 local stratum 只是“这个时钟是对的”的一种声明,并不能让它变得正确。整个隔离网络都遵循同一个时钟,所以彼此不会错开,但如果这个时钟不准,就会一起不准。现场要么设置 GPS 或标准时间接收器这样的硬件基准,要么制定在每次导入流程时用人工确认的时刻来校准的流程。
在现场相遇的样子
我们在这个实验 VM 中实测过。只设置 no-resolv 而去掉 local=,对照表中没有的 nope.airgap.internal,以及外部名称 ubuntu.com,都是 status: REFUSED。因为没有可转发的上游,意思是“这里不回答”。加入 local=/airgap.internal/ 之后,同样的内部名称变成了 NXDOMAIN(权威的“不存在”)。收到 REFUSED 的客户端会去找下一个名称服务器,而收到 NXDOMAIN 的客户端会就此停止——这就是至少内部区域必须给出权威答复的原因。chrony 加入 local stratum 8 并重新启动后,chronyc tracking 显示 Reference ID : 7F7F0101 () 和 Stratum : 8,而 systemctl reload dnsmasq 正如单元中所写,是 kill -HUP $MAINPID。
这个实验的 VM 从一开始就连不上外部的 NTP(UDP 123)——只开放了公网 80/443 和 DNS。所以安装之后,chrony 是在没有同步任何一台上游服务器的状态下运行的。这正是隔离网络服务器上常见的样子。如果再封闭出站,名称解析也会中断。
下一项实验要做什么
封闭 VM 的出站,做成真正的隔离网络,并把 dnsmasq 搭建成没有上游的 airgap.internal 权威服务器。让 client 命名空间使用该 DNS 和搜索域,并在不重启的情况下添加名称。把 chrony 改为内部时间服务器,从 client 发起查询,最后用交接用的检查脚本收尾。
参考文档:dnsmasq(8)、resolved.conf(5)、ip-netns(8)、chrony.conf(5)、chronyd(8)、chronyc(1)