BIND主从域名服务器与分离解析实战:从单点故障到内外网安全解析
发布时间:2026/10/17 13:08:58 锦皓数字建站

1. 为什么还要自己搭DNS从一次内部服务发现故障说起很多团队觉得域名解析是基础设施的事买了个域名、配几条A记录就完事了。直到某天内部服务大面积超时排查半天发现是单台DNS服务器扛不住并发查询或者某条解析记录被误改导致整个测试环境瘫痪才意识到DNS这块看着简单真出问题就是全局性的。我这次要聊的就是怎么用主从域名服务器加分离解析这两套机制把DNS从能用做到稳且安全。先说清楚这套东西是什么。主从域名服务器Master/Slave现在官方叫Primary/Secondary解决的是单点故障和负载分担问题——主服务器负责写记录从服务器自动同步主服务器的区域数据对外提供查询。分离解析Split DNS / View解决的是内外有别的问题——同一个域名内网用户查到的是内网地址外网用户查到的是公网地址两拨人互不干扰。这套方案适合谁我总结下来是三类人一是中小团队里兼管网络和运维的工程师二是需要给内部系统做私有域名解析的开发者三是准备考网络相关认证、需要动手把DNS原理跑通一遍的学习者。哪怕你之前只会在域名服务商后台点几下鼠标加记录跟着这篇走完也能把DNS的底层逻辑摸个七七八八。我下面用的环境是Linux下的BINDBerkeley Internet Name Domain这是目前最主流的开源DNS服务软件绝大多数发行版的软件源里都有。整个实操我会拆成设计思路、核心配置、完整落地、排错四块来讲每一步都告诉你为什么这么配而不是让你照抄。2. 整体设计思路主从怎么分工分离解析靠什么实现2.1 主从架构到底解决了什么问题单台DNS服务器的问题很直白它挂了所有依赖它解析的机器全部抓瞎。有人会说那我多配几个DNS地址不就行了问题是如果这几台服务器各管各的记录不一致A机器查到的是新地址B机器查到的是旧地址那才是灾难。所以主从的核心不是多几台而是数据一致性前提下的冗余。主服务器Primary是唯一可以修改区域数据的地方所有记录的增删改都在这台上做。从服务器Secondary通过区域传送Zone Transfer机制定期或在主服务器通知时把整个区域文件拉过来。这里有个关键点从服务器拉取的是整个区域不是单条记录所以它天然和主服务器保持一致。区域传送分两种完全区域传送AXFR会把整个区域文件传一遍增量区域传送IXFR只传变化的部分。数据量小的时候差别不大但区域里记录成千上万条时IXFR能省下大量带宽。BIND默认会优先尝试IXFR失败再退回AXFR这个不用手动干预。那从服务器怎么知道主服务器数据变了靠NOTIFY通知。主服务器一改记录序列号Serial变化它就会主动给从服务器发个通知说我更新了你来拉。从服务器收到通知立刻发起传送比单纯靠定时轮询快得多。当然定时轮询Refresh参数还是要配的作为通知丢失时的兜底。2.2 分离解析的实现原理View视图机制分离解析在BIND里靠View视图实现。你可以把View理解成给不同来源的查询者看不同的区域数据。每个View里可以定义自己的区域文件、自己的转发策略、自己的递归权限。判断一个查询该走哪个View靠的是匹配顺序。BIND会从上到下依次匹配每个View的match-clients条件第一个匹配上的就用那个View。所以顺序很重要通常把范围小的、内网的View放前面把any这种兜底的放最后。这里有个新手最容易踩的坑一旦用了View所有区域都必须定义在某个View里面不能有区域裸奔在View外面。因为BIND需要明确知道每个区域归哪个视图管。另外根区域.的定义也要注意如果两个View都要做递归那每个View里都得有根区域的提示文件配置。2.3 为什么选BIND而不是别的市面上DNS软件不少dnsmasq轻量适合做转发和缓存CoreDNS在容器环境里很流行PowerDNS有数据库后端。但要做标准的主从同步加分离解析BIND是最成熟、文档最全、行为最可预期的选择。它的配置文件语法虽然啰嗦但每一行都有明确含义调通了之后你对DNS协议的理解会上一个台阶。dnsmasq做分离解析就比较别扭CoreDNS的视图功能要靠插件拼都不如BIND直接。提示如果你的需求只是内网做个小缓存转发dnsmasq足够且更省资源。但只要涉及主从同步和内外分离直接上BIND别绕弯路。3. 核心配置细节区域文件、序列号与同步参数3.1 区域文件里那些不能乱填的参数区域文件Zone File是DNS数据的实际载体开头那段SOA记录是重中之重。我拿一个内部域名lab.internal举例SOA长这样$TTL 3600 IN SOA ns1.lab.internal. admin.lab.internal. ( 2024011501 ; Serial 序列号 3600 ; Refresh 刷新间隔 1800 ; Retry 重试间隔 604800 ; Expire 过期时间 86400 ; Minimum 最小TTL )逐行说。Serial序列号是主从同步的命脉从服务器就是靠比较自己和主服务器的序列号来决定要不要更新。格式通常是年月日两位序号比如2024011501表示2024年1月15日的第1次修改。这里有个铁律每次改完记录必须手动递增序列号否则从服务器根本不知道你改了。我见过太多人改完记录发现从服务器没同步查半天就是忘了改序列号。Refresh是从服务器多久主动问一次主服务器你序列号变了吗3600秒就是1小时。Retry是传送失败后多久重试1800秒。Expire是如果一直联系不上主服务器从服务器多久后放弃这份数据不再对外应答604800秒是7天。Minimum现在主要用作否定缓存的TTL即查不到这个结果缓存多久。注意Serial只增不减。如果你手滑改小了从服务器会认为自己的数据更新反而不会去同步。真改错了就把数字改得比原来更大。3.2 主服务器配置的关键几行主服务器的named.conf里区域定义要标明类型是master老版本写primary并指定区域文件路径zone lab.internal IN { type master; file /etc/named/zones/lab.internal.zone; allow-transfer { 192.168.10.12; }; // 只允许从服务器拉取 also-notify { 192.168.10.12; }; // 主动通知从服务器 };allow-transfer是安全关键绝对不能写成any否则任何人都能把你的整个区域文件拉走等于把内网拓扑图拱手送人。只写从服务器的IP。also-notify让主服务器改完记录主动推通知比等从服务器轮询快。3.3 从服务器配置与同步验证从服务器上对应的区域定义类型是slave或secondary要指定主服务器地址zone lab.internal IN { type slave; file /var/named/slaves/lab.internal.zone; masters { 192.168.10.11; }; };从服务器的区域文件不用手动创建BIND同步过来会自动生成。同步成没成看两个地方一是日志里有没有transfer of lab.internal/IN from 192.168.10.11这样的记录二是/var/named/slaves/目录下有没有生成文件。用dig 从服务器IP lab.internal SOA查一下如果返回的序列号和主服务器一致就说明同步成功了。3.4 分离解析的View配置骨架View的配置结构是这样的view internal { match-clients { 192.168.10.0/24; 127.0.0.1; }; recursion yes; zone lab.internal IN { type master; file /etc/named/zones/lab.internal.internal.zone; }; }; view external { match-clients { any; }; recursion no; zone lab.internal IN { type master; file /etc/named/zones/lab.internal.external.zone; }; };内网View允许递归recursion yes因为内网用户需要查外部域名。外网View关掉递归recursion no只回答自己权威的区域避免被当成开放解析器被人利用。两个View里同名区域指向不同的文件内网文件里写内网IP外网文件里写公网IP这就是分离解析的本质。提示match-clients里一定要带上127.0.0.1否则本机自己查自己都可能匹配不上任何View导致解析失败。4. 完整实操从零把主从加分离解析跑起来4.1 环境规划与软件安装我规划两台机器主服务器192.168.10.11从服务器192.168.10.12内网网段192.168.10.0/24。域名用lab.internal内网解析到192.168.10.100外网解析到203.0.113.100这是文档专用测试网段实际换成你的公网IP。两台机器都装BIND# 以常见的包管理为例 yum install bind bind-utils -y # 或者 apt install bind9 bind9utils -y装完先别急着配把防火墙放行53端口TCP和UDP都要UDP用于普通查询TCP用于区域传送和大响应firewall-cmd --permanent --add-port53/udp firewall-cmd --permanent --add-port53/tcp firewall-cmd --reload4.2 主服务器区域文件编写先建目录和文件。内网区域文件lab.internal.internal.zone$TTL 3600 IN SOA ns1.lab.internal. admin.lab.internal. ( 2024011501 3600 1800 604800 86400 ) IN NS ns1.lab.internal. IN NS ns2.lab.internal. ns1 IN A 192.168.10.11 ns2 IN A 192.168.10.12 app IN A 192.168.10.100 www IN A 192.168.10.100外网区域文件lab.internal.external.zone$TTL 3600 IN SOA ns1.lab.internal. admin.lab.internal. ( 2024011501 3600 1800 604800 86400 ) IN NS ns1.lab.internal. IN NS ns2.lab.internal. ns1 IN A 203.0.113.11 ns2 IN A 203.0.113.12 app IN A 203.0.113.100 www IN A 203.0.113.100两个文件结构一样只是IP不同。注意NS记录在两份文件里指向的地址也不同这是符合实际的——外网用户解析ns1.lab.internal应该拿到公网地址。4.3 主服务器named.conf完整配置options { listen-on port 53 { any; }; directory /var/named; allow-query { any; }; allow-recursion { 192.168.10.0/24; 127.0.0.1; }; forwarders { 8.8.8.8; 1.1.1.1; }; dnssec-validation no; }; view internal { match-clients { 192.168.10.0/24; 127.0.0.1; }; recursion yes; zone lab.internal IN { type master; file /etc/named/zones/lab.internal.internal.zone; allow-transfer { 192.168.10.12; }; also-notify { 192.168.10.12; }; }; }; view external { match-clients { any; }; recursion no; zone lab.internal IN { type master; file /etc/named/zones/lab.internal.external.zone; allow-transfer { 192.168.10.12; }; also-notify { 192.168.10.12; }; }; };forwarders配的是上游解析地址内网用户查外网域名时由这台服务器代为转发。dnssec-validation no是为了简化实验生产环境建议开启并配好信任锚。4.4 从服务器配置从服务器的named.conf同样用View结构但区域类型改成slaveoptions { listen-on port 53 { any; }; directory /var/named; allow-query { any; }; allow-recursion { 192.168.10.0/24; 127.0.0.1; }; forwarders { 8.8.8.8; 1.1.1.1; }; dnssec-validation no; }; view internal { match-clients { 192.168.10.0/24; 127.0.0.1; }; recursion yes; zone lab.internal IN { type slave; file /var/named/slaves/lab.internal.internal.zone; masters { 192.168.10.11; }; }; }; view external { match-clients { any; }; recursion no; zone lab.internal IN { type slave; file /var/named/slaves/lab.internal.external.zone; masters { 192.168.10.11; }; }; };从服务器的区域文件路径指向slaves目录这个目录要确保BIND进程有写权限。同步过来的文件会自动放在这里。4.5 启动与验证先检查配置语法这个命令能帮你抓出大部分拼写和语法错误named-checkconf named-checkzone lab.internal /etc/named/zones/lab.internal.internal.zone两个都通过后启动服务systemctl start named systemctl enable named验证分三步。第一步在主服务器上查内网视图dig 192.168.10.11 app.lab.internal应该返回192.168.10.100。第二步模拟外网查询。因为本机就在内网网段直接查会匹配到internal视图所以要指定一个不在内网网段的源地址来测试或者干脆从一台外网机器上查。如果手头没有外网机器可以临时把测试机的IP改到别的网段或者用dig的-b参数指定源地址dig -b 203.0.113.50 192.168.10.11 app.lab.internal应该返回203.0.113.100。第三步验证主从同步。在从服务器上查dig 192.168.10.12 app.lab.internal如果返回和主服务器一致的结果说明同步成功。再去主服务器改一条记录递增序列号重启或重载BIND然后看从服务器是否自动更新。4.6 序列号递增与重载的正确姿势改完区域文件后递增Serial然后rndc reload lab.internalrndc是BIND的管理工具reload只重载指定区域比重启整个服务温和。重载后主服务器会发NOTIFY给从服务器从服务器自动拉取。你可以在从服务器日志里看到传送记录。如果没配rndc密钥用systemctl reload named也行但那样是重载全部区域。注意每次改区域文件Serial必须变。我习惯在文件里写个注释记下每次改了什么时间长了回头看能省很多事。5. 常见问题与排查技巧实录5.1 从服务器死活不同步这是最高频的问题。排查顺序我整理成一张表现象可能原因排查方法从服务器无区域文件生成主服务器allow-transfer没放行从服务器IP主服务器日志看有无denied transfer序列号一致但不更新忘了递增Serial对比两边SOA序列号传送超时53/tcp被防火墙拦telnet主服务器53端口测试日志报refusedmasters地址写错核对从服务器masters配置同步了但查询无结果View匹配顺序问题确认查询源IP落在哪个View我踩过最坑的一次是allow-transfer里写了从服务器的公网IP但从服务器实际是用内网IP去连主服务器的导致一直被拒。后来统一用内网IP通信才解决。所以主从之间走哪个网段allow-transfer就写哪个网段的IP别想当然。5.2 分离解析不生效内外查到一样的结果八成是View的match-clients顺序或范围写错了。BIND从上往下匹配如果external视图写在internal前面且match-clients是any那所有查询都会命中external。正确做法是内网View在前外网View在后。另外检查内网View的match-clients有没有包含你测试机的实际IP段网段写错一位就匹配不上。还有个隐蔽的坑如果你在options里配了allow-query或allow-recursionView里又配了一遍以View里的为准。别在options里配了allow-query { any; }就以为万事大吉View里的设置会覆盖它。5.3 递归查询被拒绝外网View里recursion no是故意的但内网用户如果查外网域名失败要检查内网View的recursion yes和allow-recursion是否包含了内网网段。另外forwarders要配可用的上游地址否则递归查询发不出去。测试递归是否正常查一个外部域名dig 192.168.10.11 www.example.com如果返回正常结果说明转发链路通。如果超时检查forwarders地址是否可达。5.4 区域文件语法错误导致服务起不来named-checkzone是你的好朋友改完区域文件先跑一遍。最常见的语法错误是括号不配对、每行结尾漏分号、SOA记录里的邮箱格式写错admin.lab.internal.末尾那个点不能少少了就变成相对域名了。还有TTL单位直接写数字是秒写1h这种带单位的在某些版本里不认稳妥起见统一用秒。5.5 日志在哪看BIND的日志默认可能进系统日志也可能单独配。排查时最直接的是journalctl -u named -f或者看/var/log/messages里grep named。想更精细地控制日志在named.conf里配logging块把category设成xfer-in、xfer-out、notify这些专门盯同步相关的事件。我调试主从同步时就把xfer-in和notify的日志级别调高每次传送的细节都看得清清楚楚。5.6 安全加固的几个必做项跑通之后别就这么放着几个安全项必须做。第一allow-transfer只放从服务器IP这个前面强调过了。第二外网View关递归防止被利用做放大攻击。第三如果服务器有公网IP考虑把version隐藏掉在options里加version not currently available;免得被人扫出BIND版本针对性攻击。第四主从之间的区域传送如果跨公网考虑用TSIG密钥做传送认证在allow-transfer里配key而不是单纯IP。TSIG配置大概是这样主从两边都要有相同的密钥key transfer-key { algorithm hmac-sha256; secret 你的base64密钥; }; // 主服务器zone里 allow-transfer { key transfer-key; }; // 从服务器zone里 masters { 192.168.10.11 key transfer-key; };密钥用tsig-keygen命令生成两边保持一致。这样即使IP被伪造没有密钥也拉不走区域数据。6. 几个让我少走弯路的实操心得主从加分离解析这套东西配通一次之后你会发现逻辑很清晰但第一次配的时候坑是真不少。我最大的体会是先把单视图的主从跑通再加View。一上来就主从加分离一起配出问题你根本分不清是同步的问题还是视图匹配的问题。分两步走每步验证通过再进下一步排查成本低得多。第二个心得是序列号管理要形成习惯。我现在改区域文件的流程固定是改记录、改Serial、named-checkzone、rndc reload、去从服务器确认。这五步走完才算改完少一步都可能留下隐患。尤其是团队协作时别人不知道你改了没递增序列号从服务器数据就是旧的这种问题排查起来特别费时间。第三个是测试分离解析一定要用真实来源IP。本机测试很容易因为源IP落在内网段而只测到内网视图外网视图到底对不对根本没验证。有条件就从不同网段的机器分别测没条件就用dig -b指定源地址别偷懒。最后说个扩展方向。这套主从加分离的架构往上可以加DNSSEC签名防篡改往旁边可以加DNS over TLS加密查询链路往深了可以做动态更新让DHCP分配的地址自动注册到DNS。但那些都是后话把主从同步和分离解析这两个地基打牢后面的扩展才有意义。我见过太多人一上来就追新特性结果基础的同步都没搞稳出了故障连日志都不会看。基础这东西慢就是快。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。