ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

CentOS 8源码编译安装BIND 9.18完整指南

CentOS 8源码编译安装BIND 9.18完整指南 1. 为什么放着现成的bind不装非要从源码编译先说结论如果你只是想在CentOS 8上搭一个能用、够用的DNS服务器yum install bind一行命令就搞定了这篇文章的很多内容你都用不上。但如果你遇到的是下面这些情况才真的需要走源码编译这条路。首先是版本太旧。CentOS 8发行版自带的bind版本停留在9.11.x这个版本在2022年就已经结束常规维护了只留了安全修复通道。但DNS领域的变化从来没停过比如新的攻击手法、EDNS Client Subnet的解析优化、对DoTDNS over TLS和DoHDNS over HTTPS的支持这些新特性如果你不去升级bind版本根本拿不到。企业内网做递归解析的对性能和安全的敏感度极高停留在一个EOL版本上说实话心里是不踏实的。其次是系统自带的bind想改编译参数很痛苦。rpm包在打包时固定了一组编译选项比如是否启用DNSSEC验证、是否带GeoIP支持、Listen端口绑定方式这些在编译阶段就定死了运行阶段你改不了。我遇到过需要把bind的max-cache-size调大、需要启用new-zones特性结果用系统自带包怎么调都调不到想要的最终效果最后只能回归源码编译。最后还有一个现实问题新版本的bind对OpenSSL、libuv这些基础库版本有明确要求CentOS 8系统库相对保守直接装新版bind的二进制包会出现so库版本不匹配的情况。而源码编译时绑定到系统已有的库上去构建反而能绕开一部分兼容性问题。所以我的判断标准很直接能用旧版凑合就凑合一旦业务对DNS解析的性能、安全特性或者特殊配置有硬性要求就该上源码编译这条路。这篇文章就是从零开始在CentOS 8上从源码编译安装新版bind9的完整记录。2. 编译前的地基依赖环境准备这一步偷懒后面全是坑2.1 CentOS 8 EOL之后先把源切对有一个很容易被忽略的前提CentOS 8在2024年5月已经正式EOL了。默认的mirrorlist.centos.org源会报404或者连不上如果你不处理这个问题后面连yum install都会失败更别提编译依赖了。这一步不说清楚很多新手会在装依赖时直接卡死。我当时用的是vault.centos.org的源。先把你系统的repo文件里的mirrorlist注释掉把baseurl改到vault源。具体操作是用sed批量替换。cd /etc/yum.repos.d/ sed -i s/mirrorlist/#mirrorlist/g *.repo sed -i s|#baseurlhttp://mirror.centos.org|baseurlhttp://vault.centos.org|g *.repo sed -i s|baseurlhttp://mirror.centos.org|baseurlhttp://vault.centos.org|g *.repo替换完以后先把缓存清掉再重新建立yum clean all yum makecache这里有个细节如果你用的CentOS版本是8.5.2111你的repo文件里写的是$releasever变量它会被替换成8而vault源还需要精确到小版本号。稳妥的做法是手动把repo文件里的$releasever改成8.5.2111或者直接用--releasever8.5.2111参数执行yum命令。2.2 编译工具的完整清单先装基础编译工具链yum install -y gcc gcc-c make cmake这就是新版bind和旧版在构建系统上的一个关键差异bind 9.16及更早的版本用的是Autotools体系执行./configure就行但是bind 9.18开始切换到CMake构建系统。所以如果你准备编译的是9.18或者更新版本cmake必须装上这和以前装bind源码的体验完全不一样。然后是bind核心依赖每一个都有明确的用途libuv-develbind 9.16以后用它替代了自研的事件处理库网络I/O、定时器、信号处理全走libuv。没有这个包你编译100%会报错。openssl-devel提供TLS和加密算法库。bind 9.18的DNS over TLS、DNSSEC验证都依赖它。不装的话configure阶段会警告禁用TLS。libcap-devel提供Linux Capabilities支持让bind在非root用户下也能绑定53端口。这个对生产环境非常重要。libxml2-devel和json-c-devel分别提供XML和JSON输出支持给rndc status、named-checkconf这些命令提供统计信息的序列化能力。pkg-configCMake在检测依赖时主要依赖pkg-config来查找库文件路径不装的话很多依赖检测不到。我建议一次性装全yum install -y libuv-devel openssl-devel libcap-devel libxml2-devel json-c-devel pkg-config顺便把文档生成时会用到的工具也装了比如python3-docutils和perl避免编译到一半因为docbook格式问题中断。2.3 小坑提醒依赖版本与链接方式CentOS 8自带的libuv版本是1.40.0左右而bind 9.18要求libuv 1.40.0刚好卡线没问题。但如果你用的是CentOS 8 Stream或者自己更新过部分库版本跨度可能会带来意想不到的问题。另外注意一点CentOS 8的OpenSSL默认是1.1.1而bind 9.20系列要求OpenSSL 1.1.1也能满足。如果哪天CentOS 8装的是OpenSSL 3.0以上的版本编译时很可能遇到OPENSSL_API_COMPAT相关的警告甚至报错碰到这种情况不要去改bind源码应该考虑在configure阶段关闭部分TLS特性或者降低OpenSSL版本。我在实际编译中没有遇到这个问题但是提前了解这些边界可以省掉很多查错时间。3. 源码获取与CMake配置阶段参数的坑全在这层3.1 从哪下载源码怎么确认校验值新版bind的源码可以从ISC官网下载也可以去GitHub的isc-projects/bind9仓库拉Release tag。推荐走官网下载页面因为会同时给出SHA256校验值安全上更稳。我编译时用的是bind 9.18.29这个版本属于9.18维护分支的后期版本修了一堆已知CVE稳定性也好。cd /usr/local/src wget https://downloads.isc.org/isc/bind9/9.18.29/bind-9.18.29.tar.xz wget https://downloads.isc.org/isc/bind9/9.18.29/bind-9.18.29.tar.xz.sha256 sha256sum -c bind-9.18.29.tar.xz.sha256校验输出出现OK字样后再解压tar -xf bind-9.18.29.tar.xz cd bind-9.18.293.2 CMake配置命令理解参数比抄命令更重要在bind 9.18及之后的新版中传统的./configure --prefixxxx变成了cmake -B build这种形式。它的配置过程可以通过cmake -L查看所有可选项也可以在源码根目录下的README.md里找到官方推荐配置。以下是我在实际编译时使用的配置方案cmake -B build \ -DCMAKE_INSTALL_PREFIX/usr/local/bind9 \ -DCMAKE_BUILD_TYPERelease \ -DBUILD_TESTINGoff \ -DENABLE_THREADSon \ -DENABLE_DNSTAPoff \ -DENABLE_AFLoff \ -DENABLE_LINUX_CAPABILITIESon \ -DENABLE_DEVELOPERoff \ -DENABLE_FUZZINGoff \ -DENABLE_LIBLZ4off \ -DENABLE_LIBZSTDoff \ -DENABLE_LIBUVon \ -DENABLE_OPENSSLon \ -DENABLE_LIBXML2on \ -DENABLE_JSON_Con这里为什么这样选我给你逐个拆解。CMAKE_INSTALL_PREFIX很好理解就是安装路径。我特意没用默认的/usr/local而是单独放到/usr/local/bind9下。这样做的核心原因是不和系统自带的bind rpm包冲突。如果你之前用yum装过bindrpm包里的二进制都在/usr/sbin/named、/etc/named.conf这些标准路径而你源码编译的版本单独放一个目录用哪个版本由你自己控制启停服务时不会互相干扰。DCMAKE_BUILD_TYPERelease会让编译器开优化选项线程模型默认启用这对bind的性能影响非常直接。ENABLE_LINUX_CAPABILITIESon是关键。bind的日常运行不应该用root直接跑必须降权到专用用户。但是这个CAP_NET_BIND_SERVICE权限要保留否则无法监听53端口。Linux capabilities机制允许进程降权后仍保留“绑定小于1024端口”的能力这就是这项配置的用处。生产环境务必开启。ENABLE_DNSTAP默认关闭因为启用后还需要额外装protobuf-c这样的依赖而且大部分场景用不上。你在排查DNS解析延迟问题时可以后续再编译一个带dnstap的版本没必要一开始就加上。ENABLE_LIBUV和ENABLE_OPENSSL、ENABLE_LIBXML2、ENABLE_JSON_C这四个是显式指定编译依赖避免CMake自动检测时漏掉或者找错库路径。在定制过库安装路径的系统上这些显式选项能减少很多配置阶段的不确定性。3.3 Make阶段可能出现的编译错误与处理CMake配置成功后执行cmake --build build -j$(nproc)这一步会编译一段时间9.18系列的代码量不小-j参数用来指定并行编译进程数按CPU核心数来。如果你的机器内存不大建议限制一下比如-j2否则编译过程中内存吃满可能直接被oom-killer干掉。编译过程中有可能会遇到下面几个典型报错我列出真实的处理经验。如果你看到Could NOT find LibUV或者Could NOT find OpenSSL这种错误先去yum list installed | grep libuv确认是否装了devel包。CentOS 8区分libuv和libuv-devel运行库和解压头的开发包是两回事。只装了运行库CMake是找不到头文件的。如果你看到与json-c相关的错误比如json_object_new_int64未定义引用那一般是因为系统安装的是旧版json-c。虽然CMake检测通过了但是链接阶段发现函数表对不上。这个问题的解决办法不是去换json-c版本而是在CMake配置里增加-DENABLE_JSON_Coffbind没有json-c也能正常编译运行损失的只是rndc输出JSON格式统计数据的能力。如果编译过程中报ISC_LANG_BEGIN_DECLS找不到等奇怪的宏错误通常不是bind源码的问题而是你下载的源码解压不完整或者系统时间不对导致make依赖判断错乱。重新校验tar包后再解压一次基本能解决。编译完成后当前路径下会生成build/bin/named/named这个二进制文件这个就是我们需要的核心程序。在正式安装之前建议先看一下它的动态链接依赖确认编译产物引用的so库都在系统里存在。ldd build/bin/named/named如果出现not found说明某个动态库没装或者路径不对。这种检查一小步能帮你省掉安装完以后服务起不来的大麻烦。4. 安装与基础配置编译装完工作才完成一半4.1 安装到目标路径编译完成后执行cmake --install build这一步会把named、rndc、dig、nslookup、delv、named-checkconf、named-checkzone等一系列二进制文件拷贝到/usr/local/bind9/bin和/usr/local/bind9/sbin下面。这里我建议把sbin目录做一个软链到/usr/local/sbin或者把它加进PATH环境变量不然每次执行named-checkconf都要写全路径时间久了很难受。ln -sf /usr/local/bind9/sbin/named /usr/local/sbin/named ln -sf /usr/local/bind9/sbin/rndc /usr/local/sbin/rndc ln -sf /usr/local/bind9/sbin/dig /usr/local/sbin/dig ln -sf /usr/local/bind9/sbin/named-checkconf /usr/local/sbin/named-checkconf ln -sf /usr/local/bind9/sbin/named-checkzone /usr/local/sbin/named-checkzone这里有个非常重要的点系统里如果之前用yum装过bind/usr/sbin/named可能已经存在。你软链到/usr/local/sbin之后要注意/usr/local/sbin在PATH中的位置是否在/usr/sbin之前。可以用which named确认一下实际指向。4.2 必须创建的运行用户与目录结构新编译的bind不会自动创建运行用户这一步需要手动做。安全上必须用非root用户运行named这是bind文档里强调过的要求。useradd -r -d /var/named -s /sbin/nologin named-r表示创建系统用户-d指定它的home目录-s /sbin/nologin防止有人用这个账户登录。然后创建bind运行所需的目录mkdir -p /var/named/chroot/var/named mkdir -p /var/named/chroot/var/run/named mkdir -p /var/named/chroot/etc mkdir -p /run/named chown -R named:named /var/named/chroot chown -R named:named /run/named如果你不打算启用chroot那么直接把工作目录放到/var/named下mkdir -p /var/named/zones mkdir -p /run/named chown -R named:named /var/named chown -R named:named /run/namedchroot是bind的老话题了把进程锁在固定目录里即使被攻击也只影响chroot目录内的文件。但是chroot在故障排查时会给你带来额外负担日志和文件路径都是双层结构。如果你的DNS服务器暴露在公网上建议启用chroot如果只是内网解析服务普通目录就够了。4.3 生成rndc密钥与最小可用配置bind通过rndc命令来管理服务它依赖一个预共享密钥。创建方式如下rndc-confgen -a -c /etc/rndc.key chown root:named /etc/rndc.key chmod 640 /etc/rndc.key如果不想用rndc-confgen -a生成单独文件也可以在named.conf里直接写入key块、controls块。两种方式在效果上一样但单独文件隔离性好升级重启时不容易弄丢密钥我推荐前者。然后写一个最简的named.confoptions { listen-on port 53 { any; }; listen-on-v6 port 53 { any; }; 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 { any; }; allow-recursion { 127.0.0.1; 10.0.0.0/8; }; dnssec-validation auto; }; zone . IN { type hint; file /var/named/named.ca; }; include /etc/rndc.key;这里把allow-query设为any允许所有客户端查询allow-recursion限制为本地回环和内部网段避免被开放递归放大攻击利用。dnssec-validation auto用系统默认的root信任锚做DNSSEC校验需要系统root key文件存在。然后需要下载root zone hint文件cd /var/named curl -o named.ca https://www.internic.net/domain/named.root chown named:named named.ca4.4 把bind交给systemd管理源码编译安装的一个好处是你可以完全控制服务的启动参数。但随之而来的问题是没有现成的systemd unit文件。我写一个最小可用的服务文件放到/etc/systemd/system/named.service[Unit] DescriptionBIND 9.18 DNS Server Afternetwork.target [Service] Typeforking PIDFile/run/named/named.pid ExecStart/usr/local/bind9/sbin/named -c /etc/named.conf -u named ExecReload/usr/local/bind9/sbin/rndc reload ExecStop/usr/local/bind9/sbin/rndc stop Restarton-failure [Install] WantedBymulti-user.target注意Typeforking表示named启动后会fork到后台运行PID文件路径要和named.conf里的pid-file设置一致否则systemd无法准确判断服务是否存活。在options里加一行pid-file /run/named/named.pid;确保路径匹配。写完以后加载并启动systemctl daemon-reload systemctl enable named systemctl start named启动以后用systemctl status named和ss -lunp | grep :53确认进程是否在监听。5. 排错实录编译和启动阶段最值得记录的坑5.1 53端口被系统自带dnsmasq或NetworkManager抢占CentOS 8桌面版或某些最小化安装上dnsmasq经常默认占用53端口NetworkManager的DNS代理也可能占用。启动named后如果发现Address already in use可以用ss -lunp | grep :53看输出里哪条进程占用了53端口。如果是dnsmasq直接systemctl stop dnsmasq并禁止开机启动如果是NetworkManager的dnsdnsmasq配置去/etc/NetworkManager/NetworkManager.conf里把dns行注释掉再重启NetworkManager。5.2 权限不足导致启动即失败如果启动时日志里出现permission denied或者using default key多半是目录权限或者capabilities没设置到位。我遇到过的真实情况是named报错无法创建/var/named/data/cache_dump.db最后发现是/var/named/data目录属主是root而不是named。调试这种问题最快的办法不是反复重启服务而是执行journalctl -u named -f看named标准输出和错误输出再配合named-checkconf检查配置。需要注意的是如果配置了linux-caps功能建议用setcap cap_net_bind_serviceep /usr/local/bind9/sbin/named给named加上绑定低端口的权限不然即便用named用户启动53端口还是无法绑定。这是我踩过一次的坑named用户下启动时bind端口绑定不了日志提示没有权限而我用root启动就正常浪费了两个小时才想到是capabilities的问题。5.3 chroot模式下找不到zone文件如果你按照网上教程配置了chroot需要清楚bind在chroot下看到的/etc/named.conf实际是宿主机上的/var/named/chroot/etc/named.conf而zone文件的路径则在/var/named/chroot/var/named/里。一个经典错误是明明把zone文件放进了/var/named/zones目录named 却报文件不存在。原因就是配置文件里写的是file /var/named/zones/example.zone;但chroot后它真实查找的路径是/var/named/chroot/var/named/zones/example.zone。你需要确认目录是否存在并且把zone文件拷贝到chroot目录里。如果你是新手我建议第一版先不要开chroot直接把DNS服务跑起来验证通了以后再做加固。否则chroot的路径问题会和配置问题混在一起排查难度直接翻倍。5.4 DNSSEC验证失败导致的解析异常还有一个比较隐蔽的问题如果配置了dnssec-validation auto但系统里没有root信任锚文件或者缓存了旧的DS记录你可能会发现某些域名可以解析某些域名迟迟不返回结果。排查方法是临时把配置改成dnssec-validation no;如果问题消失说明就是DNSSEC相关。此时可以执行dig . DNSKEY | grep -E DNSKEY|RRSIG确认root信任锚能正常获取或者用rndc flush清空缓存后重试。绝大多数情况是根信任锚获取失败而不是bind本身出问题。6. 生产环境下的进阶考量6.1 安全加固从裸奔到像样的配置跑通之后如果这台DNS要长期服役还需要做三件事。第一是allow-transfer。如果你的服务器不是权威DNS尽量设置allow-transfer { none; };避免别人恶意拉取区域数据。如果是主从结构这里应该限死为从服务器的IP。第二是配置ACL。在named.conf里定义acl internal { 10.0.0.0/8; 172.16.0.0/12; 192.168.0.0/16; 127.0.0.1; }; options { allow-query { internal; }; allow-recursion { internal; }; };这样能限制外部用户通过你的服务器做递归解析防止被用来做DDoS放大攻击。第三是日志配置。默认情况下bind把日志写到syslog排查问题非常难用。建议单独配置日志logging { channel default_log { file /var/named/log/named.log versions 3 size 5m; severity info; print-time yes; print-category yes; }; category default { default_log; }; category queries { default_log; }; };记住日志文件目录需要提前创建并chown给named用户否则bind会因为无法写入日志文件而拒绝启动。6.2 验证安装成功以及性能基准建议安装完成后做一轮功能验证dig 127.0.0.1 www.baidu.com如果拿到正确的A记录说明递归解析功能正常。然后测试正向反向解析可以用delv来验证DNSSEC用rndc status检查服务器运行状态。生产环境正式上线前建议用dnsperf这类工具做一次性能基准测试。我在内网环境里测过源码编译的9.18版本在并发解析场景下QPS每秒查询数明显比yum自带的9.11高出不少延迟也更稳定。这背后的原因一方面是libuv事件库的高并发处理能力另一方面是新版本对缓存和递归算法的优化这两项特性在编译后是天然生效的不需要额外配置。如果你所在环境的DNS请求量比较大这个收益值得关注。6.3 升级与卸载的注意事项源码编译安装的升级路径和yum包完全不同。以后从9.18升级到9.20系列时需要重新走一遍下载源码、CMake配置、编译、安装的流程。安装后检查一下/usr/local/bind9/etc下面的是否有新旧配置冲突尤其是named.conf里的语法、zone文件格式有没有变化。如果以后不想要这个源码编译版本了卸载也很简单直接删掉/usr/local/bind9整个目录、移除软链接、删除/etc/systemd/system/named.service和/etc/rndc.key最后systemctl daemon-reload即可。因为没有rpm包所以没有任何包管理器的残留记录这也是源码编译的一个实际优点。我在实际使用中还有一个习惯每次升级前用tar备份当前的/etc/named.conf和所有zone文件放到/root/bind_backup/下。源码编译的升级很简单但配置文件的兼容性问题才是真正容易出幺蛾子的地方。备份做了升级出错就能随时回滚。
返回列表