6 min read

DNSdist配置并启用DNS-over-HTTP(DoH)

这篇文章分享了基于DNSdist和CloudFlare的公共DNS服务器搭建局域网(或公共网络)中你自己或你的组织私有的DNS服务器并且支持DoH协议,可以为你提供更加安全的网络实践。

这篇文章是《基于DNSdist搭建动态的私有化DNS(DnsOverHttp(s))服务》的一部分,继续阅读之前,请确保你已经按照前文《在Ubuntu上安装基于DNSdist的DNS负载均衡服务器》的指导,在你的服务器上完成了DNSdist的安装和基本配置,且可以正常运行。今天我们主要讨论的是,如何基于之前的DNSdist配置来开启DNS-over-HTTP支持,使得客户端可以通过DoH协议访问你的私有DNS服务器。

修改配置

运行下列命令,修改你的DNSdist配置文件

sudo nano /etc/dnsdist/dnsdist.conf

在你的配置文件末尾添加下列配置内容

-- Add a new DoH listen address and port
addDOHLocal("10.243.245.122:8053", null, null,"/dns-query")

以上配置的意思为开启一个本地DOH监听,监听本地地址10.243.245.122(由于我的服务器存在多个网卡,且10.243.245.122是我的内网地址,我希望仅通过内网访问我的DoH服务器,因此这里监听内网IP),端口为8053,后面的两个null,表示不设置TLS证书和私钥,因此默认提供HTTP监听,最后的“/dns-query”表示匹配URL路径,仅当访问匹配的URL路径时对应的流量才会路由给DOH。

city skyline near body of water during daytime
Photo by Zhang qc / Unsplash

修改之前配置的Server部分,这里我们使用CloudFlare提供的公共DNS服务,来作为DNSdist的下游服务器:

-- Add new backend DNS servers, default port is 53
newServer({address="1.1.1.1", qps=100})
newServer({address="1.0.0.1", qps=5000})
setServerPolicy(firstAvailable) -- first server within its QPS limit

额外的,如果你也希望通过内网访问DoH服务器,但是你的内网IP是自定义的,那么你还需要在配置文件中增加ACL来允许特定的IP地址段或IP地址访问你的DoH服务,这里我们举例让ACL放行10.243.0.0/16范围的IP地址访问,则在配置文件添加下列行:

-- Allow a IP grange to access DNS service
addACL('10.243.0.0/16')

测试模式运行

正常情况下,你的DoH会默认放行常见的内网IP段。接下来,让我们运行测试命令,尝试启动一下我们的DoH服务器:

dnsdist -C   /etc/dnsdist/dnsdist.conf -vvv

执行上述命令,控制台进入调试运行模式,输出如下

测试运行DNSdist服务器

接下来,通过我编写的基于Python的DoH测试工具,来测试我们的这个局域网内的DoH服务器(由于网上找了很久没有找到合适的工具,因此决定用Python写一个,虽然命令行测试就很棒,但由于之前排错过程使用命令行非常不方便,另外期间我也尝试了采用Windows10/11自带的curl命令进行测试,但依然会收到莫名的错误比如“The DNS query could not be parsed”,因此我认为这种错误是由于构造的DoH查询不规范造成的,几经修改还是没有成功,最后在Python上使用dnspython扩展包构建了标准的DoH请求,成功了。)下面分享这一个实现的测试代码:

#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# Created by Travis Tang, published on https://evzs.com
#
from dns import message
import requests

# 设置DoH服务器URL
doh_url = "http://10.243.245.122:8053/dns-query?".encode()

# 构建请求头部,指明我们希望以JSON形式获取响应
headers = {
    'Accept': 'application/dns-message',
    'Content-Type': 'application/dns-message'
}

# 设置DNS查询参数
# 构造一个DNS查询
query = message.make_query('www.evzs.com', 'A')
query_data = query.to_wire()  # 将查询消息转换为二进制数据

# 发送GET请求
response = requests.post(doh_url, headers=headers, data=query_data)

# 检查HTTP响应状态是否为200(OK)
if response.status_code == 200:
    # 将响应内容(二进制数据)解码为DNS消息
    dns_response = message.from_wire(response.content)
    # 打印解码后的DNS响应
    print(dns_response)
else:
    print("Failed to get DNS response, status code:", response.status_code)

在Python3.12上运行

如果你需要使用上述代码,你只需要安装对应的Python扩展并且修改DoH服务器地址和域名即可,从Python发出DoH请求之后,在我的PyCharm中获得了来自我的DoH服务器的DNS查询响应:

同时,在调试服务器窗口中也出现了请求日志:

DoH服务器响应客户端请求

到这里就表示我们基本的DoH服务就搭建完成了,接下来让我们重新以系统服务启动它。

启动系统服务

退出调试模式,执行下列命令来重启你的DNSdist服务

sudo systemctl restart dnsdist
sudo systemctl status dnsdist
DNSdist DoH服务器正在运行

重启后将会看到界面上告知目前DNSdist已经开启了 DoH服务。为了安全使用,你可能还需要对你所在的网络在DNSdist配置文件中使用ACL进行授权,还有记得服务器和对应的网络安全组(如果你的云服务器提供商有这个)上放行对应的IP和端口。至此,你便可以开始独享你自己的DoH DNS服务器了。😃