dnscrypt-proxy + dnsmasq的高级应用 - 智能分流DoH/DoT

2022-11-18 14:59:55 浏览数 (2)

前言

普通DNS没有加密能力,所有查询默认都是UDP明文传输,当然有些返回字节结果过大也会截断采用TCP传输(retrying in TCP mode),但这些都是明文;对于一些安全要求较高的业务场景,为避免出现劫持、污染等引起的安全威胁,DNS over HTTPS(DoH)以及DNS over TLS(DoT)就派上了用场。同时HTTPDNS也可以规避运营商劫持的问题,主要原理就是绕过ISP提供的LDNS直接请求HTTPDNS服务,具有域名防劫持、调度精准等特性,但主要面向的是移动APP场景,解析流程如下:

回到正题,dnscrypt-proxy作为DoH/DoT的DNS转发服务,配合业界已经存在的各大DoH/DoT公共服务,轻松实现DNS加密传输。

在此基础上,又需要做到国内外域名分流走不同的DoH/DoT解析,以提高解析效率及精准度,这里使用了dnsmasq dnscrypt-proxy实现,架构如下:

dnsmasq的安装配置这里不会详细展开介绍,可以参照上篇文章。

一、安装dnscrypt-proxy

1.软件源安装

各个发行版的软件仓库基本都会内置dnscrypt-proxy,也优先推荐选择此安装方式,会自动写好systemd服务。

发行版

安装命令

Arch

pacman -Sy dnscrypt-proxy/yay -Sy dnscrypt-proxy

CentOS/RedHat

yum install -y dnscrypt-proxy

Debian/Ubuntu

apt-get install -y dnscrypt-proxy

2.二进制安装

(1)获取

到releases页面下载最新版:

代码语言:shell复制
cd /opt  #习惯放到/opt目录,你也可以放到/etc目录或其它目录下
wget https://github.com/DNSCrypt/dnscrypt-proxy/releases/download/2.1.2/dnscrypt-proxy-linux_x86_64-2.1.2.tar.gz

截至2022年11月最新版为2.1.2。

(2)解压及软链
代码语言:shell复制
tar xvf dnscrypt-proxy-linux_x86_64-2.1.2.tar.gz
mv linux-x86_64 dnscrypt-proxy
cd dnscrypt-proxy
ln -sf /opt/dnscrypt-proxy/dnscrypt-proxy /usr/sbin/dnscrypt-proxy
  • 重命名提升可读性,解压后,你会在目录下看到一个可执行的dnscrypt-proxy文件以及一些示例配置文件。
  • 将可执行文件软链到PATH目录。
(3)写systemd服务
代码语言:shell复制
vim /etc/systemd/system/dnscrypt-proxy.service

写入如下内容:

代码语言:shell复制
[Unit]
Description=Encrypted/authenticated DNS proxy
ConditionFileIsExecutable=/usr/sbin/dnscrypt-proxy

[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/usr/sbin/dnscrypt-proxy "-config" "dnscrypt-proxy.toml"

WorkingDirectory=/opt/dnscrypt-proxy

Restart=always

RestartSec=120
EnvironmentFile=-/etc/sysconfig/dnscrypt-proxy

[Install]
WantedBy=multi-user.target

重载systemd守护进程:

代码语言:shell复制
systemctl daemon-reload

开机自启动:

代码语言:shell复制
systemctl enable dnscrypt-proxy.service

二、Public Servers与服务配置重写

1.临时测试解析情况

目录里面的example-dnscrypt-proxy.toml则为主要配置文件,把它拷贝一份作为正式配置文件:

代码语言:shell复制
cp example-dnscrypt-proxy.toml dnscrypt-proxy.toml

在还没有通过systemctl启动时可以先前台运行:

代码语言:shell复制
./dnscrypt-proxy "-config" "dnscrypt-proxy.toml"

测试解析是否正常:

代码语言:shell复制
./dnscrypt-proxy -resolve google.com

可以正常解析到地址,通过日志看,默认会请求内置的DoH server,因为这里DoH server大多是境外的,如果你在国内,此时科学上网能力是基础保障。

2.DoH/DoT服务列表

(1)public servers

dnscrypt-proxy官方提供一个服务列表(境内可能需要科学上网才能打开),可以任意在支持的列表中选择上游DNS使用的服务器。

国内支持的目前只有阿里和清华大学的DoH server:

(2)public servers map

同时还提供地图查找法,支持的点会在地图上标注出来:

方便选择距离你最近的DoH上游服务器。

3.配置文件重写

基于国内外分流需求,需要写两个配置文件,一个用于国外,一个用于国内。

(1)国内配置

dnscrypt-proxy.toml内置的大部分功能是我们不需要的,这里将dnscrypt-proxy.toml重写为:

代码语言:shell复制
# Empty listen_addresses to use systemd socket activation
listen_addresses = ['0.0.0.0:5533']
server_names = ['alidns-doh','tuna-doh-ipv4']
cache_size = 4096
cache_min_ttl = 2400
cache_max_ttl = 86400
cache_neg_min_ttl = 60
cache_neg_max_ttl = 600
[query_log]
  file = '/var/log/dnscrypt-proxy/query.log'

[nx_log]
  file = '/var/log/dnscrypt-proxy/nx.log'

[sources]
  [sources.'public-resolvers']
  url = 'https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md'
  cache_file = '/var/cache/dnscrypt-proxy/public-resolvers.md'
  minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
  refresh_delay = 72
  prefix = ''
  • 监听地址这里设置5533而非53,防止系统有内置的DNS服务,产生冲突;
  • server_names即为定义的上游DoH/DoT服务器,不能任意写,需要在支持的服务列表里面。
(2)国外配置

dnscrypt-proxy.toml拷贝一份:

代码语言:shell复制
cp dnscrypt-proxy.toml dnscrypt-proxy-foreign.toml

server_names改成国外的DoH/DoT服务器,并修改监听端口,修改后的示例如下,其他地方不用删改:

代码语言:shell复制
listen_addresses = ['0.0.0.0:25533']
server_names = ['cloudflare','google']

4.写systemd服务

国内配置已经写到systemd,那么国外配置同理,也需要一个进程监听运行。

代码语言:shell复制
vim /etc/systemd/system/dnscrypt-proxy-foreign.service

写入如下内容:

代码语言:shell复制
[Unit]
Description=Encrypted/authenticated DNS proxy
ConditionFileIsExecutable=/usr/sbin/dnscrypt-proxy

[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/usr/sbin/dnscrypt-proxy "-config" "dnscrypt-proxy-foreign.toml"

WorkingDirectory=/opt/dnscrypt-proxy

Restart=always

RestartSec=120
EnvironmentFile=-/etc/sysconfig/dnscrypt-proxy

[Install]
WantedBy=multi-user.target

重载systemd守护进程:

代码语言:shell复制
systemctl daemon-reload   

开机自启动:

代码语言:shell复制
systemctl enable dnscrypt-proxy-foreign.service

三、启动与测试验证

1.启动

国内外分别加载一个toml配置文件,并且已经写好对应的systemd服务了,接下来启动并测试下是否符合预期。

代码语言:shell复制
systemctl start dnscrypt-proxy.service
systemctl start dnscrypt-proxy-foreign.service

服务正常监听并运行,5533监听用于国内域名,25533监听用于国外域名。

2.测试验证

此时并没有做国内外智能分流,先单独验证下两个服务是否都正常解析。

查看解析日志文件:

代码语言:shell复制
tail -f /var/log/dnscrypt-proxy/query.log

dig命令测试不同端口的解析所走的上游DNS:

代码语言:shell复制
dig zijiebao.com @192.168.1.72 -p 5533  short
dig youtube.com @192.168.1.72 -p 25533  short

到此,说明两个服务运行状态良好。

四、Dnsmasq实现国内外域名智能分流

1.修改dnsmasq上游DNS

如dnsmasq还没安装配置,可参考上篇文章,直到做到dnsmasq-china-list这一步实现dnsmasq维度的国内外分流。

之后,只需修改上游DNS为dnscrypt-proxy运行的机器即可,如果dnsmasq和dnscrypt-proxy在同一台机器运行,则修改成本地地址即可。

(1)指定国内上游DoH监听地址

国内Doh则需修改dnsmasq-china-list里的accelerated-domains.china.conf,将IP替换为国内DoH监听地址:

代码语言:shell复制
sed -i 's|114.114.114.114|127.0.0.1#5533|g' accelerated-domains.china.conf

替换后的结果形如server=/0-100.com/127.0.0.1#5533

因为我这里将两个服务独立运行在两个系统,所以替换为对应运行dnscrypt-proxy的机器的国内DoH监听地址:

(2)指定国外上游DoH监听地址

因为resolv.conf不能指定端口,需要在dnsmasq.conf定义上游DNS为国外DoH的监听地址。所以/etc/resolv.conf这里写本地地址,dnsmasq.conf加一条到国外DoH监听地址的配置:

代码语言:shell复制
$ cat /etc/resolv.conf
nameserver 127.0.0.1
$ cat /etc/dnsmasq.conf
log-queries
log-facility=/var/log/dnsmasq.log
no-hosts
bogus-nxdomain=119.29.29.29
cache-size=1000
port=53
#以下为增加的配置,25533前面的#号并非注释,而是指定端口,如果dnsmasq和dnscrypt-proxy在同一台机器,替换为127.0.0.1#25533
server=192.168.1.72#25533
(3)iptables DNAT规则

基于#(2)的拓展方案,可选项,二者任选其一,适用于dnsmasq和dnscrypt-proxy在不同机器运行的情况。

如果不想在dnsmasq.conf里面指定server为国外DoH地址,而是直接在/etc/resolv.conf设置,那么只需在dnscrypt-proxy机器做一条53端口DNAT到国外DoH监听端口的规则:

代码语言:shell复制
#192.168.1.72 替换为dnscrypt-proxy机器内网IP
iptables -t nat -A PREROUTING -i ens192 -p tcp --dport 53 -j DNAT --to-destination 192.168.1.72:25533
iptables -t nat -A PREROUTING -i ens192 -p udp --dport 53 -j DNAT --to-destination 192.168.1.72:25533

此时修改dnsmasq机器的/etc/resolv.conf文件内容如下:

代码语言:shell复制
nameserver 192.168.1.72

2.验证DoH/DoT智能分流场景

使用任意一台内网机器,将DNS解析修改为dnsmasq的机器地址,之后访问国内外域名测试验证。

机器

角色/服务

192.168.1.71

客户端,dns指向dnsmasq

192.168.1.72

dnscrypt-proxy

192.168.1.73

dnsmasq

(1)日志验证

如果你完全按照我上面的配置来搭建,那么对应的日志文件路径如下:

  • dnsmasq query日志路径:/var/log/dnsmasq.log
  • dnscrypt-proxy query日志路径:/var/log/dnscrypt-proxy/query.log

使用dig命里测试,并监听对应的日志文件输出:

可以清晰看到,国内域名解析,dnsmasq转发给dnscrypt-proxy的国内上游DoH,国外域名解析,则转发给国外上游DoH,并且缓存记录在dnsmasq做了控制,重复请求直接命中缓存返回给客户端。

(2)抓包验证

dig github.com作为验证示例:

同时在dnsmasq和dnscrypt-proxy机器上部署抓包:

dnsmasq机器:

代码语言:shell复制
tcpdump -i any -nn -s 0 port 53 or port 5533 or port 25533 or port 443 -v -w dnsmasq.pcap

dnscrypt-proxy机器:

代码语言:shell复制
tcpdump -i any -nn -s 0 port 53 or port 5533 or port 25533 or port 443 -v -w dnscrypt-proxy.pcap

之后将.pcap文件下载到本地,使用wireshark分析如下:

  • dnsmasq:9号包,收到来自客户端的DNS query请求;
  • dnsmasq:10号包,dnsmasq向上游dnscrypt-proxy请求;
  • dnscrypt-proxy:5号包,dnscrypt-proxy收到来自dnsmasq的dns query请求;
  • dnscrypt-proxy:6-30号包,向上游Google DoH建立TCP三次握手以及TLS握手,加密传输DNS请求;
  • dnscrypt-proxy:31-33号包,拿到加密后的响应数据;
  • dnscrypt-proxy:34号包,解密后将解析结果返回给dnsmasq;
  • dnsmasq:11号包,拿到解析记录;
  • dnsmasq:12号包,将结果返回给客户端。

可以清晰看到,dnsmasq收到请求后,转发给国外DoH服务器处理,并且此过程经过了TLS加密传输,极大的保证了DNS安全性,拿到请求并且解密后正常返回给客户端。

总结

到此,dnsmasq dnscrypt-proxy实现国内外DoH/DoT分流解析已经全部完成。实现原理也很简单,dnsmasq机器作为入口,使用dnsmasq-china-list大陆域名白名单实现分流转发给上游dnscrypt-proxy处理,dnscrypt-proxy再往对应的DoH/DoT public servers加密转发拿到解析结果。

另外,dnscrypte-proxy还有负载均衡能力,在toml配置文件中通过lb_strategy参数指定,参数范围可以是:

  • first:总是选择列表中最快的服务器
  • p2:随机选择前2名最快的服务器,默认选项
  • ph:在所有服务器中最快的一半之间随机选择
  • random:从Server列表中随机选取

根据不同业务场景调整,同时可以多选几个优质上游DoH/DoT,适当增加dnscrypte-proxy的RS数量,提升优选对象。

附带PDF版本:

dnscrpt-proxy dnsmasq的高级应用-分流实现DoH、DoT.pdf
dnscrpt-proxy dnsmasq的高级应用-分流实现DoH、DoT.pdf(亮色版).pdf

0 人点赞