本地模拟CNAME解析

  众所周知,我们可以通过修改本地 hosts 文件来定义一个域名的指向,这个过程我们可以简单的理解为在本机创建了一个优先级很高的 DNS A 记录,实现了某种程度上的域名劫持, 利用这种特性我们可以实现诸如广告屏蔽、本地测试、别名等需求,但 hosts 机制仅仅能定义域名与 IP 对应关系, 并不能模拟其他的 Record 类型, 比如 CNAME 记录。

前言

  由于近期某个应用在某些地区的连通性较差,于是考虑临时对其使用 CDN, 在上线之前,我们需要测试并调整 CDN 回源、缓存策略等各项配置,由于不是新上线的域名,这个域名是有用户在使用的,所以我们不能简单粗暴的将 DNS 记录直接切到 CDN 的 CNAME 域名, 因此我们只能考虑在本地搭建一个 DNS 服务,用于模拟 CNAME。

安装 DNS 服务

  本机为 macOS,使用 homebrew 来安装 dnsmasq 搭建 DNS 服务器, 其他系统配置文件路径会有所不同。

1
2
3
4
5
# 安装 dnsmasq 
brew install dnsmasq

# 备份默认配置文件
cp /opt/homebrew/etc/dnsmasq.conf /opt/homebrew/etc/dnsmasq.conf.bak

默认的 dnsmasq 配置文件很长,不过大多数配置都注释掉了,可以根据注释来配置我们需要的,这里仅配置一下我们需要关注的地方:

vim /opt/homebrew/etc/dnsmasq.conf:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 定义一下上游的 DNS 服务地址
resolv-file=/Users/alliot/dns.conf
# 按照 resolv-file 中列出的顺序查询上游 DNS 服务器
strict-order

user=root
listen-address=127.0.0.1

# 从指定的文件读取额外的主机名映射,也就是 hosts
addn-hosts=/etc/banner_add_hosts

# 创建一条CNAME记录,www.iots.vip会CNAME到alliot.blog.net,多条记录可以在配置文件中写多条配置
cname=www.iots.vip,alliot.blog.net

创建一下 dns.conf 指定上游的 DNS:
vim /Users/alliot/dns.conf

1
2
nameserver 114.114.114.114
nameserver 223.5.5.5

之后重启服务:

1
2
# 这里由于我们上面启动dnsmasq的用户是root,所以必须加sudo  
sudo brew services restart dnsmasq

配置本机DNS

假设我们 macOS 当前使用的 Wi-Fi 网络,我们可以直接使用命令行配置 Wi-Fi 网卡的 DNS 服务器:

1
2
3
4
5
6
7
# 获取当前的 DNS 配置
networksetup -getdnsservers Wi-Fi

# 修改DNS为127.0.0.1,即我们的dnsmasq服务
networksetup -setdnsservers Wi-Fi 127.0.0.1

networksetup -getdnsservers Wi-Fi

当然你也可以通过界面来配置:
依次点击 “系统偏好设置 > 网络 > 高级 > DNS”,选中 “DNS” 配置,删除现有的公共 DNS,填入 127.0.0.1 保存即可。

至此我们便可以完成了在本地模拟 CNAME 解析了,当我们访问 www.iots.vip 时,便 CNAME 到了 alliot.blog.net

验证

我们可以使用 dig 来指定 DNS 服务器来验证一下:

1
2
3
4
5
6
7
8
9
# 先使用114的DNS解析来看一下现网的DNS记录: 
dig +short www.iots.vip @114.114.114.114
# 这里会返回我们现网的DNS记录

# 再使用本地我们的dnsmasq来解析一下:
dig +short www.iots.vip @127.0.0.1

# 再直接使用当前系统配置的DNS服务看看是否生效:
dig +short www.iots.vip

结语

原理是 DNS 的工作方式是分层的,就像一个有许多层次的目录或者电话簿。
我们这里将本机 DNS 指定为了我们启动的 dnsmasq, dnsmasq 指定了 114.114.114.114 为上游 DNS, 那么当我们访问一个网站时,DNS 解析路径就为 hosts -> dnsmasq -> 114DNS.
www.iots.vip 在 dnsmasq 时找到了一个 CNAME 记录,便不会再往 114DNS 去查询,达到了自定义 CNAME 记录的目的。