macOS分域名DNS解析部署
macOS 分域名 DNS 解析部署
1 描述
把指定域名后缀的解析交给局域网内的一台 DNS 服务器,其余域名继续使用系统默认 DNS。
用途:为开发域名提供统一解析,不修改 macOS 全局 DNS,也不在多台机器上维护 /etc/hosts。
组成部分:
- Ubuntu 上运行 dnsmasq,应答开发域名后缀,其余查询转发到公共上游。
- macOS 上放置
/etc/resolver配置文件,把该后缀的查询指向 Ubuntu。
后缀使用 .test。RFC 6761 将 .test 保留给测试用途,公网 DNS 不解析该后缀。不要使用 .local,RFC 6762 将其定义为 mDNS 语义,会被 Bonjour 特殊处理。
2 环境
| 角色 | 地址 | 职责 |
|---|---|---|
| macOS | 192.168.3.220 | 开发机,开发域名的解析目标 |
| Ubuntu | 192.168.3.202 | 运行 dnsmasq,应答开发域名 |
两台机器在同一局域网内互通。
本文使用以下变量,按实际环境修改:
| 变量 | 取值 | 含义 |
|---|---|---|
DEV_DOMAIN | dev.test | 开发域名后缀 |
TARGET_IP | 192.168.3.220 | 开发域名解析到的地址 |
DNS_IP | 192.168.3.202 | 运行 dnsmasq 的地址 |
UPSTREAM_DNS | 223.5.5.5 | 其余域名的上游 DNS |
3 安装步骤
3.1 Ubuntu 侧
声明变量。后续命令在同一个终端会话中执行。
export DEV_DOMAIN=dev.test
export TARGET_IP=192.168.3.220
export DNS_IP=192.168.3.202
export UPSTREAM_DNS=223.5.5.5
安装 dnsmasq 和 dig。
sudo apt update
sudo apt install -y dnsmasq dnsutils
创建开发域名配置。各配置项含义见文件内注释。
sudo tee /etc/dnsmasq.d/dev-domains.conf >/dev/null <<EOF
# $DEV_DOMAIN 及其所有子域解析到 $TARGET_IP
address=/$DEV_DOMAIN/$TARGET_IP
# 该后缀只在本机应答,任何记录类型都不转发到上游
local=/$DEV_DOMAIN/
# 不读 /etc/resolv.conf 取上游
no-resolv
# 显式指定上游:$DEV_DOMAIN 之外的域名走这里
# 使用 no-resolv 后不写本行,则没有任何上游,其余域名全部解析失败
server=$UPSTREAM_DNS
# 本机自用
listen-address=127.0.0.1
# 局域网地址,供 macOS 发送查询
listen-address=$DNS_IP
# 只绑定上面列出的地址,不监听通配地址
bind-interfaces
EOF
检查配置语法。本命令只做语法检查,不检查地址绑定。
sudo dnsmasq --test
启动服务并设置开机自启。
sudo systemctl restart dnsmasq
sudo systemctl enable dnsmasq
sudo systemctl status dnsmasq --no-pager
预期结果:Active: active (running)。
3.2 macOS 侧
声明变量。
export DEV_DOMAIN=dev.test
export DNS_IP=192.168.3.202
创建 /etc/resolver 目录。使用 install -d -m 而非 mkdir -p,权限不受当前 umask 影响。
sudo install -d -m 0755 /etc/resolver
创建 resolver 配置文件。文件名即匹配的域名后缀,不能添加扩展名。
printf 'nameserver %s\n' "$DNS_IP" | sudo tee /etc/resolver/$DEV_DOMAIN
刷新系统 DNS 缓存。两条命令成功时都没有输出。
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder
4 测试
4.1 Ubuntu 侧
在 Ubuntu 上查询本机 dnsmasq。RFC 7766 要求 DNS 同时支持 UDP 和 TCP,两种方式都要测试。
dig @127.0.0.1 foo.$DEV_DOMAIN A +short
dig +tcp @127.0.0.1 foo.$DEV_DOMAIN A +short
预期输出:
192.168.3.220
4.2 macOS 侧直接查询
在 macOS 上直接查询 Ubuntu,验证链路和 dnsmasq。
dig @$DNS_IP foo.$DEV_DOMAIN A +short
dig +tcp @$DNS_IP foo.$DEV_DOMAIN A +short
预期输出:
192.168.3.220
验证上游转发。
dig @$DNS_IP www.baidu.com A +short
预期输出:返回公网 IP 地址。
4.3 macOS 系统解析路径
dig @服务器 指定了服务器地址,不经过 macOS 的多 resolver 选择,无法验证 /etc/resolver 是否生效。以下命令走系统解析路径。
确认 resolver 已被系统加载。
scutil --dns | grep -A 3 "domain.*$DEV_DOMAIN"
预期输出:
domain : dev.test
nameserver[0] : 192.168.3.202
flags : Request A records
reach : 0x00020002 (Reachable,Directly Reachable Address)
查询一级子域、多级子域和裸域。
dscacheutil -q host -a name foo.$DEV_DOMAIN
dscacheutil -q host -a name a.b.c.$DEV_DOMAIN
dscacheutil -q host -a name $DEV_DOMAIN
预期输出(以一级子域为例):
name: foo.dev.test
ip_address: 192.168.3.220
dscacheutil 解析失败时没有任何输出,退出码同样为 0。以输出内容判断结果,不以退出码判断。
4.4 其余域名未受影响
分域名解析要求其余域名仍走系统默认 DNS。
dscacheutil -q host -a name www.baidu.com
预期输出:返回公网 IP 地址,且与 $DNS_IP 无关。
5 引用
- 用 splitdns 管理 macOS 的开发域名解析
- soulteary/splitdns
- resolver(5) 手册
- dnsmasq 手册
- RFC 6761 §6.2:
.test保留域名 - RFC 6762 §3:
.local属于 mDNS - RFC 7766 §5:DNS 需同时支持 UDP 与 TCP