把名字解析的路走到底
本实验在 VM 中运行
Ubuntu 24.04 由 systemd-resolved 负责名称解析。我们还会使用 dnsmasq
搭建一台自己的 DNS 服务器,在两层之间来回观察名称从哪里获得应答。
dnsmasq 已经安装,但服务处于失败状态——找出原因
就是第 3 步。
目标
通过命令确认 /etc/resolv.conf 中的存根地址是什么、真正的上游服务器在哪里、如何让自己的
DNS 服务器只处理特定域名,以及 /etc/hosts 为什么会
优先于 DNS。
为什么重要
“无法运行”时遇到的第一个岔路口就是名称。但在现代 Linux 中,名称解析
并非只有一层——应用程序调用 libc 的 getaddrinfo,它会
按照 nsswitch.conf 的顺序先查看 /etc/hosts,然后询问 resolv.conf 中的
存根(127.0.0.53),存根再查看各链路的设置并转发到上游。dig 则
只会直接访问其中的最后一个阶段。因此,实际中确实会发生 dig 成功但 curl 失败的
情况,只有知道哪一层存在差异,才能修复问题。
步骤
- 将
/etc/resolv.conf指向的实际文件路径和nameserver行保存到/root/dns/resolv.txt。 - 将存根实际询问的上游 DNS 服务器地址保存到
/root/dns/upstream.txt,内容为upstream=<주소>一行。 - 找出 dnsmasq 无法启动的原因,并将显示占用端口 53 的进程的
ss输出保存到/root/dns/port53.txt。 - 为虚拟接口
dns0添加10.53.0.1/24,并通过/etc/dnsmasq.d/lab.conf让 dnsmasq 只监听10.53.0.1,为app.lab.internal→10.53.0.10、db.lab.internal→10.53.0.20提供应答,然后恢复服务。 - 将
dig @10.53.0.1 app.lab.internal的完整输出保存到/root/dns/dig.txt。 - 使用
resolvectl为dns0链路指定 DNS 服务器10.53.0.1和路由域~lab.internal,使getent hosts app.lab.internal能够成功,并将resolvectl status dns0保存到/root/dns/link.txt。 - 在
/etc/hosts中加入10.53.0.99 db.lab.internal,并将getent和dig @10.53.0.1的应答保存到/root/dns/hosts.txt,内容为getent=、dig=两行。 - 在
/root/dns/report.md中写入stub=、upstream=、local=三行、dnsmasq 查询日志中包含app.lab.internal的一行,以及/etc/hosts优先于 DNS 的原因。
参考
readlink -f /etc/resolv.conf、resolvectl status、resolvectl dns、resolvectl domain。- 占用端口的进程:
ss -ulpn 'sport = :53'。journalctl -u dnsmasq中有失败原因。 - dnsmasq 配置关键项:
listen-address=、bind-interfaces、no-resolv、log-queries、address=/이름/주소。修复后执行systemctl restart dnsmasq。 - 常见错误 1:遗漏
bind-interfaces。这样一来,dnsmasq 会试图占用所有地址上的端口 53,并再次与存根发生冲突。 - 常见错误 2:在第 6 步中遗漏域名前的
~。没有~时,它会成为搜索域,只会附加在短名称后面,而不会将查询发送到该服务器。
resolv.conf 的真实身份
将 /etc/resolv.conf 指向的实际文件路径和 nameserver 行保存到 /root/dns/resolv.txt。
readlink -f /etc/resolv.conf 会给出符号链接末端的真实路径。使用 grep nameserver /etc/resolv.conf 查看存根地址。将两者写入同一个文件。
真正的服务器在哪里
将存根实际询问的上游 DNS 服务器地址保存到 /root/dns/upstream.txt,内容为 upstream=<주소> 一行。
resolvectl status 的链路条目中有 Current DNS Server。它是默认路由接口(enp1s0)的值。resolvectl dns enp1s0 更简短。
dnsmasq 为什么无法启动
找出 dnsmasq 无法启动的原因,并将显示占用端口 53 的进程的 ss 输出保存到 /root/dns/port53.txt。
systemctl status dnsmasq 和 journalctl -u dnsmasq 中有 Address already in use。要确认由谁占用,请使用 ss -ulpn 'sport = :53'——-p 会显示进程名称(必须是 root)。
搭建自己的 DNS 服务器
为虚拟接口 dns0 添加 10.53.0.1/24,并通过 /etc/dnsmasq.d/lab.conf 让 dnsmasq 只监听 10.53.0.1,为 app.lab.internal→10.53.0.10、db.lab.internal→10.53.0.20 提供应答,然后恢复服务。
执行 ip link add dns0 type dummy 后配置地址并设为 UP。配置中使用 listen-address=10.53.0.1 和 bind-interfaces,使其只占用该地址,并像 address=/app.lab.internal/10.53.0.10 这样固定名称。no-resolv 表示不使用上游。完成后执行 systemctl restart dnsmasq,再执行 systemctl is-active dnsmasq。
使用 dig 直接查询
将 dig @10.53.0.1 app.lab.internal 的完整输出保存到 /root/dns/dig.txt。
@서버 会忽略 resolv.conf,直接询问该服务器。输出的 ANSWER SECTION 中应显示 A 记录,开头应显示 status: NOERROR。
将自己的服务器接入存根
使用 resolvectl 为 dns0 链路指定 DNS 服务器 10.53.0.1 和路由域 ~lab.internal,使 getent hosts app.lab.internal 能够成功,并将 resolvectl status dns0 保存到 /root/dns/link.txt。
执行 resolvectl dns dns0 10.53.0.1 和 resolvectl domain dns0 '~lab.internal'。带 ~ 的域是一条路由规则,意思是“将对此域的查询发送到此链路的服务器”。使用 getent hosts app.lab.internal 检查——应输出 10.53.0.10。
/etc/hosts 优先
在 /etc/hosts 中加入 10.53.0.99 db.lab.internal,并将 getent 和 dig @10.53.0.1 的应答保存到 /root/dns/hosts.txt,内容为 getent=、dig= 两行。
执行 echo '10.53.0.99 db.lab.internal' >> /etc/hosts。然后执行 getent hosts db.lab.internal | awk '{print $1}' 和 dig @10.53.0.1 db.lab.internal +short。两个值不同——这正是本步骤的要点。
学到了什么
在 /root/dns/report.md 中写入 stub=、upstream=、local= 三行、dnsmasq 查询日志中包含 app.lab.internal 的一行,以及 /etc/hosts 优先于 DNS 的原因。
stub 是第 1 步中的 nameserver,upstream 是第 2 步中的值,local 是自己的 dnsmasq 地址。日志使用 journalctl -u dnsmasq --no-pager | grep 'query\[A\] app.lab.internal'。原因中必须包含 nsswitch 或 files 一词。