BIND主从+分离解析:构建高可用内网DNS架构实战
1. 项目缘起与整体设计思路很多中小型网络环境里DNS往往是最容易被忽视的一环。大家习惯性地把域名解析交给上游服务商或者随便找一台机器装个DNS软件就完事。直到某天内网服务大面积解析异常、外网访问时快时慢、运维排查发现解析记录被人误改才意识到DNS这块地基没打牢。我这次要聊的就是一套在真实内网环境中反复验证过的方案DNS主从服务器配合分离解析用一台主服务器、一台从服务器加上视图view机制把内网用户和外网用户的解析请求彻底分开处理。先说清楚这套方案到底解决什么问题。第一单点故障。只有一台DNS服务器时它挂了整个内网就瘫了主从架构让从服务器自动同步主服务器的区域数据主服务器宕机时从服务器顶上解析不中断。第二内外网解析混乱。同一个域名内网用户应该解析到内网地址走内网高速通道外网用户应该解析到公网地址如果混在一起要么内网流量绕到公网出口再回来要么外网用户拿到内网地址根本访问不了。第三管理效率。所有解析记录只在主服务器上维护从服务器自动同步不用两头改减少人为出错。这套方案适合谁我总结下来是三类人一是中小企业或园区的网络运维内网有一定规模但预算有限不想买商业DNS设备二是自己搭实验环境学习DNS原理的技术爱好者主从加分离解析是DNS进阶的必经之路三是需要给多个业务系统做域名隔离的开发者比如测试环境和生产环境用不同解析结果。不管你属于哪一类只要跟着把配置走一遍基本能掌握DNS服务里最核心的几块内容。方案选型上我用的软件是BINDBerkeley Internet Name Domain这是目前最成熟、文档最全的开源DNS服务实现几乎所有Linux发行版都能直接装。为什么不用dnsmasq或者CoreDNSdnsmasq更适合小型转发场景对主从同步和视图分离的支持比较弱CoreDNS插件化设计很灵活但配置视图分离需要额外插件学习成本反而更高。BIND原生支持主从复制zone transfer和视图view配置直观排错资料多是这类需求的首选。整体架构是这样主服务器假设内网地址为192.168.10.11负责维护所有区域文件包括内网视图和外网视图两套解析数据从服务器192.168.10.12通过区域传送从主服务器同步数据同样配置两套视图。客户端根据来源IP被划分到不同视图内网网段走内网视图其他来源走外网视图。主从之间通过TSIG密钥做传输认证防止未授权的区域传送。下面我按实际搭建顺序把每一步的原理、参数和踩坑点都拆开讲。2. 核心细节解析与实操要点2.1 主从复制的工作机制与关键参数主从复制的核心是区域传送Zone Transfer。主服务器上的区域文件发生变化时会通过NOTIFY消息通知从服务器从服务器收到后发起传送请求把最新的区域数据拉过来。传送方式有两种AXFR全量传送和IXFR增量传送。AXFR每次把整个区域文件传一遍数据量大时开销明显IXFR只传变化的部分效率高但需要主服务器保留变化日志。实际配置中我一般让BIND自动选择主服务器开启IXFR支持从服务器优先请求增量失败时回退全量。关键参数在named.conf里主服务器的区域定义中要写type master从服务器写type slave并用masters指定主服务器地址。还有一个容易被忽略的参数是allow-transfer必须限制只允许从服务器IP发起传送否则任何人都能把你的区域文件拉走等于把内网拓扑暴露了。我见过不少配置直接写allow-transfer { any; };这是典型的危险操作。序列号Serial是主从同步的触发依据。每次修改区域文件后必须手动递增Serial值从服务器才会认为数据有更新。Serial通常用日期加序号的形式比如2025010101表示2025年1月1日第1次修改。这里有个坑如果你改了记录但忘了改Serial从服务器不会同步排查时容易误以为是网络问题。我的习惯是每次编辑区域文件前先改Serial形成肌肉记忆。2.2 分离解析的视图机制与匹配逻辑分离解析靠的是BIND的**视图view**功能。一个视图就是一套独立的解析策略包含自己的区域文件和选项。客户端请求到达时BIND按named.conf中视图定义的顺序逐个匹配match-clients第一个匹配上的视图生效。所以视图的顺序很重要通常把范围小的内网视图放前面范围大的外网视图放后面。match-clients支持IP网段、ACL访问控制列表和密钥。我一般先定义一个ACL把内网网段写进去视图里引用这个ACL这样管理起来清晰。比如acl internal-net { 192.168.10.0/24; 192.168.20.0/24; 127.0.0.1; };然后内网视图写match-clients { internal-net; };外网视图写match-clients { any; };。注意any必须放在最后否则它会匹配所有请求后面的视图永远轮不到。每个视图里都要完整定义根区域hint、本地回环解析和需要解析的域名区域。这里有个常见错误只在内网视图里定义域名区域外网视图忘了定义结果外网用户解析该域名时得到REFUSED。视图之间是隔离的不会互相继承每个视图都得写全。2.3 区域文件编写规范与记录类型区域文件zone file的语法看着简单但细节很多。开头必须是$TTL定义默认生存时间然后SOA记录接着NS记录最后才是各种A、CNAME、MX等记录。SOA记录里的参数直接影响从服务器同步行为Serial序列号前面说过每次修改必须递增。Refresh从服务器多久检查一次主服务器是否有更新我设的是3600秒1小时。Retry同步失败后多久重试设600秒10分钟。Expire从服务器联系不上主服务器时多久后停止对外提供该区域解析设604800秒7天。Minimum否定缓存时间设86400秒1天。这些值不是随便填的。Refresh太短会增加主服务器负担太长会导致更新延迟Expire太短主从网络短暂中断就会导致从服务器停止解析太长又可能提供过期数据。我实测下来中小企业环境用上面这组值比较均衡。记录类型里A记录是IPv4地址AAAA是IPv6CNAME是别名MX是邮件交换PTR是反向解析。分离解析场景下同一个主机名在内网视图和外网视图的A记录值不同这是核心。比如app.example.com在内网视图解析到192.168.10.100在外网视图解析到203.0.113.50。注意CNAME记录不能和MX记录共存于同一名称这是DNS协议限制踩过一次坑排查了半天。2.4 TSIG密钥与传输安全主从之间的区域传送如果不加密不认证任何知道主服务器IP的人都能请求传送。TSIGTransaction Signature用共享密钥对传送消息做签名主从双方配置相同密钥验证通过才允许传送。生成密钥用tsig-keygen命令tsig-keygen -a HMAC-SHA256 transfer-key输出会包含密钥名称和密钥值把这段配置分别放到主从服务器的named.conf里。主服务器的allow-transfer写{ key transfer-key; };从服务器的masters写{ 192.168.10.11 key transfer-key; };。这样即使有人伪造从服务器IP没有密钥也传不走数据。密钥算法选HMAC-SHA256比HMAC-MD5更安全现代BIND版本都支持。密钥值要保密配置文件权限设成640属主是named用户。我见过把密钥写在公开文档里的等于没设防。3. 实操过程与核心环节实现3.1 环境准备与软件安装我用的系统是常见的Linux发行版两台机器主服务器192.168.10.11从服务器192.168.10.12。系统最小化安装后先更新软件源然后安装BIND# 主从服务器都执行 sudo apt update sudo apt install bind9 bind9utils bind9-doc -y安装完成后BIND服务默认可能已经启动先停掉等配置改完再启动sudo systemctl stop named sudo systemctl disable named配置文件在/etc/bind/目录下主要文件有named.conf主配置、named.conf.options全局选项、named.conf.local本地区域定义、named.conf.default-zones默认区域。我习惯把自定义配置写在named.conf.local里全局选项改named.conf.options这样升级时不容易被覆盖。防火墙方面DNS主要用53端口TCP和UDP都要放行。主从之间还需要TCP 53做区域传送。如果开了防火墙记得加规则sudo ufw allow 53/tcp sudo ufw allow 53/udp sudo ufw allow from 192.168.10.12 to any port 533.2 主服务器全局选项配置先改named.conf.options重点是监听地址、允许查询范围和转发器。监听地址写主服务器自己的IP和回环options { directory /var/cache/bind; listen-on port 53 { 127.0.0.1; 192.168.10.11; }; listen-on-v6 { none; }; allow-query { any; }; recursion yes; forwarders { 8.8.8.8; 114.114.114.114; }; dnssec-validation auto; auth-nxdomain no; };这里allow-query设any是因为要同时服务内外网用户具体哪些请求走哪个视图由视图的match-clients控制。forwarders是转发器本地解析不了的域名转发给上游。recursion yes允许递归查询内网客户端需要这个。dnssec-validation auto开启DNSSEC验证增强安全性。注意如果服务器只做权威解析不做递归recursion要设noallow-query也要收紧。我这里因为要服务内网客户端所以开了递归。3.3 定义ACL与TSIG密钥在named.conf开头或单独的named.conf.local里定义ACL和密钥acl internal-net { 192.168.10.0/24; 192.168.20.0/24; 127.0.0.1; }; key transfer-key { algorithm hmac-sha256; secret 你生成的密钥值; };密钥值用tsig-keygen生成后粘贴进来。ACL里把内网网段和回环都写上回环地址用于本机测试。3.4 主服务器视图与区域配置接下来是核心部分在named.conf.local里定义两个视图。先写内网视图view internal { match-clients { internal-net; }; recursion yes; zone . { type hint; file /usr/share/dns/root.hints; }; zone localhost { type master; file /etc/bind/db.local; }; zone 10.168.192.in-addr.arpa { type master; file /etc/bind/db.192.168.10; }; zone example.com { type master; file /etc/bind/db.example.com.internal; allow-transfer { key transfer-key; }; also-notify { 192.168.10.12; }; }; };再写外网视图view external { match-clients { any; }; recursion no; zone example.com { type master; file /etc/bind/db.example.com.external; allow-transfer { key transfer-key; }; also-notify { 192.168.10.12; }; }; };几个关键点解释一下。内网视图开了递归因为内网客户端需要解析外部域名外网视图关了递归只做权威解析防止被利用做DNS放大攻击。also-notify让主服务器在区域变化时主动通知从服务器加快同步速度。反向解析区域10.168.192.in-addr.arpa用于内网IP反查主机名外网视图里不需要。3.5 区域文件编写实例内网视图的区域文件db.example.com.internal$TTL 86400 IN SOA ns1.example.com. admin.example.com. ( 2025010101 ; Serial 3600 ; Refresh 600 ; Retry 604800 ; Expire 86400 ; Minimum TTL ) IN NS ns1.example.com. IN NS ns2.example.com. ns1 IN A 192.168.10.11 ns2 IN A 192.168.10.12 app IN A 192.168.10.100 db IN A 192.168.10.101 www IN CNAME app外网视图的区域文件db.example.com.external$TTL 86400 IN SOA ns1.example.com. admin.example.com. ( 2025010101 ; Serial 3600 ; Refresh 600 ; Retry 604800 ; Expire 86400 ; Minimum TTL ) IN NS ns1.example.com. IN NS ns2.example.com. ns1 IN A 203.0.113.10 ns2 IN A 203.0.113.11 app IN A 203.0.113.50 www IN CNAME app注意两个文件的Serial要一致从服务器同步时按Serial判断是否需要更新。内网app解析到192.168.10.100外网解析到203.0.113.50这就是分离解析的效果。3.6 从服务器配置与同步验证从服务器的named.conf.options和主服务器类似监听地址改成自己的IP。视图配置里区域类型改成type slavemasters指向主服务器并带上密钥view internal { match-clients { internal-net; }; recursion yes; zone example.com { type slave; file /var/cache/bind/db.example.com.internal; masters { 192.168.10.11 key transfer-key; }; }; };外网视图同理。从服务器的区域文件不用手动创建BIND同步后会自动生成到file指定的路径。配置完成后先启动主服务器再启动从服务器sudo systemctl start named sudo systemctl enable named验证同步是否成功在从服务器上查日志sudo tail -f /var/log/syslog | grep named看到transfer of example.com/IN/internal from 192.168.10.11#53: Transfer completed就说明同步成功。也可以用dig命令测试dig 192.168.10.12 app.example.com如果返回的地址和主服务器一致说明从服务器工作正常。3.7 分离解析效果验证验证分离解析需要从不同来源IP发起查询。内网机器上执行dig 192.168.10.11 app.example.com应该返回192.168.10.100。外网机器或模拟外网来源执行同样命令应该返回203.0.113.50。如果结果不对检查视图的match-clients顺序和ACL定义。我踩过一次坑ACL里写了192.168.10.0/24但测试机IP是192.168.10.200按理说应该匹配结果却走了外网视图。排查发现是named.conf里视图顺序写反了外网视图的any放在了前面所有请求都被它截胡了。调整顺序后正常。4. 常见问题与排查技巧实录4.1 主从同步失败排查速查表主从同步出问题是这类配置里最高频的故障。我把常见现象、原因和解决方法整理成表方便对照排查现象可能原因排查方法解决从服务器日志无传送记录主服务器未通知或网络不通主服务器also-notify是否配从IPtelnet 主IP 53测试连通性补配also-notify放行防火墙传送被拒绝TSIG密钥不匹配或allow-transfer未放行主服务器日志看transfer of ... denied核对两端密钥名称和值检查allow-transfer同步后记录仍旧Serial未递增对比主从区域文件Serial值修改主服务器区域文件后递增Serial从服务器区域文件为空文件路径权限问题检查/var/cache/bind目录属主和权限chown bind:bind并确保可写同步延迟大Refresh值过大或NOTIFY未送达查看主服务器also-notify和从服务器Refresh值调小Refresh确认NOTIFY端口53可达这张表覆盖了我遇到过的八成同步问题。特别提醒修改主服务器区域文件后一定要rndc reload或重启named让配置生效光改文件不重载等于没改。4.2 分离解析不生效的典型场景分离解析不生效表现是内外网用户拿到相同解析结果。除了前面说的视图顺序问题还有几个隐蔽原因。一是match-clients里用了localhost而不是127.0.0.1某些BIND版本对localhost的解析依赖系统hosts文件可能匹配不上。二是ACL定义在视图之后BIND解析配置时ACL必须先定义后引用顺序错了会报语法错误或静默失效。三是客户端走了缓存之前查询的结果被本地或中间DNS缓存了改配置后要清缓存再测。我一般用dig norecurse直接问权威服务器绕过缓存干扰。还有一个容易忽略的点如果内网客户端配置的DNS服务器地址是主服务器的外网IP那它的请求来源会被识别为外网走外网视图。所以内网客户端的DNS设置必须指向内网IP。这个在跨网段环境里尤其要注意。4.3 性能与安全加固经验DNS服务跑起来容易跑得稳、跑得安全需要额外功夫。性能方面BIND默认的缓存大小可能不够可以在named.conf.options里加max-cache-size 256M;根据内存调整。查询日志默认不开排查问题时可以临时开querylog但生产环境长期开着会吃磁盘记得关。安全方面除了TSIG和allow-transfer限制还要注意关闭版本信息泄露在options里加version not currently available;防止有人通过dig chaos txt version.bind探测BIND版本限制递归查询来源外网视图已经关了递归内网视图的allow-recursion可以进一步收紧到内网ACL定期检查区域文件权限确保只有named用户可读。提示rndc是BIND的管理工具可以不用重启就重载配置、查看状态。配置rndc.key后用rndc reload重载区域rndc status看运行状态比systemctl restart温和得多生产环境推荐用这个。4.4 日常维护与记录管理心得主从加分离解析的环境日常维护主要是改记录和看同步。改记录的标准流程是先改主服务器区域文件递增Serialrndc reload然后等从服务器自动同步最后在从服务器上dig验证。不要直接改从服务器的区域文件因为下次同步会被覆盖。记录管理上我建议按业务或部门分区域文件不要所有记录堆在一个文件里。比如db.example.com.internal里再按$INCLUDE拆分子文件或者用子域名委派。这样改一处不影响其他部分也方便多人协作时减少冲突。Serial的递增我习惯用日期两位序号比如2025010101、2025010102一天内改多次就递增后两位跨天重置为01。这样一眼能看出最后修改时间。还有个小技巧主从服务器的系统时间要同步用NTP服务对齐。时间差太大可能导致TSIG签名验证失败因为签名带时间戳。我遇到过从服务器时间慢了半小时传送一直失败查了半天才发现是时间问题。现在新环境部署第一步就是配NTP。这套配置我在几个不同规模的环境里跑过从十几台机器的小内网到上百台的中型网络稳定性没问题。关键是把视图顺序、Serial递增、TSIG密钥这三件事做对剩下的就是日常维护的细活了。后面如果业务扩展还可以在主从基础上加第二台从服务器做负载分担或者用allow-update配合密钥做动态更新那是另一个话题了。