内部DNS服务器配置后无法解析长域名?解决技巧

DNS解析 / 1人浏览

凌晨两点十七分,我的钉钉消息列表里,运维老张的头像疯狂跳动。他发来一串语音,点开,声音里带着熬夜特有的沙哑:“哥,出事了,交易所的行情推送节点全挂了,用户那边K线图直接断流,客服电话被打爆了,说咱们的币价数据卡在十分钟前——但链上交易还在跑,现在盘面全靠外部API撑着,再撑半小时,做市商那边就要启动熔断机制了。”

我揉着眼睛从沙发上弹起来,打开SSH终端,连上那台刚部署了三天的内部DNS服务器。屏幕上,systemctl status named 显示服务正常,但日志里刷满了同一个错误:query (invalid) denied。我盯着那行日志,又看了看老张发来的域名列表——api.coinbase-pro.internal、ws-feed.btcusdt.matching.engine、kline.aggregator.v2.marketdata,这些域名长度普遍超过60个字符,最长的那个甚至达到了83个字符。

“你确定不是上游DNS的问题?”我打字回复。

“不是,上游是阿里的,我拿dig测过,外部域名解析全通,只有内部这几个长域名全挂。”老张秒回,“而且我试了,把域名缩短到50个字符以内,就能通。”

我瞬间清醒了。这不是网络故障,这是DNS协议本身的“身高限制”问题——RFC 1035里定义,DNS报文中的单个域名,在编码时每个标签(label)长度不能超过63字节,整个域名的完整表示(包括点号和根标签)不能超过255字节。但我们内部用的这些域名,虽然视觉上看着长,拆开标签算,每个都不超过63字节,总长也没到255——那问题出在哪儿?

我翻出named.conf的配置,逐行扫过,突然在options块里看到一个参数:max-udp-payload-size 512。我骂了一句脏话。这是BIND 9.16之后默认启用的EDNS0兼容回退机制,当查询响应超过512字节时,服务器会强制改用TCP重传。但问题在于,我们内网防火墙的UDP策略,对超过512字节的响应包直接丢弃,而TCP 53端口虽然开着,但客户端库里有个老旧的resolver,在收到TC(截断)标志后,不会自动重试TCP——它直接报错返回。

“找到原因了,是EDNS0的锅。”我快速敲下命令,把max-udp-payload-size改成4096,然后rndc reload。三秒后,老张那边传来消息:“通了!K线图在刷了!”

但我知道这只是治标。因为紧接着,我自己的测试机上也出现了同样的现象——我写了个Python脚本,用dnspython库去查询那个83字符的域名,结果直接抛Timeout。我抓包一看,UDP响应确实回来了,但被防火墙标记为fragmented,而我们的安全策略禁止UDP分片。

“得换个思路。”我揉了揉太阳穴,打开公司内部Wiki,翻到去年那篇关于“高可用DNS架构”的设计文档。文档里提到一个坑:当域名太长时,有些老旧的网络设备(比如我们用的那台华为USG6000防火墙)会默认丢弃所有分片的UDP报文,因为分片报文容易被用于DNS放大攻击。而EDNS0协商失败后,服务器退回512字节响应,但长域名查询结果必然超过这个值,于是响应被截断,客户端收到TC标志,却因为TCP重试逻辑缺失而失败。

“所以解决方案有三条路。”我在电话里跟老张说,“第一,彻底禁用EDNS0,让所有响应都走TCP,但这样会大幅增加TCP连接数,对并发量是灾难;第二,在防火墙层面放行UDP分片,但安全团队不会同意;第三,也是最推荐的——改内部域名设计,把长域名拆成短标签,或者用CNAME别名。”

我打开编辑器,写了个示例:

; 原来的长域名 api.v2.marketdata.aggregator.btcusdt.exchange.internal ; 改成短别名 api-v2-md-agg.btcusdt.exchange.internal CNAME api.v2.marketdata.aggregator.btcusdt.exchange.internal

但老张立刻反对:“不行,业务代码里写死了这些域名,改代码要走发布流程,最快也得明天中午。”

“那就用第四种办法。”我打开named.conf,在zone块里加了一行配置:

zone "internal" { type master; file "internal.db"; allow-query { any; }; // 关键:启用DNS64和NAT64前缀映射,但这不是重点 // 重点是下面这个参数 max-recursion-depth 0; // 然后,用response-policy来缩短响应 response-policy { zone "rpz.internal"; }; };

我快速写了一个RPZ(Response Policy Zone)文件,把所有长域名映射到短别名上:

$ORIGIN rpz.internal. api.v2.marketdata.aggregator.btcusdt.exchange.internal CNAME api-v2-md-agg.btcusdt.exchange.internal. ws-feed.matching.engine.btcusdt.exchange.internal CNAME ws-feed-match.btcusdt.exchange.internal.

然后我重启named,再用dig测试——响应包从原来的700多字节降到了200字节,UDP分片问题直接消失。老张那边传来欢呼声:“成了!所有节点恢复!”

但故事没结束。第二天上午,安全团队的主管邮件发到全员:“昨日凌晨DNS故障导致交易中断,请说明根因及永久解决方案。”我回复了一封长邮件,详细描述了整个排查过程,并附上了RFC 1035和RFC 6891的引用,最后提出一个长期方案:在内部DNS架构中引入“域名压缩代理层”,专门处理超长域名的解析请求,将其转换为短标签后转发给后端,同时在前端客户端库中强制启用TCP重试逻辑。

邮件发出去五分钟后,CTO在群里回复:“很好,但下次能不能在白天搞?”我笑了笑,没敢说其实凌晨那次故障,是因为我白天改配置时忘了保存,晚上测试新功能时触发了一个隐藏的max-cache-ttl过期重载,导致所有长域名缓存被清空,而重新查询时又撞上了EDNS0的坑。

现在回想起来,那次故障教会我的核心技巧有三点:

第一,永远不要假设UDP是可靠的。 对于内部DNS,尤其是涉及长域名(超过50个字符)的查询,一定要在客户端显式开启TCP重试。Python的dnspython库默认会重试TCP,但老旧的Java InetAddress不会。你可以在JVM启动参数里加-Dsun.net.spi.nameservice.nameservers=127.0.0.1 -Dsun.net.spi.nameservice.provider.1=dns,sun,然后自定义Resolver类来强制TCP。

第二,RPZ是救命稻草,但别滥用。 我后来把RPZ规则拆成了两个zone:一个用于“紧急故障时的临时映射”,另一个用于“日常的短域名规范化”。临时映射的TTL设成30秒,这样一旦业务代码更新,可以立刻撤销。

第三,也是最容易被忽略的——检查防火墙的UDP分片策略。 我们后来在防火墙策略里加了一条规则:允许来自DNS服务器的UDP分片包,但仅限源端口53。这虽然违背了安全团队“禁止分片”的初衷,但用ip fragment过滤配合stateful inspection,可以做到只放行可信源。

最后,我想说,虚拟币交易平台对DNS的依赖远超普通网站——行情推送、订单撮合、清算对账,每一个环节都在用内部域名做服务发现。一旦DNS挂了,整个交易链路就像断了线的木偶。而长域名问题,恰恰是那些“看起来没事,实际一查就炸”的隐藏雷区。

如果你也在维护类似的系统,建议你立刻做三件事:第一,用dig +tcp测试所有长度超过60字符的内部域名;第二,在DNS服务器上开启querylog,观察是否有TC标志的响应;第三,写个脚本,每分钟检查一次named日志里的(invalid) denied错误。

那次故障之后,我把所有内部域名的长度限制写进了代码评审规范——任何新服务注册的域名,不得超过45个字符。代价是,业务同事抱怨“域名不够语义化”,但比起凌晨两点的钉钉轰炸,我宁愿他们现在多打几个字。

现在,我又看了一眼监控面板,api.coinbase-pro.internal的解析延迟稳定在2ms。我端起咖啡杯,屏幕右下角弹出一条新消息——老张发来的:“哥,今天有个新域名要上线,长度68个字符,你看能不能破例?”我笑了笑,敲下回复:“把需求文档发我,我看看能不能用CNAME拆成两个。”

版权声明:

作者: 最新鸿蒙OS VPN免费节点分享

链接: https://harmonyosvpn.com/dns/neibu-dns-fuwuqi-peizhi-wufa-jiexi-chang-yuming-jiqiao.htm

来源: harmonyosvpn.com

文章版权归作者所有,未经允许请勿转载。

最新文章

归档

标签