Ubuntu服务器直接依赖外部DNS解析,等于把每次内部服务互访的路径暴露在公网环境里。一旦外部DNS被劫持、投毒或响应延迟,内部系统之间的调用就可能被导向错误地址,甚至造成数据泄露。内网DNS部署的核心价值不是简单“加速解析”,而是把解析控制权收回自己手里,切断外部不可控因素对内部服务发现的影响。下面直接讲部署和加固的具体方法。
确认内网DNS的部署场景与边界先明确你的Ubuntu服务器集群里,哪些主机需要被内部域名访问,哪些服务依赖域名而不是IP。常见场景包括:数据库主从用域名切换、微服务注册中心用内部域名通信、CI/CD流水线拉取内部Git仓库。内网DNS只负责解析这些内部域名,所有非内部域名的查询可以转发到上游可信DNS,或者直接拒绝,避免服务器把内部请求发到公网。
部署前要规划一个专用域名区域,例如internal.example.com或lan.local。不要使用公网真实存在的域名作为内部区域,否则解析逻辑会混乱。所有内部主机的A记录、CNAME记录都归属这个区域,外部用户无法从公网解析到这些名称。
Ubuntu 22.04/24.04仓库里的BIND9版本稳定,直接安装即可:
sudo apt update sudo apt install bind9 bind9utils bind9-doc -y
安装后第一步不是配置区域文件,而是限制BIND只监听内网网卡。编辑/etc/bind/named.conf.options:
options {
directory "/var/cache/bind";
listen-on port 53 { 127.0.0.1; 10.10.10.10; };
listen-on-v6 port 53 { ::1; };
allow-query { 127.0.0.1; 10.10.10.0/24; };
allow-recursion { 127.0.0.1; 10.10.10.0/24; };
allow-transfer { none; };
recursion yes;
dnssec-validation yes;
forwarders {
223.5.5.5;
119.29.29.29;
};
forward only;
};
这里的10.10.10.10是假设的Ubuntu服务器内网IP,10.10.10.0/24是允许查询的内网网段。关键点在于allow-recursion只放行内网,allow-transfer设为none,防止区域传送泄露内部记录。上游转发使用了国内公共DNS,避免依赖单一外部解析源,同时forward only确保外部查询失败时不会自行迭代到根服务器,减少外部暴露面。
编辑/etc/bind/named.conf.local,声明内部正向区域:
zone "internal.example.com" {
type master;
file "/etc/bind/db.internal.example.com";
allow-update { none; };
};
然后创建区域文件/etc/bind/db.internal.example.com:
$TTL 3600
@ IN SOA ns1.internal.example.com. admin.internal.example.com. (
2025061801 ; serial
3600 ; refresh
600 ; retry
1209600 ; expire
3600 ) ; negative cache TTL
@ IN NS ns1.internal.example.com.
ns1 IN A 10.10.10.10
db1 IN A 10.10.10.21
cache1 IN A 10.10.10.22
gitlab IN A 10.10.10.23
registry IN A 10.10.10.24
每修改一次记录,必须递增serial值,否则从属DNS不会同步。如果只有单台内网DNS,serial可以手动维护;如果有多台,建议用脚本自动更新serial。
配置反向解析减少日志误报很多安全审计工具和系统日志会做反向PTR查询,如果内网IP没有PTR记录,日志里会出现大量解析失败噪音,掩盖真正的异常。为内网网段创建反向区域:
zone "10.10.10.in-addr.arpa" {
type master;
file "/etc/bind/db.10.10.10";
};
区域文件内容:
$TTL 3600
@ IN SOA ns1.internal.example.com. admin.internal.example.com. (
2025061801 ; serial
3600 ; refresh
600 ; retry
1209600 ; expire
3600 ) ; negative cache TTL
@ IN NS ns1.internal.example.com.
10 IN PTR ns1.internal.example.com.
21 IN PTR db1.internal.example.com.
22 IN PTR cache1.internal.example.com.
23 IN PTR gitlab.internal.example.com.
24 IN PTR registry.internal.example.com.
PTR记录让内部IP反查域名时返回可控结果,避免外部DNS被查询,也方便运维排障时快速识别主机角色。
限制递归查询防止被滥用为放大攻击源内网DNS如果开放递归给所有来源,可能被外部主机利用进行DNS放大攻击。即使监听在内网IP上,如果该IP通过NAT或防火墙误暴露到公网,风险依然存在。除了allow-recursion限制内网网段,还可以在防火墙层面只放行内网网段访问53端口:
sudo ufw allow from 10.10.10.0/24 to any port 53 proto udp sudo ufw allow from 10.10.10.0/24 to any port 53 proto tcp sudo ufw deny 53
Ubuntu默认防火墙规则顺序很重要,先放行内网,再拒绝所有其他来源访问53端口。这样即使BIND配置出现遗漏,网络层也能兜底。
配置客户端服务器使用内网DNSUbuntu服务器自身要优先使用内网DNS。如果使用netplan管理网络,编辑/etc/netplan/00-installer-config.yaml:
network:
ethernets:
ens18:
dhcp4: no
addresses:
- 10.10.10.10/24
routes:
- to: default
via: 10.10.10.1
nameservers:
addresses:
- 127.0.0.1
- 10.10.10.10
search:
- internal.example.com
注意把内网DNS放在第一位,并且search域设置为内部区域。这样服务器内部访问db1时,会自动补全为db1.internal.example.com并走内网解析。其他内网主机也把DNS指向10.10.10.10,不要混用外部DNS,否则会出现部分解析走公网的情况。
如果使用systemd-resolved管理DNS,需要修改/etc/systemd/resolved.conf:
[Resolve] DNS=10.10.10.10 Domains=internal.example.com FallbackDNS=223.5.5.5
然后重启服务:sudo systemctl restart systemd-resolved。但要确认/etc/resolv.conf是指向/run/systemd/resolve/stub-resolv.conf的软链接,否则配置不生效。
外部解析启用DNSSEC验证可以防止缓存投毒,但内部区域如果也要DNSSEC,需要额外签名。对于大多数内网环境,内部区域不签名、外部转发启用验证是平衡安全与维护成本的做法。在named.conf.options里保持dnssec-validation yes,BIND会验证上游返回的DNSSEC签名,但不会要求内部区域签名,因为内部区域是权威数据,不走验证逻辑。
如果内网规模较大且有严格合规要求,可以对内部区域做DNSSEC签名,使用dnssec-keygen和dnssec-signzone生成密钥并签名区域。但签名后每次修改记录都要重新签名,维护复杂度明显上升。对于减少外部解析风险这个目标,先做好网络隔离和递归限制比内部签名更直接有效。
部署完成后,要持续观察BIND日志里是否有异常查询。开启查询日志:
sudo rndc querylog on
日志会写入/var/log/syslog,可以用以下命令过滤出被拒绝的查询:
sudo grep "denied" /var/log/syslog | tail -n 50
重点看是否有非内网来源尝试查询,或者内部主机频繁查询外部域名。如果某台内部主机突然大量查询外部域名,可能是被入侵后作为跳板进行C2通信。内网DNS在这里起到了一个轻量级检测点的作用。
还可以配置BIND的response policy zone(RPZ)来阻断已知恶意域名。Ubuntu仓库版本支持RPZ,创建一个本地策略区域,把威胁情报里的恶意域名指向0.0.0.0或127.0.0.1,这样内网主机即使被诱导访问恶意域名,也无法解析到真实IP。
单台内网DNS一旦宕机,所有内部服务解析中断,等于自己制造了一个单点故障。至少部署两台,一台主、一台从。主DNS上允许从DNS做区域传送,修改named.conf.local:
zone "internal.example.com" {
type master;
file "/etc/bind/db.internal.example.com";
allow-transfer { 10.10.10.11; };
also-notify { 10.10.10.11; };
};
从DNS上配置为slave类型:
zone "internal.example.com" {
type slave;
masters { 10.10.10.10; };
file "/var/cache/bind/db.internal.example.com";
};
客户端DNS配置同时指向两台内网DNS,这样任意一台维护或故障都不影响解析。主从同步通过区域传送完成,serial递增后从服务器自动拉取更新。
验证部署效果与回滚方案部署完成后用dig验证:
dig @10.10.10.10 db1.internal.example.com dig @10.10.10.10 -x 10.10.10.21 dig @10.10.10.10 www.baidu.com
第一条应返回内网IP,第二条返回PTR记录,第三条走上游转发返回公网地址。如果第三条失败,检查forwarders和forward only配置。如果第一条失败,检查区域文件路径和权限,BIND运行用户bind需要能读取区域文件。
回滚方案是保留原有/etc/resolv.conf备份,如果内网DNS出现严重问题,客户端可以临时切回外部DNS。但要注意,切回外部DNS后内部域名将无法解析,依赖内部域名的服务会中断。所以更稳妥的做法是准备一台最小化的备用DNS,而不是直接退回外部解析。
内网DNS部署不是一次性工作,后续每增加一台内部服务器,都要同步添加A记录和PTR记录。把区域文件纳入版本管理,每次变更走审批和记录,才能长期保持解析可控。相比让服务器直接查询外部DNS,自建内网DNS把内部服务发现与外部分辨率隔离开,显著降低了被劫持、被投毒、被外部探测的风险。
