在隔离网络中搭建内部 DNS 与时间服务器
本实验在真正的 VM 中运行
这是一台 Ubuntu 24.04 VM。准备步骤已经创建了网络命名空间 client——它虽然在同一台 VM 内,但在网络上是独立的,相当于“隔离网络中的另一台服务器”,并通过 veth 与 VM(10.203.0.1)相连(10.203.0.2)。要在其中输入命令,使用 ip netns exec client <명령>(占位符为要执行的命令)。已安装 dnsmasq、chrony 和 dig(dnsmasq 服务因 53 端口冲突而处于失败状态——该问题的诊断在 network-basics 课程中讲解)。评分由从外部连入 VM 的 8899 端口的代理完成,所以如果封闭了已建立的连接,评分就会中断。
目标
在无法访问互联网的网络中搭建内部 DNS 和时间服务器,让其他服务器使用它们,最后用交接用的检查脚本收尾。
为什么重要
内部镜像要用名称访问,才能与证书匹配,而且必须有解析该名称的 DNS。但如果隔离网络的 DNS 对不认识的名称还要去外部询问,每次都要等待超时。内部区域必须以权威身份立即应答。时间也一样。连不上互联网时间服务器的服务器,时钟会相互越走越远,哪怕只差几分钟,证书有效期确认和日志对照就会出问题。隔离网络中必须有一个基准时钟。
预计 50 分钟。会话结束后 VM 会消失,想保留的文件请在结束前另行保存。
步骤
- 用重复运行也只会留下一套规则的
/root/infra/egress.sh封闭出站(链AIRGAP-EGRESS,在 OUTPUT 中引用一次)。放行回环、已建立的连接和内部网段10.203.0.0/24。在封闭之前和之后分别测量curl -s -o /dev/null -w '%{http_code}' https://ubuntu.com,以before=和after=写入/root/infra/egress-proof.txt。 - 通过
/etc/dnsmasq.d/airgap.conf让 dnsmasq 只在10.203.0.1上监听,在没有上游的情况下,以权威身份应答airgap.internal区域。主机名对照表放在/etc/airgap/hosts(registry、pypi、ntp三个名称 →10.203.0.1)中,并通过addn-hosts读取。启用服务,使其开机时也会启动。 - 写入
/etc/netns/client/resolv.conf,让client使用该 DNS 和搜索域airgap.internal。在client中,短名称registry必须能被解析。 - 在
/etc/airgap/hosts中添加nexus.airgap.internal→10.203.0.1,并在不重启的情况下让 dnsmasq 生效。把生效前后 dnsmasq 主进程的 PID,以pid_before=和pid_after=写入/root/infra/reload.txt。 - 通过
/etc/chrony/conf.d/airgap-server.conf让 chrony 向内部网段提供时间,并在没有上游时也以层级 8 作为基准。 - 在
client中,在不改变时钟的情况下向10.203.0.1询问时间,并把输出保存到/root/infra/ntp-query.txt。 - 编写交接用的检查脚本
/root/infra/check.sh <네임스페이스>(占位符为命名空间)。如果在该命名空间中registry、pypi、ntp都能解析,且时间服务器有应答,则以 0 结束,否则以非 0 值结束。
参考
- 区域权威:
local=/airgap.internal/、切断上游:no-resolv、主机名对照表文件:addn-hosts=<파일>(占位符为文件) - 确认:
dig @10.203.0.1 registry.airgap.internal +short,不存在的名称的状态:dig @10.203.0.1 nope.airgap.internal | grep status - 向 dnsmasq 发送 SIGHUP:
systemctl reload dnsmasq或kill -HUP <PID>、主 PID:systemctl show -p MainPID --value dnsmasq - chrony 状态:
chronyc -n tracking,客户端列表:chronyc -n clients、不改变时钟的查询:chronyd -Q '<server 줄>'(占位符为 server 配置行) - 常见错误 1:没有
local=,只设置no-resolv。对不认识的内部名称,返回的不是“不存在”(NXDOMAIN),而是“不予回答”(REFUSED),客户端无法得出该名称不存在的结论,就会去找下一个服务器。 - 常见错误 2:修改
addn-hosts之后重启 dnsmasq。虽然可行,但缓存和查询日志会中断——对照表文件通过 SIGHUP 就会被重新读取。
封闭出站,变成隔离网络
用 /root/infra/egress.sh 封闭出站(链 AIRGAP-EGRESS),并把封闭前后 https://ubuntu.com 的状态码,以 before= 和 after= 写入 /root/infra/egress-proof.txt。
新建一条链,并在 OUTPUT 的最前面转到这条链,采用清空再重新填充的方式,无论运行几次,结果都是同样的形态。先用 RETURN 写明要放行的对象(回环、已建立的连接、评分代理、内部网段),最后再封闭。
没有上游的内部 DNS
通过 /etc/dnsmasq.d/airgap.conf 让 dnsmasq 只在 10.203.0.1 上监听,在没有上游的情况下以权威身份应答 airgap.internal,并把主机名对照表放在 /etc/airgap/hosts 中读取。启用服务,使其开机时也会启动。
需要两行把监听地址收窄为一个,一行切断上游,一行把区域声明为本地,一行读取对照表文件。请用 dig 的 status 确认,不存在的内部名称是否立即被应答为“不存在”。
让其他服务器使用内部 DNS
写入 /etc/netns/client/resolv.conf,让 client 使用 10.203.0.1 和搜索域 airgap.internal。
如果存在 /etc/netns/<名称>/resolv.conf,ip netns exec 会在该命名空间内把它放到 /etc/resolv.conf 的位置上。nameserver 和 search 两行就够了。确认时,在 client 内用短名称执行 dig +search。
不重启就添加名称
在 /etc/airgap/hosts 中添加 nexus.airgap.internal → 10.203.0.1,并在不重启 dnsmasq 的情况下让其生效。把生效前后的主进程 PID,以 pid_before= 和 pid_after= 写入 /root/infra/reload.txt。
dnsmasq 收到 SIGHUP 后,会清空缓存,并重新读取 /etc/hosts 和 addn-hosts 文件(配置文件本身不会重新读取)。也请确认 systemd 的 reload 发送的是什么。
没有上游的内部时间服务器
通过 /etc/chrony/conf.d/airgap-server.conf 让 chrony 向 10.203.0.0/24 提供时间,并在没有上游时也以层级 8 作为基准。
chrony 默认不接受任何客户端。需要一个允许接收网段的指令,以及一个在没有上游的情况下以自己的时钟为基准对外提供的指令。Ubuntu 的 chrony.conf 会读取 conf.d。修改之后重新启动服务,并查看 tracking 中的 Stratum。
从其他服务器询问时间
在 client 中,在不改变时钟的情况下向 10.203.0.1 询问时间,并把输出保存到 /root/infra/ntp-query.txt。
chronyd 有一种模式:不用配置文件,而是通过命令行指令询问一次,不改变时钟,只打印差值就结束。请在命名空间内运行,并设置一个限时,让它等待几秒。
交接用的检查脚本
编写 /root/infra/check.sh <네임스페이스>(占位符为命名空间)。如果在该命名空间中 registry、pypi、ntp 都能解析,且时间服务器有应答,则以 0 结束,否则以非 0 值结束。
评分器会对 client,以及两个没有连接内部网络的空命名空间运行这个脚本。前者必须为 0,后者必须不为 0。请给 dig 和 chronyd -Q 设置较短的限时。