TT Lab
开始
学习 学习路径 课程

隔离网络镜像与私有 CA

在隔离网络中搭建内部 DNS 与时间服务器

在 TT Lab 中继续学习

本实验在真正的 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 会消失,想保留的文件请在结束前另行保存。

步骤

  1. 用重复运行也只会留下一套规则的 /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。
  2. 通过 /etc/dnsmasq.d/airgap.conf 让 dnsmasq 只在 10.203.0.1 上监听,在没有上游的情况下,以权威身份应答 airgap.internal 区域。主机名对照表放在 /etc/airgap/hosts(registry、pypi、ntp 三个名称 → 10.203.0.1)中,并通过 addn-hosts 读取。启用服务,使其开机时也会启动。
  3. 写入 /etc/netns/client/resolv.conf,让 client 使用该 DNS 和搜索域 airgap.internal。在 client 中,短名称 registry 必须能被解析。
  4. 在 /etc/airgap/hosts 中添加 nexus.airgap.internal → 10.203.0.1,并在不重启的情况下让 dnsmasq 生效。把生效前后 dnsmasq 主进程的 PID,以 pid_before= 和 pid_after= 写入 /root/infra/reload.txt。
  5. 通过 /etc/chrony/conf.d/airgap-server.conf 让 chrony 向内部网段提供时间,并在没有上游时也以层级 8 作为基准。
  6. 在 client 中,在不改变时钟的情况下向 10.203.0.1 询问时间,并把输出保存到 /root/infra/ntp-query.txt。
  7. 编写交接用的检查脚本 /root/infra/check.sh <네임스페이스>(占位符为命名空间)。如果在该命名空间中 registry、pypi、ntp 都能解析,且时间服务器有应答,则以 0 结束,否则以非 0 值结束。

参考

封闭出站,变成隔离网络

用 /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 设置较短的限时。