
别让 systemd-resolved 毁掉你的内网 DNS说实话在银河麒麟 V10以下简称麒麟 V10上折腾内网 DNS我一开始是真没把 systemd-resolved 当回事。直到我在一台干净的系统上装好 Bind9配好 zone 文件满心欢喜地启动服务然后 dig 一下内网域名——还是去了公网 DNS 那边而且 /etc/resolv.conf 死活不听我指挥。那一刻我才反应过来这又是 systemd-resolved 在背后抢地盘。如果你也在麒麟 V10 上做过类似的事大概率能对上号服务装好了、端口也监听了但系统解析请求根本不走你的 Bind9或者你一重启电脑配置就“回到解放前”。这篇文章就是把我在麒麟 V10 下用 Bind9 搭内网 DNS 的全过程、踩过的坑和最终的解决办法一次性写清楚。适合谁来参考两类人一是刚接触麒麟 V10、想在公司内网或者实验室里搭一套私有 DNS 的运维/开发二是已经被 systemd-resolved 折磨过、想搞清楚它到底怎么关、怎么绕的人。内容覆盖从安装、配置、zone 文件编写到最关键的 systemd-resolved 冲突处理、防火墙/SELinux 放行再到验证和排错每一步我都会解释“为什么要这么做”而不是只丢给你一串命令。1. 环境准备与整体思路拆解1.1 为什么选择 Bind9 而不是 Dnsmasq我知道可能有人会问内网 DNS 用 Dnsmasq 不是更轻量吗确实Dnsmasq 在几十台机器的小型局域网里完全够用配置也简单甚至还能顺带做 DHCP。但我选择 Bind9 的原因很直接麒麟 V10 这类服务器场景通常应用在政企、军工或专业研究环境这类环境对 DNS 的规范性要求更高Bind9 支持的 zone 管理、view 分区、ACL 控制、日志审计这些能力是 Dnsmasq 给不了的。还有一点很现实内网 DNS 往往不只是“解析几个机器名”那么简单后续你可能要加反向解析、要按来源 IP 返回不同结果、要做子域委托Bind9 的扩展性明显更好。如果你只是临时用一下、机器不超过 20 台那 Dnsmasq 确实更省事但我这篇文章按 Bind9 来写因为标题就是要用 Bind9而且它在生产环境里的表现更稳。1.2 麒麟 V10 的系统特点对 DNS 的影响麒麟 V10 有桌面版和服务器版之分无论哪个版本底子都是 Linux。大部分基于 RPM 体系的发行版网络管理用的是 NetworkManagerDNS 解析栈用的是 glibc nsswitch而 systemd-resolved 则是中间的一个“代理层”。关键点在这麒麟 V10 默认开启了 systemd-resolved它会去监听 127.0.0.53:53。你 Bind9 装好再监听 53 端口要么起不来要么起来了但系统请求根本不会发到你的 Bind9 上因为本机解析流量都被 systemd-resolved“截胡”了。这套机制本来是解决多网络切换时 DNS 自动更新的问题但在固定内网环境里它就是添乱的那个角色这也是这篇文章标题里“别再被 systemd-resolved 坑了”的由来。在开始动手之前我先把整体思路理一遍你心里有个底装好 Bind9 相关包。写主配置文件 named.conf定义 access control、监听地址、zone 声明。编写正反向 zone 数据文件。用 named-checkconf、named-checkzone 做语法检查。处理 systemd-resolved 冲突让本机解析走自己的 Bind9。放行防火墙、调整 SELinux确保远程机器也能查询。用 dig、host、systemd-resolve 等工具做全面验证。每一步都有坑我一个个说。2. Bind9 安装与核心配置详解2.1 安装 Bind9 及配套工具麒麟 V10 的软件仓库里有现成的 Bind 包直接用 yum 装就行yum install -y bind bind-utilsbind 是主程序bind-utils 提供 dig、host、nslookup 这些查询工具强烈建议一起装后面验证全靠它们。装完之后确认一下版本named -v正常情况下会输出类似BIND 9.11.x之类的信息。麒麟 V10 的仓库版本不算特别新但稳定够用。这里有个小细节麒麟 V10 上 named 服务默认以named用户运行但如果你装了 bind-chroot 包有些定制镜像里会带上这个那 named 的根目录会被锁到/var/named/chroot下面配置文件路径、zone 文件路径全都要跟着变。我这次在标准麒麟 V10 上没遇到 chroot但如果你发现自己改了/etc/named.conf却完全不生效、服务还起不来先检查是不是装了 chroot 版。这是很多人在麒麟环境里遇到的第一个“隐形坑”。2.2 主配置文件 /etc/named.conf 的实战写法先说明一下麒麟 V10 自带的/etc/named.conf是一个最小默认配置它会在本机回环接口上监听 DNS 查询并且只允许本机访问完全没法当内网 DNS 用。我们的目标很简单监听内网 IP允许内网网段查询配置我们的私网 zone。我直接给出一个我在内网环境常用的模板你按自己环境改一下 IP 和域名就行options { listen-on port 53 { 127.0.0.1; 192.168.10.10; }; listen-on-v6 port 53 { ::1; }; directory /var/named; dump-file /var/named/data/cache_dump.db; statistics-file /var/named/data/named_stats.txt; memstatistics-file /var/named/data/named_mem_stats.txt; recursion yes; allow-query { localhost; 192.168.10.0/24; }; allow-recursion { localhost; 192.168.10.0/24; }; allow-transfer { none; }; forwarders { 114.114.114.114; 223.5.5.5; }; dnssec-enable yes; dnssec-validation yes; managed-keys-directory /var/named/dynamic; pid-file /run/named/named.pid; session-keyfile /run/named/session.key; }; zone . IN { type hint; file named.ca; }; zone internal.example IN { type master; file internal.example.zone; allow-update { none; }; }; zone 10.168.192.in-addr.arpa IN { type master; file 192.168.10.zone; allow-update { none; }; }; include /etc/named.rfc1912.zones; include /etc/named.root.key;挑几个关键配置单独解释免得你照抄之后不知道动了哪根筋第一listen-on里除了回环地址必须加上服务器本身的内网 IP。如果不加Bind9 默认只监听本机回环局域网其他机器根本连不上。很多人装完发现“只有自己能解析别人解析不了”九成是这里没配。第二allow-query和allow-recursion建议明确限制网段别图省事直接写any。内网 DNS 如果对全网开放递归很容易被当成开放解析器轻则被外部扫描重则被用来做 DNS 放大攻击。这一点在政企环境里尤其敏感。我的习惯是查询和递归都严格限制到内网子网。第三forwarders配的是上游公网 DNS比如 114.114.114.114、223.5.5.5。内网解析不了的外部域名Bind9 会转发给它们。如果你所在内网有安全要求、不允许出公网解析那这一段可以直接删掉Bind9 就只做纯内网解析。第四dnssec-enable和dnssec-validation这两个参数如果你的内网 DNS 纯粹解析私网域名其实可以关掉设为 no能省掉不少 DNSSEC 验证上的麻烦。但如果你还要转发解析公网域名建议保持 yes不然有些启用了 DNSSEC 的域名会解析失败。2.3 zone 文件里最容易写错的地方配置好主文件之后正反向 zone 文件是另一大“事故高发区”。我写一个正向 zone 示例$TTL 1D IN SOA ns1.internal.example. admin.internal.example. ( 2024011801 ; serial 3H ; refresh 15M ; retry 1W ; expiry 1D ) ; minimum IN NS ns1.internal.example. ns1 IN A 192.168.10.10 gw IN A 192.168.10.1 web01 IN A 192.168.10.21 db01 IN A 192.168.10.22反向 zone 文件$TTL 1D IN SOA ns1.internal.example. admin.internal.example. ( 2024011801 ; serial 3H ; refresh 15M ; retry 1W ; expiry 1D ) ; minimum IN NS ns1.internal.example. 10 IN PTR ns1.internal.example. 1 IN PTR gw.internal.example. 21 IN PTR web01.internal.example. 22 IN PTR db01.internal.example.这里必须提醒几件事都是我在实际配置中踩过的或者帮别人排查时见过的高频问题一是 SOA 记录里的serial。它相当于 zone 文件的版本号每次你修改这个 zone 文件必须把 serial 数字往上加否则从服务器或者缓存不会重新加载。很多新手改完 zone 文件重启 nameddig 发现还是旧数据就是 serial 没改。二是反向 zone 文件名和网络段的对应关系。192.168.10.0/24网段的反向 zone 名是10.168.192.in-addr.arpa也就是把 IP 反过来写、去掉最后一段然后把in-addr.arpa附加在后面。反过来10.168.192里面对应的反向记录是 IP 最后一段不是完整 IP。三是文件结尾必须有换行。这个听起来很蠢但 Bind9 对 zone 文件结尾缺失换行的情况会直接报 parse error服务起不来。Vim 默认会自动加换行但如果是用 echo 或者某些编辑器写文件很容易踩到这个坑。四是文件所有者和权限。麒麟 V10 上 zone 文件放在/var/named/下面建议属主设为named:named权限给640。如果权限不对named 进程可能读不到 zone 文件日志里会报 permission denied服务看起来是起来了但 zone 加载失败解析照样不通。3. 核心实操让系统解析真正走内网 DNS3.1 关键冲突点systemd-resolved 的 127.0.0.53前面说了一堆理论现在到真正决定成败的环节了。麒麟 V10 上默认跑着 systemd-resolved它干了一件大事把127.0.0.53:53这个地址占了。所有本机应用程序查 DNS比如你直接在服务器上 dig、ping 域名都会先发给 127.0.0.53再由 systemd-resolved 转发给它在/etc/resolv.conf里记录的上游 DNS。也就是说就算你把/etc/named.conf配得再对、把 Bind9 服务起来并且绑定了 53 端口你也监听不了 127.0.0.53被 systemd-resolved 占着而且本机程序发出去的解析请求也只会去 127.0.0.53根本轮不到你的 Bind9 处理。这就是“服务起来了、配置也没错但解析不生效”的终极原因。解决思路分两种方案 A停掉 systemd-resolved释放 127.0.0.53把/etc/resolv.conf改成指向 127.0.0.1 或服务器内网 IP。这是最干净、最直接的做法内网固定环境比较推荐。方案 B保留 systemd-resolved但关闭它的 stub listener然后让它把你指定的 DNS 作为上游转发下去。适合不太想动系统组件、又希望统一管理解析的场景。我在实际部署中更倾向于方案 A理由很简单既然我们自己在跑 Bind9就没必要再留一层转发代理减少一层就少一个故障点。3.2 动手关闭 systemd-resolved方案 A 实操先编辑 systemd-resolved 的配置文件vim /etc/systemd/resolved.conf找到DNSStubListener这一行修改成DNSStubListenerno这一行的意思是让 systemd-resolved 不再监听 127.0.0.53:53把端口还给系统。如果这行被注释了去掉注释后改。然后依次执行systemctl stop systemd-resolved systemctl disable systemd-resolvedstop是立刻停掉disable是防止开机重启后又起来。两个都要做只做stop的话机器一重启 systemd-resolved 又回来了DNS 又乱套。接下来处理/etc/resolv.conf。这个文件在麒麟 V10 上默认是指向/run/systemd/resolve/stub-resolv.conf的软链接内容里写着nameserver 127.0.0.53。现在我们把它替换成真实的配置文件rm -f /etc/resolv.conf然后新建一个写入我们自己的配置vi /etc/resolv.conf内容就两行核心配置nameserver 127.0.0.1 search internal.examplenameserver 127.0.0.1表示本机所有解析请求都交给本机的 Bind9也就是我们刚刚搭好的内网 DNS。search internal.example是可选配置加上之后你 ping web01 的时候系统会自动解析成web01.internal.example内网体验会舒服很多。这个步骤要注意一个问题如果你这台机器用的是 NetworkManager 管理网络连接它可能在网卡重连、重启网络服务的时候自动覆盖/etc/resolv.conf。到时候你会发现又变成了老样子。解决办法有两个一是把 NetworkManager 的 DNS 接管功能禁用掉在/etc/NetworkManager/NetworkManager.conf的[main]段下加一行[main] dnsnone改完重启 NetworkManagersystemctl restart NetworkManager二是干脆不用 NetworkManager 管理这个网卡改用静态配置这个看你们内网的管理习惯。我个人更推荐静态 IP 手动管理的/etc/resolv.conf在服务器场景下最可控。3.3 保留 systemd-resolved 的退让方案方案 B 实操如果你所在环境的运维规范里强制要求 systemd-resolved 必须保持运行有些安全基线扫描会限制系统组件状态那方案 B 可以让你少动系统组件同样打开/etc/systemd/resolved.confvim /etc/systemd/resolved.conf先关掉 stub listenerDNSStubListenerno再增加两条DNS127.0.0.1 Domainsinternal.exampleDNS127.0.0.1的意思是让 systemd-resolved 把解析请求转发给本机 Bind9Domainsinternal.example是在私网域上用这个 DNS。保存后systemctl restart systemd-resolved然后在/etc/resolv.conf里保持nameserver 127.0.0.53不动因为 systemd-resolved 还能正常转发。但这种做法的缺点很明显你没法彻底卸载 systemd-resolved多了一层进程排查问题时要多考虑一层转发链路。我自己的习惯是内网环境直接方案 A只有安全扫描特别严格的环境才用方案 B。4. 防火墙、SELinux 与远程查询放行4.1 防火墙必须放行 TCP/UDP 53Bind9 服务起来了本机也能解析了但局域网其他机器还是查不了——那问题多半出在防火墙。麒麟 V10 默认可能开着 firewalld 或者直接用 iptables取决于你的桌面/服务器定制包。我用 firewalld 的情况比较多放行 DNS 服务的命令firewall-cmd --permanent --add-servicedns firewall-cmd --reload如果你用的是 iptables 管理那就得自己加规则iptables -A INPUT -p udp --dport 53 -j ACCEPT iptables -A INPUT -p tcp --dport 53 -j ACCEPT service iptables save这里要特别提醒一下DNS 查询默认走 UDP 53但区域传输AXFR/IXFR走 TCP 53。如果你只放行了 UDPdig 查询正常但dig axfr可能超时。日常使用建议 TCP 和 UDP 都放行免得到时候排查半天。还有个小概率坑如果 Bind9 是绑定在特定内网 IP 上而防火墙开启了zone管理你得确保允许流量进入的 zone 里包含了对应网卡。否则就算你加了--add-servicedns它可能只对默认 zone 生效。4.2 SELinux 对 named 的限制很多人在麒麟 V10 上安装完 Bind9服务起不来查日志发现Permission denied或者open /etc/named.conf: Permission denied然后百思不得其解我明明用 root 运行的怎么会没权限原因多半是 SELinux。麒麟 V10 默认开启 SELinux而 named 在 SELinux 中被限制在一个特定上下文里它只能读特定目录下的配置文件。如果你把 zone 文件放在了别的自定义目录又没更新 SELinux 上下文就会触发拒绝。查看 SELinux 状态getenforce如果是Enforcing那就要给 named 相关的文件打上正确上下文。常用命令semanage fcontext -a -t named_zone_t /var/named(/.*)? restorecon -Rv /var/named如果 zone 文件放在/var/named下默认上下文正常是没问题的。但如果文件是新创建的、来源是上传压缩包解压出来的SELinux 上下文可能没继承就是历史安全上下文丢失需要执行restorecon -Rv /var/named恢复。如果你确实不想处理 SELinux 上下文但也有别急着直接setenforce 0——这在政企环境里可能过不了安全基线。建议先用ausearch -m avc查一下具体是哪条策略被拒绝了再对症处理能不动 SELinux 就不动。5. 启动服务与解析验证的完整记录5.1 启动 Bind9 并设为开机自启配置文件、zone 文件都准备完之后别急着直接systemctl start named先做语法检查。检查主配置named-checkconf /etc/named.conf没有任何输出说明语法没问题。检查正向 zonenamed-checkzone internal.example /var/named/internal.example.zone检查反向 zonenamed-checkzone 10.168.192.in-addr.arpa /var/named/192.168.10.zone这两条命令会输出OK或者直接报错。我特别建议大家养成这个习惯先检查再启动。省得启动失败之后去翻日志冤枉得很。语法检查通过后正式启动systemctl start named systemctl enable named然后确认一下状态systemctl status named如果服务状态是 active (running)再看端口监听netstat -lnptu | grep 53你会看到named监听在127.0.0.1:53和192.168.10.10:53上面。如果你之前已经关闭了 systemd-resolved这里不会出现127.0.0.53:53占用的情况这也是验证系统组件是否处理干净的一个标志。5.2 用 dig 验证正反向解析与转发能力服务起来之后第一轮验证本机解析dig 127.0.0.1 web01.internal.example重点看 Answer 段有没有返回192.168.10.21以及SERVER字段显示的是不是127.0.0.1#53。如果 Answer 段为空或者REFUSED说明 zone 加载没生效或者查询权限没配上。再测反向dig 127.0.0.1 -x 192.168.10.21正常会返回21.10.168.192.in-addr.arpa. 86400 IN PTR web01.internal.example.然后验证公网域名转发是否正常dig 127.0.0.1 www.baidu.com如果之前配了 forwarders这里会返回真实 IP说明你的内网 DNS 能同时做私网和公网解析可以当内网统一的解析入口。最后从局域网内另一台机器上验证远程查询。把客户机的 DNS 临时改成 192.168.10.10你的 Bind9 内网 IP然后dig 192.168.10.10 web01.internal.example如果通了说明防火墙、allow-query 都配置正确。这一步一定要做很多人在服务器本机测的没问题但远端客户机一查就失败基本都是防火墙或者 allow-query 没放行。6. 常见问题与排查技巧实录6.1 高频故障速查表我在麒麟 V10 上折腾 Bind9 的时候记录过不少问题这里整理成一个速查表方便你对照排查现象可能原因排查方向与解决服务启动失败日志报 permission deniedSELinux 上下文不对或 zone 权限问题检查/var/named下文件的属主和权限restorecon -Rv /var/nameddig 查询返回 SERVFAILzone 文件语法错误或 serial 未更新运行named-checkzone检查确认 serial 修改过本机能解析远端机器不行防火墙未放行或 allow-query 没配网段firewall-cmd --add-servicedns检查 named.conf 的 allow-query开机后 DNS 配置被重置NetworkManager 覆盖 resolv.conf在/etc/NetworkManager/NetworkManager.conf里设置dnsnone本机 dig 还是走 127.0.0.53systemd-resolved 没关干净检查systemctl status systemd-resolved确认 stop 和 disable 都执行重启后 systemd-resolved 复活只 stop 了没 disable执行systemctl disable systemd-resolved日志报 resolver priming query 超时内网无法访问上游公网 DNS关闭 forwarders 或改为内网可访问的 DNS 网关6.2 一个我印象深刻的排查案例有一次我在一台麒麟 V10 服务器上搭好 DNS本机测试一切正常但第二天发现内网其他机器全都不听指挥还是用各自原来的 DNS。我上服务器一查/etc/resolv.conf又变回了127.0.0.53systemd-resolved 也自动回来了。后来才发现这台机器上有一个定时任务在每晚同步网络配置直接调用了 NetworkManager 的接口NetworkManager 不仅把网卡配置刷新了还把/etc/resolv.conf的软链接又建回来了。所以说只关 systemd-resolved 不够还得把 NetworkManager 的 DNS 接管功能关掉双管齐下才能真正“锁死”解析配置。这种问题在纯手动配置的 Linux 上很少出现但在麒麟 V10 这种带桌面环境和管理工具的发行版上特别容易碰到。遇到这种“配置总是被改回去”的情况最后一个办法是可以把/etc/resolv.conf设为不可变属性chattr i /etc/resolv.conf这个命令会让文件无法被修改就算是 root 也不行除非先chattr -i。这是强制手段慎用但它确实能在一堆“管家工具”争抢 DNS 配置的时候保你一条命。6.3 日志是排错的第一信源排查 Bind9 问题日志永远是最靠谱的。麒麟 V10 上 named 的日志一般在/var/named/data/named.run或者用 journalctl 查journalctl -u named -f-f是实时跟踪输出适合在启动服务或者做查询的同时观察日志。常见的关键日志有client 192.168.x.x#xxxxx: query说明收到了某个客户端的查询请求如果远端机器查不了先看这里有没有到达 Bind9。zone internal.example/IN: loaded serial 2024011801说明 zone 加载成功。error (network unreachable) resolving说明 Bind9 在向上游 DNS 发请求时网络不通检查 forwarders 配置和网络路由。dns_master_load: file not found说明 zone 文件路径写错了或者文件不存在。很多人一上来就抓瞎不知道该看什么我建议遇到问题先按这个顺序来查服务状态systemctl status named→ 查端口监听netstat -lnptu | grep 53→ 查日志journalctl -u named -f→ 在本机 dig 测试 → 在远端 dig 测试。从内到外逐层排查基本能定位八成问题。7. 一些额外的加固建议DNS 服务在内网里往往扮演着“基础设施”的角色出问题影响面大暴露面也大。我额外补充几个建议方便你部署到生产环境前想清楚第一zone 传输要限制。如果你需要部署多台 DNS 做冗余主从同步时一定要注意 allow-transfer 只写从服务器 IP别写 any。我之前见过一个内网环境因为 allow-transfer 没限制被别人一条dig axfr internal.example就拿到了全部内网主机名和 IP 映射整个内网拓扑直接暴露了。第二日志可以做单独配置。Bind9 默认的日志比较简单如果需要审计内网所有解析记录可以单独配置 channel 把 query log 落盘到独立的日志文件配合 logrotate 做轮转。内网安全审计时解析记录往往比流量记录更有价值。第三版本升级和补丁要及时。Bind9 是开源 BIND 的发行版历史上出现过不少 CVE虽然内网环境相对安全但如果你的内网 DNS 同时也承担公网域名的递归解析那就处于公网可达状态该打的补丁必须打。第四DNS 和 DHCP 要联动规划。很多内网环境用 DHCP 自动分配 IP如果 DHCP 分配给客户机的 DNS 还是旧地址那你这边 DNS 配得再完美也白搭。记得检查 DHCP 服务器上的 DNS 选项统一发一个新的 DNS 地址。写在最后麒麟 V10 下用 Bind9 搭内网 DNS技术上不复杂真正让人头疼的往往不是 Bind9 本身而是 systemd-resolved 和 NetworkManager 这些系统组件在背后“偷偷捣乱”。把这几个关系理清楚、配置文件落到位、防火墙/SELinux 处理好一套能服务整个内网的 DNS 环境就算真正立住了。我个人在实际部署中还有一个体会DNS 这种基础服务最重要的是“改动可回滚”。每次修改完配置、升级 serial、重启服务前最好把旧的配置文件和 zone 文件备份一份出了问题能快速回到上一个正常状态。这个习惯帮我省过不少半夜被叫起来救火的场景。最后再分享一个小技巧把named-checkconf和named-checkzone两条检查命令写进每次部署的固定流程里哪怕只是改一行配置也先检查再重启这个习惯能帮你避开至少一半的坑。