dns主从服务配置
主服务器shell脚本
#!/bin/bash set -euo pipefail #============configuration parameters=========== MASTER_IP="192.168.153.131" DOMAIN="web.com" REV_ZONE="153.168.192.in-addr.arpa" SLAVE_IP="192.168.153.132" #=============tool parameters============= info(){ echo -e "\033[32m[info] $1\033[0m"; } warn() { echo -e "\033[33m[WARN] $1\033[0m"; } error() { echo -e "\033[31m[ERROR] $1\033[0m"; exit 1; } #dynamic cursor spinner(){ local pid=$1 local msg=$2 local spin="\|/-" local i=0 while kill -0 $pid 2>/dev/null; do i=$(( (i+1) %4 )) echo -ne "\r[${spin:$i:1}] $msg" sleep 0.1 done echo -ne "\r[√] $msg\n" } #=============main============= clear info "=============== DNS primary server configuration script ===============" info "主服务器IP: $MASTER_IP" info "域名: $DOMAIN" info "反向区域: $REV_ZONE" info "从服务器IP: $SLAVE_IP" info "===================================================" #check if the user is the root user if [ "$(id -u)" -ne 0 ]; then error "please run the script as the root user(sudo -i 或 su -)" fi # 1. install bind package info "step1/6:Installing bind package..." dnf install -y bind bind-utils &>/dev/null || error "Failed to install bind package. Check your repository." # 2. Configure named.conf info "step2/6:Configuring /etc/named.conf..." cat > /etc/named.conf << EOF options { listen-on port 53 { $MASTER_IP; 127.0.0.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"; allow-query { any; }; allow-transfer { $SLAVE_IP; }; dnssec-validation no; recursion no; }; # forward resolution zone zone "$DOMAIN" IN { type master; file "$DOMAIN.zone"; }; # reverse resolution zone zone "$REV_ZONE" IN { type master; file "$REV_ZONE.zone"; }; EOF # 立即设置 named.conf 的权限 info "Setting permissions for /etc/named.conf..." chown root:named /etc/named.conf || error "Failed to change owner of /etc/named.conf" chmod 640 /etc/named.conf || error "Failed to change permissions of /etc/named.conf" # 3. Configure forward zone file info "step3/6:Configuring forward zone file ($DOMAIN.zone)..." cat > /var/named/$DOMAIN.zone << EOF \$TTL 1D @ IN SOA ns1.$DOMAIN. admin.$DOMAIN. ( $(date +%Y%m%d%H) 1D 1H 1W 3H ) NS ns1.$DOMAIN. NS ns2.$DOMAIN. ns1 A $MASTER_IP ns2 A $SLAVE_IP www A $MASTER_IP EOF # 4. Configure reverse zone file info "step4/6:Configuring reverse zone file ($REV_ZONE.zone)..." MASTER_IP_LAST=$(echo $MASTER_IP | awk -F'.' '{print $4}') cat > /var/named/$REV_ZONE.zone << EOF \$TTL 1D @ IN SOA ns1.$DOMAIN. admin.$DOMAIN. ( $(date +%Y%m%d%H) 1D 1H 1W 3H ) NS ns1.$DOMAIN. NS ns2.$DOMAIN. $MASTER_IP_LAST PTR ns1.$DOMAIN. $MASTER_IP_LAST PTR www.$DOMAIN. EOF # 立即设置区域文件的权限 info "Setting permissions for zone files..." chown root:named /var/named/$DOMAIN.zone /var/named/$REV_ZONE.zone || error "Failed to change owner of zone files" chmod 640 /var/named/$DOMAIN.zone /var/named/$REV_ZONE.zone || error "Failed to change permissions of zone files" # 5. Final permission and SELinux configuration info "step5/6:Finalizing permissions and SELinux..." # 强制确保 /var/named 目录的权限和上下文正确 info "Securing /var/named directory..." chown root:named /var/named || error "Failed to change owner of /var/named" chmod 770 /var/named || error "Failed to change permissions of /var/named" # 配置防火墙 info "Configuring firewall..." (firewall-cmd --add-port=53/tcp --add-port=53/udp --permanent && firewall-cmd --reload) &>/dev/null & spinner $! "Firewall configuration in progress..." # 配置SELinux if [ "$(getenforce)" = "Enforcing" ]; then info "Configuring SELinux..." (setsebool -P named_write_master_zones on && setsebool -P named_read_master_zones on) &>/dev/null & spinner $! "SELinux booleans in progress..." info "Restoring SELinux contexts..." (restorecon -Rv /var/named && restorecon /etc/named.conf) &>/dev/null & spinner $! "SELinux context restoration in progress..." fi # 6. Start and verify the named service info "step6/6:Starting named service..." systemctl enable --now named &>/dev/null || error "Failed to start named service. Check logs with: journalctl -u named" # 验证服务状态 if systemctl is-active --quiet named; then info "The named service started successfully!" else error "The named service failed to start. Please check the log: journalctl -u named" fi # 最终提示 info "===================================================" info "Master DNS server configuration completed successfully!" info "Verification command: dig @$MASTER_IP www.$DOMAIN" info "Slave server configuration: Please run the setup_dns_slave.sh script on the slave server." info "==================================================="从服务器shell脚本
#!/bin/bash set -euo pipefail # 开启严格模式,出错立即退出 # ==================== 配置参数(根据实际环境修改)==================== SLAVE_IP="192.168.153.132" # 从服务器IP MASTER_IP="192.168.153.131" # 主服务器IP(用于同步区域文件) DOMAIN="web.com" # 要解析的域名(与主服务器一致) REV_ZONE="153.168.192.in-addr.arpa" # 反向解析区域(与主服务器一致) # ================================================================== # ==================== 工具函数 ==================== info() { echo -e "\033[32m[INFO] $1\033[0m" } warn() { echo -e "\033[33m[WARN] $1\033[0m" } error() { echo -e "\033[31m[ERROR] $1\033[0m" exit 1 } spinner() { local pid=$1 local msg=$2 local spin='\|/-' local i=0 while kill -0 $pid 2>/dev/null; do i=$(( (i+1) %4 )) echo -ne "\r[${spin:$i:1}] $msg" sleep 0.1 done echo -ne "\r[√] $msg\n" } # ================================================== # ==================== 主逻辑 ==================== clear info "=============== DNS 从服务器配置脚本 ===============" info "从服务器IP: $SLAVE_IP" info "主服务器IP: $MASTER_IP" info "域名: $DOMAIN" info "反向区域: $REV_ZONE" info "===================================================" # 1. 检查是否为root用户 if [ "$(id -u)" -ne 0 ]; then error "请以root用户执行脚本(sudo -i 或 su -)" fi # 2. 安装BIND软件 info "步骤1/6:安装BIND软件..." dnf install -y bind bind-utils &>/dev/null & spinner $! "正在安装bind包..." # 3. 配置named.conf info "步骤2/6:配置named.conf..." cat > /etc/named.conf << EOF options { listen-on port 53 { $SLAVE_IP; 127.0.0.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"; allow-query { any; }; dnssec-validation no; recursion no; }; # 正向解析区域(从服务器) zone "$DOMAIN" IN { type slave; masters { $MASTER_IP; }; file "slaves/$DOMAIN.zone"; }; # 反向解析区域(从服务器) zone "$REV_ZONE" IN { type slave; masters { $MASTER_IP; }; file "slaves/$REV_ZONE.zone"; }; EOF # 立即设置 named.conf 的权限 info "步骤3/6:调整文件和目录权限..." chown root:named /etc/named.conf || error "Failed to change owner of /etc/named.conf" chmod 640 /etc/named.conf || error "Failed to change permissions of /etc/named.conf" # 确保 /var/named 和 slaves 目录权限正确 chown root:named /var/named || error "Failed to change owner of /var/named" chmod 770 /var/named || error "Failed to change permissions of /var/named" # 检查named.conf语法 named-checkconf /etc/named.conf || error "named.conf配置语法错误,请检查脚本配置参数" # 4. 配置防火墙和SELinux info "步骤4/6:配置防火墙和SELinux..." # 配置防火墙,开放53端口 (firewall-cmd --add-port=53/tcp --add-port=53/udp --permanent && firewall-cmd --reload) &>/dev/null & spinner $! "正在配置防火墙..." # 处理SELinux(若开启) if [ "$(getenforce)" = "Enforcing" ]; then info "正在配置SELinux..." (setsebool -P named_write_slave_zones on && setsebool -P named_read_slave_zones on) &>/dev/null & spinner $! "正在设置SELinux布尔值..." info "正在恢复SELinux上下文..." (restorecon -Rv /var/named && restorecon /etc/named.conf) &>/dev/null & spinner $! "正在恢复文件SELinux上下文..." fi # 5. 启动named服务 info "步骤5/6:启动named服务..." systemctl enable --now named &>/dev/null || error "named服务启动失败,请检查日志:journalctl -u named" # 验证服务状态 if systemctl is-active --quiet named; then info "named服务启动成功!" else error "named服务启动失败,状态异常" fi # 6. 等待并验证区域文件同步 info "步骤6/6:等待从主服务器同步区域文件..." SYNC_TIMEOUT=20 SYNC_INTERVAL=2 SYNC_COUNT=0 while [ $SYNC_COUNT -lt $((SYNC_TIMEOUT / SYNC_INTERVAL)) ]; do if [ -f "/var/named/slaves/$DOMAIN.zone" ] && [ -f "/var/named/slaves/$REV_ZONE.zone" ]; then info "区域文件同步成功!" break fi SYNC_COUNT=$((SYNC_COUNT + 1)) echo -ne "\r[INFO] 正在等待同步...(${SYNC_COUNT}s/${SYNC_TIMEOUT}s)" sleep $SYNC_INTERVAL done echo -ne "\r" # 清除等待行 # 同步失败处理 if [ $SYNC_COUNT -ge $((SYNC_TIMEOUT / SYNC_INTERVAL)) ]; then warn "区域文件同步超时!请检查:" warn "1. 主从服务器网络连通性(ping $MASTER_IP)" warn "2. 主服务器named服务是否正常运行" warn "3. 主服务器named.conf中allow-transfer是否包含从服务器IP($SLAVE_IP)" warn "4. 查看从服务器日志:journalctl -u named | grep -i 'transfer'" fi # 最终提示 info "===================================================" info "从服务器配置完成!" info "验证命令(查询从服务器):dig @$SLAVE_IP www.$DOMAIN" info "高可用测试:停止主服务器named服务(systemctl stop named),再查询从服务器验证解析" info "==================================================="DNS 主从服务器自动化配置脚本笔记
一、脚本概述
1. 核心用途
基于CentOS/RHEL 8+系统,自动化部署BIND(DNS)主从架构,实现域名正向 / 反向解析及高可用:
- 主服务器:配置正向 / 反向解析区域、开放从服务器同步权限、配置防火墙 / SELinux
- 从服务器:同步主服务器区域文件、提供备用解析服务、主服务器故障时自动接管
2. 适用环境
- 操作系统:CentOS 8+/RHEL 8+(依赖
dnf包管理器、firewalld、SELinux) - 网络要求:主从服务器网络互通(TCP/UDP 53 端口开放)、root 权限执行
- 软件依赖:
bind(DNS 服务)、bind-utils(DNS 测试工具,如dig)
3. 架构核心逻辑
主服务器(192.168.153.131) 从服务器(192.168.153.132) ├─ 配置正向区域(web.com.zone) ├─ 配置slave类型区域 ├─ 配置反向区域(153.168.192.zone) ├─ 从主服务器同步区域文件 ├─ 允许从服务器同步(allow-transfer) ├─ 提供查询服务(与主服务器解析结果一致) └─ 监听53端口接收查询/同步请求 └─ 主服务器故障时自动响应客户端查询二、脚本结构解析
两个脚本均遵循「配置参数→工具函数→主逻辑」的结构,核心模块如下:
1. 通用模块(主从脚本共用)
| 模块 | 功能说明 |
|---|---|
| 配置参数 | 集中定义 IP、域名、反向区域等核心变量(如MASTER_IP、DOMAIN),便于修改 |
| 工具函数 | info/warn/error:日志输出(带颜色区分);spinner:动态进度条(提升体验) |
| 权限检查 | 强制要求 root 用户执行(id -u != 0则退出) |
| 服务验证 | 启动named后检查服务状态(systemctl is-active) |
2. 主服务器脚本核心逻辑(6 步)
| 步骤 | 关键操作 |
|---|---|
| 安装依赖 | 用dnf安装bind、bind-utils |
配置named.conf | 监听主服务器 IP、允许从服务器同步、关闭 DNSSEC / 递归 |
| 正向区域文件 | 创建web.com.zone,定义ns1/ns2解析、www记录 |
| 反向区域文件 | 创建153.168.192.in-addr.arpa.zone,定义PTR反向解析记录 |
| 权限 / 安全配置 | 修正named.conf、区域文件、/var/named目录权限;配置防火墙 / SELinux |
| 启动验证 | 启用并启动named,输出验证命令(dig @主IP www.web.com) |
3. 从服务器脚本核心逻辑(6 步)
| 步骤 | 关键操作 |
|---|---|
| 安装依赖 | 与主服务器一致,安装bind、bind-utils |
配置named.conf | 监听从服务器 IP、定义slave类型区域、指向主服务器 IP |
| 权限调整 | 修正named.conf、/var/named目录权限(确保同步文件可写) |
| 安全配置 | 开放防火墙 53 端口、配置 SELinux 允许从服务器同步 |
| 启动服务 | 启用并启动named,自动向主服务器请求区域同步 |
| 同步验证 | 等待 20 秒检查同步文件是否存在,超时输出排查建议 |
三、使用说明
1. 执行前提
- 主从服务器均安装
CentOS 8+/RHEL 8+,且网络互通(ping测试) - 主从服务器均配置好
yum/dnf源(确保能安装bind) - 执行用户为
root(或通过sudo -i切换)
2. 执行步骤
- 主服务器执行:
# 上传主服务器脚本(如 setup_dns_master.sh) chmod +x setup_dns_master.sh ./setup_dns_master.sh - 从服务器执行:
# 上传从服务器脚本(如 setup_dns_slave.sh) chmod +x setup_dns_slave.sh ./setup_dns_slave.sh
3. 验证方法
| 验证场景 | 命令 |
|---|---|
| 主服务器正向解析 | dig @192.168.153.131 www.web.com(返回192.168.153.131) |
| 主服务器反向解析 | dig @192.168.153.131 -x 192.168.153.131(返回ns1.web.com) |
| 从服务器同步验证 | dig @192.168.153.132 www.web.com(返回与主服务器一致结果) |
| 高可用测试 | 主服务器执行systemctl stop named,再查询从服务器(解析正常) |
四、核心改进建
1. 兼容性优化(解决跨版本 / 环境问题)
问题
当前脚本仅支持CentOS 8+(依赖dnf),不兼容CentOS 7(yum包管理器);未检查firewalld/bind依赖是否已安装。
解决方案
- 增加 OS 版本检测,自动适配
yum/dnf:# 在脚本开头添加OS检测 detect_package_manager() { if [ -f /etc/redhat-release ]; then if grep -q "CentOS Linux 7" /etc/redhat-release; then PKG_MGR="yum" else PKG_MGR="dnf" fi else error "不支持的操作系统" fi } # 安装依赖时使用 $PKG_MGR $PKG_MGR install -y bind bind-utils &>/dev/null & - 检查
firewalld是否安装,未安装则自动安装:# 防火墙配置前添加检查 if ! command -v firewall-cmd &>/dev/null; then info "firewalld未安装,正在安装..." $PKG_MGR install -y firewalld &>/dev/null & spinner $! "firewalld安装中..." systemctl enable --now firewalld &>/dev/null fi
2. 错误处理增强(降低排查难度)
问题
当前脚本通过&>/dev/null屏蔽了大部分操作的详细输出,报错时仅提示 “失败”,无法定位具体原因(如区域文件语法错误、权限修改失败)。
解决方案
- 保留关键操作的错误输出,如
named-checkzone/named-checkconf:# 原代码(屏蔽输出) named-checkzone $DOMAIN /var/named/$DOMAIN.zone || error "正向区域文件语法错误" # 改进后(显示详细错误) if ! named-checkzone $DOMAIN /var/named/$DOMAIN.zone; then error "正向区域文件语法错误,详细信息:$(named-checkzone $DOMAIN /var/named/$DOMAIN.zone 2>&1)" fi - 检查后台进程退出码,
spinner显示失败状态:# 改进spinner函数,支持失败标识 spinner(){ local pid=$1 local msg=$2 local spin="\|/-" local i=0 while kill -0 $pid 2>/dev/null; do i=$(( (i+1) %4 )) echo -ne "\r[${spin:$i:1}] $msg" sleep 0.1 done # 检查进程退出码 wait $pid if [ $? -eq 0 ]; then echo -ne "\r[√] $msg\n" else echo -ne "\r[×] $msg(失败)\n" error "$msg 执行失败,请查看日志" fi }
3. 幂等性优化(支持重复执行)
问题
当前脚本用cat >覆盖式创建文件(如named.conf、区域文件),重复执行会覆盖原有配置;Serial号($(date +%Y%m%d%H))可能因同一小时内重复执行导致同步失败。
解决方案
- 先备份原有文件,再创建新配置:
# 配置named.conf前备份 if [ -f /etc/named.conf ]; then cp /etc/named.conf /etc/named.conf.bak.$(date +%Y%m%d%H%M%S) &>/dev/null info "已备份原有named.conf到 /etc/named.conf.bak.$(date +%Y%m%d%H%M%S)" fi - 自动递增
Serial号(避免重复):# 主服务器正向区域文件Serial号改进 # 读取现有Serial号,无则用当前时间,有则+1 CURRENT_SERIAL=$(date +%Y%m%d%H) if [ -f /var/named/$DOMAIN.zone ]; then EXIST_SERIAL=$(grep -E '^[[:space:]]+[0-9]{10,}' /var/named/$DOMAIN.zone | awk '{print $1}') if [ -n "$EXIST_SERIAL" ] && [ "$EXIST_SERIAL" -ge "$CURRENT_SERIAL" ]; then CURRENT_SERIAL=$((EXIST_SERIAL + 1)) fi fi # 区域文件中替换为 $CURRENT_SERIAL
4. 安全性增强(适配生产环境)
问题
allow-query {any;}开放所有客户端查询,生产环境存在安全风险- 防火墙开放 53 端口给所有主机,未限制合法查询网段
- SELinux 配置过于宽泛(
setsebool named_write_master_zones on)
解决方案
- 限制允许查询的网段(如公司内网
192.168.153.0/24):# named.conf 中修改 allow-query allow-query { 192.168.153.0/24; 127.0.0.1; }; - 防火墙规则限制网段:
# 原代码(开放给any) firewall-cmd --add-port=53/tcp --add-port=53/udp --permanent # 改进后(仅允许内网网段) firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.153.0/24" port port="53" protocol="tcp" accept' --permanent firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.153.0/24" port port="53" protocol="udp" accept' --permanent - 精细化 SELinux 配置(替代宽泛的
setsebool):# 主服务器区域文件上下文配置 semanage fcontext -a -t named_zone_t "/var/named/($DOMAIN|$REV_ZONE).zone" restorecon -v /var/named/($DOMAIN|$REV_ZONE).zone
5. 可维护性优化(降低二次开发成本)
问题
- 主从脚本存在大量重复代码(工具函数、权限配置、防火墙逻辑)
- 配置参数内嵌在脚本中,批量修改时需编辑两个文件
解决方案
- 抽离公共模块为独立脚本(如
common.sh):# common.sh(工具函数+公共配置) info(){ echo -e "\033[32m[info] $1\033[0m"; } warn() { echo -e "\033[33m[WARN] $1\033[0m"; } error() { echo -e "\033[31m[ERROR] $1\033[0m"; exit 1; } spinner(){ ... } # 公共进度条函数 # 主从脚本引用 source ./common.sh - 配置参数抽离为独立配置文件(如
dns_config.sh):# dns_config.sh MASTER_IP="192.168.153.131" SLAVE_IP="192.168.153.132" DOMAIN="web.com" REV_ZONE="153.168.192.in-addr.arpa" ALLOW_QUERY="192.168.153.0/24" # 允许查询的网段 # 主从脚本引用 source ./dns_config.sh
6. 易用性优化(提升用户体验)
问题
- 无参数校验(如 IP 格式错误、域名非法时脚本直接执行失败)
- 无帮助信息,新用户无法快速了解脚本用法
- 从服务器同步超时后需手动排查,未提供自动修复机制
解决方案
- 增加参数校验(IP 格式、域名合法性):
# IP格式校验函数 validate_ip() { local ip=$1 if ! [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then error "IP格式错误:$ip" fi } # 执行参数校验 validate_ip $MASTER_IP validate_ip $SLAVE_IP - 增加帮助选项(
-h):if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then echo "用法:$0 [选项]" echo " -h, --help:显示帮助信息" echo " -c, --config:指定配置文件路径(默认:./dns_config.sh)" exit 0 fi - 从服务器同步超时自动修复:
# 同步超时后尝试手动触发同步 if [ $SYNC_COUNT -ge $((SYNC_TIMEOUT / SYNC_INTERVAL)) ]; then warn "区域文件同步超时,尝试手动触发同步..." if ! rndc retransfer $DOMAIN &>/dev/null; then warn "手动同步失败,请执行 journalctl -u named | grep -i 'transfer' 排查" else info "手动同步触发成功,等待3秒后验证..." sleep 3 if [ -f "/var/named/slaves/$DOMAIN.zone" ]; then info "区域文件同步成功!" fi fi fi
7. 功能补充(增强生产可用性)
- 增加日志记录:将所有操作输出到
/var/log/dns_setup.log,方便排查# 脚本开头添加日志重定向 LOG_FILE="/var/log/dns_setup.log" exec > >(tee -a $LOG_FILE) 2>&1 - 服务健康监控:配置定时任务(
crontab),检查named服务状态,异常时自动重启# 新增健康检查函数 check_named_health() { if ! systemctl is-active --quiet named; then warn "named服务异常,正在重启..." systemctl restart named &>/dev/null fi } # 添加到crontab(每5分钟检查一次) (crontab -l 2>/dev/null; echo "*/5 * * * * /bin/bash $0 --check-health") | crontab - - 邮件通知:配置完成或失败后,发送邮件给管理员(依赖
mailx)# 配置完成后发送邮件 send_notify() { echo "DNS主服务器配置完成,验证命令:dig @$MASTER_IP www.$DOMAIN" | mailx -s "DNS配置结果" admin@web.com }
五、总结
1. 脚本优势
- 自动化程度高:一键完成主从 DNS 配置,无需手动修改配置文件
- 安全性基础保障:配置了最小权限、SELinux / 防火墙规则
- 用户体验友好:动态进度条、清晰的日志提示、同步失败排查建议
2. 核心改进优先级
- 兼容性优化(支持 CentOS 7/8)→ 2. 错误处理增强(降低排查难度)→ 3. 幂等性优化(支持重复执行)→ 4. 安全性增强(适配生产)→ 5. 可维护性 / 易用性优化