Linux运维12 min read

BIND DNS 服务搭建与运维实战

为什么运维要会 DNS

DNS 是所有网络服务的寻址根基——域名解析慢/出错,再快的应用层也白搭。内网环境至少三个场景离不开自建 DNS:

场景 用 BIND 的理由
内网域名解析(service.internal) 避免 hosts 文件散落维护,集中管理
缓存加速上游 ISP DNS 慢/被劫持时,本地缓存 + 转发更可控
分区解析(split-horizon) 内外网同名域名指向不同 IP,简化配置

BIND9 是事实标准(ISC 出品),稳定可靠、文档丰富,CentOS/Ubuntu 都预装。

安装与目录结构

# CentOS/RHEL
yum install -y bind bind-utils
systemctl enable --now named

# Ubuntu/Debian
apt install -y bind9 bind9utils dnsutils
systemctl enable --now bind9

关键目录(以 CentOS 为例,Ubuntu 路径略有不同):

路径 用途
/etc/named.conf 主配置文件
/etc/named.rfc1912.zones zone 声明(推荐拆分到此文件)
/var/named/ zone 数据文件目录
/etc/rndc.key rndc 控制密钥
/var/log/named/ 日志目录(需手动配置)

Ubuntu 的配置在 /etc/bind/,zone 数据在 /var/cache/bind/,语法一致。

主配置文件 named.conf

options {
    listen-on port 53 { 127.0.0.1; 192.168.1.10; };   # 监听地址
    listen-on-v6 port 53 { ::1; };
    directory "/var/named";                              # 工作目录
    dump-file "/var/named/data/cache_dump.db";
    statistics-file "/var/named/data/named_stats.txt";
    
    # 递归查询白名单(谁可以用本机做递归)
    allow-query { localhost; 192.168.0.0/16; 10.0.0.0/8; };
    
    # 递归开关
    recursion yes;
    
    # 上游转发(缓存服务器模式推荐)
    forwarders {
        223.5.5.5;        # 阿里 DNS
        119.29.29.29;     # 腾讯 DNS
        8.8.8.8;          # Google DNS
    };
    forward first;         # 先转发,失败再自己递归
    
    dnssec-enable yes;
    dnssec-validation yes;
    dnssec-lookaside auto;
    
    bindkeys-file "/etc/named.iscdlv.key";
    managed-keys-directory "/var/named/dynamic";
    
    pid-file "/run/named/named.pid";
    session-keyfile "/run/named/session.key";
};

logging {
    channel default_debug {
        file "data/named.run";
        severity dynamic;
    };
    channel query_log {
        file "data/query.log" versions 3 size 50m;
        severity info;
        print-time yes;
        print-category yes;
    };
    category queries { query_log; };
    category lame-servers { null; };     # 忽略 lame server 噪音日志
};

include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";

⚠️ 避坑 #1allow-query 一定要限制为可信网段。设为 any 会被公网滥用做递归放大攻击(开放解析器是 DDoS 放大攻击的常见源)。

Zone 声明与数据文件

正向 zone 声明

编辑 /etc/named.rfc1912.zones

zone "internal.example.com" IN {
    type master;
    file "internal.example.com.zone";
    allow-update { none; };           # 动态更新关闭(推荐)
    allow-transfer { 192.168.1.11; }; # 允许从服务器 zone 传输
};

zone "1.168.192.in-addr.arpa" IN {    # 反向解析 zone
    type master;
    file "192.168.1.zone";
    allow-update { none; };
};

正向 zone 数据文件

创建 /var/named/internal.example.com.zone

$TTL 86400
@   IN  SOA ns1.internal.example.com. admin.internal.example.com. (
        2026071701  ; Serial(YYYYMMDDNN 格式,每次修改必须 +1)
        3600        ; Refresh(从服务器检查间隔)
        1800        ; Retry(失败重试)
        604800      ; Expire(从服务器数据过期时间)
        86400       ; Minimum TTL(NXDOMAIN 缓存时间)
    )

    IN  NS      ns1.internal.example.com.
    IN  NS      ns2.internal.example.com.
    IN  MX  10  mail.internal.example.com.
    IN  MX  20  mail2.internal.example.com.

ns1     IN  A   192.168.1.10
ns2     IN  A   192.168.1.11
mail    IN  A   192.168.1.20
mail2   IN  A   192.168.1.21
www     IN  A   192.168.1.100
api     IN  A   192.168.1.101
db      IN  A   192.168.1.50

; 别名记录
web     IN  CNAME   www.internal.example.com.
docs    IN  CNAME   web.internal.example.com.

; TXT 记录(SPF 等)
@       IN  TXT "v=spf1 mx -all"

反向 zone 数据文件

创建 /var/named/192.168.1.zone

$TTL 86400
@   IN  SOA ns1.internal.example.com. admin.internal.example.com. (
        2026071701
        3600
        1800
        604800
        86400
    )

    IN  NS  ns1.internal.example.com.
    IN  NS  ns2.internal.example.com.

10  IN  PTR ns1.internal.example.com.
11  IN  PTR ns2.internal.example.com.
20  IN  PTR mail.internal.example.com.
100 IN  PTR www.internal.example.com.
101 IN  PTR api.internal.example.com.

⚠️ 避坑 #2:反向 zone 名称是反序 IP + .in-addr.arpa。192.168.1.0/24 → 1.168.192.in-addr.arpa。写反了查不到 PTR,会导致很多服务(邮件、SSH 反查、NFS)出问题。

配置语法检查与加载

每次改完配置必须检查语法再 reload:

# 检查主配置
named-checkconf /etc/named.conf

# 检查 zone 数据文件
named-checkzone internal.example.com /var/named/internal.example.com.zone
named-checkzone 1.168.192.in-addr.arpa /var/named/192.168.1.zone

# 全部 OK 后 reload(不中断服务)
rndc reload
# 或 systemctl reload named

# 查看 zone 是否加载成功
rndc status
dig @127.0.0.1 www.internal.example.com
dig @127.0.0.1 -x 192.168.1.100

主从复制

生产 DNS 必须主从双机,单机故障 = 整个内网域名瘫痪。

从服务器配置

zone "internal.example.com" IN {
    type slave;
    masters { 192.168.1.10; };          # 主服务器 IP
    file "slaves/internal.example.com.zone";
    allow-notify { 192.168.1.10; };     # 允许主服务器 notify
};

zone "1.168.192.in-addr.arpa" IN {
    type slave;
    masters { 192.168.1.10; };
    file "slaves/192.168.1.zone";
};

⚠️ 避坑 #3:从服务器的 zone 文件目录(/var/named/slaves/)必须让 named 用户可写,否则 AXFR 传输后无法落盘。chown named:named /var/named/slaves/

主服务器修改 zone 后递增 Serial,执行 rndc reload,主服务器自动发送 NOTIFY 给从服务器触发增量传输。

Zone 传输安全加固

# 主服务器只允许指定从服务器拉 zone
allow-transfer { 192.168.1.11; };

# 更严格:用 TSIG 密钥认证
tsig-keygen -a HMAC-SHA256 transfer-key > /etc/transfer.key
chown root:named /etc/transfer.key
chmod 640 /etc/transfer.key

# 主配置引入
include "/etc/transfer.key";

zone "internal.example.com" IN {
    type master;
    file "internal.example.com.zone";
    allow-transfer { key "transfer-key"; };
    also-notify { 192.168.1.11 key "transfer-key"; };
};

# 从服务器配置对应密钥
include "/etc/transfer.key";
server 192.168.1.10 { keys { "transfer-key"; }; };

Split-Horizon(分区解析)

同一域名,内网解析到内网 IP,外网解析到公网 IP:

# 用 view 实现
acl "internal" { 192.168.0.0/16; 10.0.0.0/8; 127.0.0.1; };

view "internal_view" {
    match-clients { "internal"; };
    
    zone "example.com" IN {
        type master;
        file "internal/example.com.zone";     # 内网版本
    };
    zone "." IN {
        type hint;
        file "/var/named/named.ca";
    };
};

view "external_view" {
    match-clients { any; };                   # 其余所有
    
    zone "example.com" IN {
        type master;
        file "external/example.com.zone";     # 公网版本
    };
    recursion no;                             # 外部不允许递归
};

⚠️ 避坑 #4:使用 view 后,所有 zone 必须在 view 内声明,不能再放到 view 外。否则配置报错。

DNSSEC 签名

DNSSEC 给 zone 数据做数字签名,防止 DNS 响应被篡改(缓存投毒):

# 1. 生成 KSK(密钥签名密钥)和 ZSK(zone 签名密钥)
cd /var/named
dnssec-keygen -a ECDSAP256SHA256 -b 256 -n ZONE internal.example.com
dnssec-keygen -a ECDSAP256SHA256 -b 384 -f KSK -n ZONE internal.example.com

# 2. 在 zone 文件中引入公钥
$INCLUDE Kinternal.example.com.+013+12345.key
$INCLUDE Kinternal.example.com.+013+67890.key

# 3. 签名 zone
dnssec-signzone -o internal.example.com -N increment \
  -k Kinternal.example.com.+013+67890.key \
  internal.example.com.zone \
  Kinternal.example.com.+013+12345.key

# 4. 修改 named.conf 指向签名后的文件
zone "internal.example.com" IN {
    type master;
    file "internal.example.com.zone.signed";   # 注意 .signed 后缀
    dnssec-enable yes;
    inline-signing yes;
};

DNSSEC 的坑

  • 签名后 zone 文件变成二进制 .signed,不能直接编辑。改原始 .zone 后重新签名。
  • KSK 轮换时必须把 DS 记录注册到上级 zone(公网域名要找注册商)。
  • 时间必须同步(NTP),否则签名验证失败。

性能调优

# 系统层面(关键:提高 UDP 缓冲区)
sysctl -w net.core.rmem_max=4194304
sysctl -w net.core.rmem_default=4194304
sysctl -w net.core.netdev_max_backlog=2500

# named.conf 调优
options {
    max-cache-size 512m;                    # 缓存上限
    max-cache-ttl 604800;                   # 最大缓存 TTL(1 周)
    max-ncache-ttl 10800;                   # NXDOMAIN 缓存(3 小时)
    cleaning-interval 120;                  # 清理周期
    lame-ttl 600;                           # lame server 缓存
    
    # 多线程
    tcp-clients 100;                        # TCP 连接上限
    recursive-clients 1000;                 # 递归客户端上限
    clients-per-query 10;                   # 单查询并发上限
};

高并发场景考虑用 unbound(更轻量的纯递归缓存服务器),BIND 适合权威 DNS + 中小规模递归。

监控与日志

# 查询日志(已在 named.conf 配置)
tail -f /var/named/data/query.log

# 统计信息
rndc stats
cat /var/named/data/named_stats.txt

# 关键监控指标
# - QPS(每秒查询数)
# - 缓存命中率(>90% 为健康)
# - 递归延迟(<50ms 为优)
# - zone 传输状态(主从一致)
# - DNSSEC 验证失败数(应 = 0)

Prometheus + bind_exporter 监控模板

# bind_exporter 暴露 9119 端口
# 关键告警规则
- alert: DnsHighQueryLatency
  expr: histogram_quantile(0.95, bind_resolver_query_duration_seconds_bucket) > 0.5
  for: 5m
  labels: { severity: warning }

- alert: DnsZoneTransferFailed
  expr: increase(bind_zone_transfer_failure_total[10m]) > 0
  labels: { severity: critical }

- alert: DnsServiceDown
  expr: up{job="bind"} == 0
  for: 1m
  labels: { severity: critical }

常见故障排查

故障 诊断 解决
解析超时 dig @127.0.0.1 domain +trace 检查上游 forwarder 可达性、防火墙 53 端口
zone 不生效 rndc reloaddig @127.0.0.1 domain 检查 Serial 是否递增、named-checkzone 语法
反向解析失败 dig -x 192.168.1.100 反向 zone 名是否反序、PTR 记录是否齐全
从服务器数据不更新 rndc retransfer 手动触发 检查 allow-transfer、Serial 是否大于从服务器
DNSSEC 验证失败 dig +dnssec domain 看 AD 标志 检查时间同步、KSK/ZSK 是否过期、DS 是否注册上级
缓存投毒嫌疑 rndc flushname domain 清缓存 开启 DNSSEC 验证、限制 allow-query
日志爆炸 query.log 大小 配置 versions 3 size 50m 滚动
SERVFAIL 响应 dig +trace domain 逐级排查 上游故障 / DNSSEC 签名失效 / zone 配置错

⚠️ 避坑 #5rndc flush 会清空整个缓存,生产环境高峰期慎用。用 rndc flushname domain 只清指定域名的缓存。

十条避坑清单

  1. allow-query 必须限网段:开放递归器会被滥用做 DDoS 放大攻击,any 是灾难
  2. Serial 改了必须 +1:不递增从服务器不更新;推荐 YYYYMMDDNN 格式好维护
  3. 反向 zone 名反序写:192.168.1.0/24 → 1.168.192.in-addr.arpa,写反了 PTR 全失效
  4. FQDN 结尾加点:zone 文件里 ns1.internal.example.com.(末尾点)才是绝对域名,少了点会拼接当前 zone 名
  5. 从服务器目录权限/var/named/slaves/ 必须 named 用户可写,否则 AXFR 无法落盘
  6. view 内必须包所有 zone:用了 view 就不能再有 view 外的 zone 声明
  7. DNSSEC 时间必须同步:签名依赖时间戳,NTP 不同步 = 验证全部失败
  8. forward first vs forward onlyforward first 失败回退自递归(更可靠);forward only 全依赖上游(上游挂了就全挂)
  9. rndc reload 不中断服务:改配置用 rndc reload,不要 systemctl restart(会断开所有连接)
  10. TTL 别设太短:内网 zone TTL 86400(1天)够用;TTL 太短 = 缓存频繁失效 = 上游压力 + 解析变慢

总结

BIND DNS 运维核心要点:

  • allow-query 限网段是安全底线——开放递归器是公害
  • 主从双机 + TSIG 认证——单机 DNS 不可接受
  • Serial 递增 + rndc reload——改 zone 数据的标准流程
  • 反向 zone 与正向配对——很多服务依赖 PTR 反查
  • DNSSEC 看场景上——公网权威 zone 推荐,内网可不上

掌握 zone 声明、数据文件编写、主从复制、split-horizon 这四块,BIND DNS 运维就能覆盖企业内网绝大多数需求。

分享:

评论区