Ubuntu 上 mDNS 解析异常排查实录:从 Docker 劫持到 firewalld 放行
背景
两台 Ubuntu 主机:
| 主机名 | 局域网 IP | 网卡 | 防火墙 |
|---|---|---|---|
ubuphy001 | 192.168.1.104 | enp1s0 | firewalld |
ubuvm001 | 192.168.1.103 | ens33 | UFW |
ubuphy001 上运行了 Docker,创建了多个网桥接口。目标:让 ping ubuphy001.local 解析到本机真实的局域网 IP。
问题现象
最初在 ubuphy001 上执行:
ping ubuphy001.local
PING ubuphy001.local (172.21.0.1) 56(84) bytes of data.
返回的竟然是 Docker 网桥地址,而不是 192.168.1.104。
而 ping ubuphy001(不带 .local)返回 127.0.1.1,这是 /etc/hosts 里的回环别名,与 mDNS 无关,属于正常现象。
第一层问题:Avahi 把 Docker 网桥 IP 也广播了出去
原因
avahi-daemon 默认会在所有处于 UP 状态的网络接口上注册本机地址。Docker 创建的 docker0、br-xxxx 网桥也是接口,它们上面的 IP(如 172.20.0.1、172.21.0.1)就被 Avahi 一并当作”本机可用地址”发布出去了。
当有设备查询 ubuphy001.local 时,Avahi 返回了包含 Docker IP 的地址列表,系统优先选中了 Docker 网桥地址。
解决:限制 Avahi 的广播接口
编辑 /etc/avahi/avahi-daemon.conf,在 [server] 段落中指定只允许物理网卡:
[server]
allow-interfaces=enp1s0
这里有一个关键认知:deny-interfaces 不支持 br-* 这类通配符。Avahi 官方只接受逗号分隔的接口名列表,社区里流传的 br-* 写法属于”碰巧能用”的副作用,并不可靠。更稳妥的做法是用 allow-interfaces 白名单,只放行物理网卡,Docker 接口自动被排除。
改完后重启 Avahi:
sudo systemctl restart avahi-daemon.service
改完 allow-interfaces 后,ping 自己反而失败了
重启 Avahi 后出现了一个新现象:
ping ubuphy001.local
ping: ubuphy001.local: Name or service not known
这跟防火墙没有关系。
ping xxx.local 的解析路径是:
ping → NSS → nss-mdns → 本机 Avahi 守护进程(D-Bus / 本地 socket)→ 返回结果
这是一条纯本机进程间通信的路径,不经过网络层,也不经过防火墙。
真正的原因是:allow-interfaces 改动后,Avahi 需要重新枚举接口并重新注册本机主机名。在某些时序下,Avahi 重启后没有正确发布 ubuphy001.local 这个主机名记录,导致本机 NSS 查询返回 Name or service not known。
再重启一次 Avahi,让它重新走一遍注册流程,主机名就恢复了:
sudo systemctl restart avahi-daemon.service
第二层问题:局域网内其他设备无法解析本机
现象
在 ubuvm001 上执行 avahi-browse -alr,看不到 ubuphy001 的任何服务。同时,从 ubuvm001 ping ubuphy001.local 也可能失败。
原因
mDNS 使用多播地址 224.0.0.251:5353(IPv4)。如果 ubuphy001 的防火墙拦截了 mDNS 多播包,它的广播就传不出去,局域网内其他设备收不到它的公告,自然无法解析 ubuphy001.local。
注意:这影响的是”别人能否解析你”,不影响”你自己 ping 自己”。
解决:放行防火墙的 mDNS 服务
ubuphy001 使用 firewalld:
sudo firewall-cmd --permanent --add-service=mdns
sudo firewall-cmd --reload
sudo firewall-cmd --list-services
输出中应包含 mdns:
dhcpv6-client http https mdns ssh
ubuvm001 使用 UFW:
sudo ufw allow 5353/udp
最终结果
在 ubuphy001 上:
ping ubuphy001.local
PING ubuphy001.local (192.168.1.104) 56(84) bytes of data.
64 bytes from ubuphy001 (192.168.1.104): icmp_seq=1 ttl=64 time=0.077 ms
...
在 ubuvm001 上:
ping ubuphy001.local
PING ubuphy001.local (192.168.1.104) 56(84) bytes of data.
64 bytes from 192.168.1.104 (192.168.1.104): icmp_seq=1 ttl=64 time=0.524 ms
...
双向解析均恢复正常。
关于 avahi-browse 的补充说明
排查过程中容易产生一个误解:以为 avahi-browse 能列出所有主机。
实际上,avahi-browse 的工作是发现网络上广播的服务(Service Discovery),不是列出所有主机名。它显示的是”某台机器通过 mDNS 宣告了某个服务”。
ubuvm001之所以出现在ubuphy001的avahi-browse输出中,是因为它注册了Device Info和Microsoft Windows Network(SMB)服务。- 如果一台机器只跑了 Avahi 的基础主机名广播,没有注册任何服务,那么
avahi-browse就看不到它——这跟-l参数无关。
验证主机名解析,应该用 avahi-resolve -n xxx.local,而不是 avahi-browse。
关键认知总结
| 层面 | 配置 | 作用 | 影响范围 |
|---|---|---|---|
| Avahi 广播层 | allow-interfaces=enp1s0 | 决定 Avahi 在哪个网卡上说话,排除 Docker 接口 | 影响本机 mDNS 广播内容 |
| Avahi 重启 | systemctl restart avahi-daemon | 重新注册主机名记录 | 影响本机 .local 解析是否正常 |
| 防火墙层 | firewall-cmd --add-service=mdns 或 ufw allow 5353/udp | 决定 mDNS 多播包能否通过防火墙 | 影响其他设备能否解析本机 |
三个容易混淆的点:
ping xxx.local走本机进程间通信,不经过防火墙。 本机解析失败,查 Avahi 状态和 NSS 配置,不要查防火墙。- 防火墙只影响”别人能否解析你”。 放行
5353/udp解决的是局域网内其他设备的解析问题。 deny-interfaces不支持通配符。 用allow-interfaces白名单更稳妥。
附:完整操作清单
ubuphy001(firewalld):
# 1. 限制 Avahi 只在物理网卡上广播
sudo vi /etc/avahi/avahi-daemon.conf
# 在 [server] 段落添加:
# allow-interfaces=enp1s0
# 2. 重启 Avahi
sudo systemctl restart avahi-daemon.service
# 3. 放行 firewalld 的 mDNS 服务
sudo firewall-cmd --permanent --add-service=mdns
sudo firewall-cmd --reload
# 4. 验证
ping ubuphy001.local
avahi-resolve -n ubuphy001.local
ubuvm001(UFW):
sudo ufw allow 5353/udp
ping ubuphy001.local