dnsmasq 不支持 doh 和 dot 怎么办,想自建 doh 和 dot 服务端怎么办,总有 ddos 想限速怎么办,快用 dnsdist
1 、 dnsdist 是什么
dnsdist 是一个抗 DoS 的 DNS 负载均衡器。其设计目标是路由流量到优选的服务器,同时分流或阻止恶意流量。 [ 链接 ]
2 、 dnsdist 基础配置
使用 Nginx 作为 dot/doh 网关 [ 链接 ]
DOH 服务搭建 [ 链接 ]
Nginx+doh [ 链接 ]
集群 DNS 基础解析 [ 链接 ]
2.1 、安装 DNSdist
因为官方源中的 dnsdist 是 v1.4 版本,太老了,所以需要换源 [ 链接 ]
举个例子,v1.4 不能使用 DoT DoH 请求其他 DNS 服务器。(newServer 通通 UDP)
而且最麻烦的是配置多了没有使用也不会提醒错误。
2.2 、启用 DoT 与 DoH
首先绑定端口,同时配置 客户端列表 ACL
-- Bind Address addLocal("0.0.0.0:53") addLocal("[::]:53") addLocal("0.0.0.0:5300") addLocal("[::]:5300") -- Query ACL setACL({'0.0.0.0/0','::/0'})
然后配置运行时的 Buffer 和 缓冲块,以及 请求来源 TCP 的 最大线程数
-- Running Processes setRingBuffersSize(1024000, 100) setMaxTCPClientThreads(8)
创建 DoT/DoH 配置,默认 DoT 使用 853/tcp ,DoH 使用 443/tcp
需要提前准备相应的证书,以及 DoH 的请求 URL 后缀,默认 /dns-query
-- DoT addTLSLocal("0.0.0.0:853", 'fullchain.pem', 'privkey.pem', {provider="openssl"}) -- DoH addDOHLocal('0.0.0.0:443', 'fullchain.pem', 'privkey.pem', "/dns-query") addDOHLocal("127.0.0.1:8053", nil, nil, "/resolve", {reusePort=true})
如果希望实现 curl 请求的 DoH 服务器,则需要一个 Nginx 前端展示,后端直接转发,同时去除证书
dnsdist 不支持 使用 json 格式的 doh,有关信息查阅 [ 链接 ]
location /dns-query { proxy_http_version 1.0; proxy_cache doh_cache; proxy_cache_key $scheme$proxy_host$uri$is_args$args$request_body; proxy_pass http://127.0.0.1:8053; }
最后准备一个缓存,通常来说缓存 65535 条就够了,如果有需求修改 TTL 缓存,则需要扩大此值
默认 newServer 的 Pool 是 "" ,所以缓存需要应用在 getPool("") 上。
-- Cache getPool(""):setCache(newPacketCache(65535, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false}))
2.3 、查看运行状态
-- Console Bind Address controlSocket("127.0.0.1:5300") setKey("console_password") setConsoleACL({'127.0.0.1/32','::1/128'}) -- WebServer webserver("0.0.0.0:8083") setWebserverConfig({password="website_password",apiKey="api_password",acl="10.179.0.0/16"})
2.4 、配置 DNS 来源服务器
上游是 53/UDP 的可以直接请求,QPS 是限制最大并发请求数,Name 是别名
newServer({address="1.0.0.1:53",qps=100,name="CF-DNS-UDP"}) newServer({address="1.1.1.1:53",qps=100,name="CF-DNS-UDP"})
如果考虑 53/UDP 污染的情况,可以使用 DoT/DoH 的方式从来源请求
tcponly 是限定使用 tcp 协议,subjectName 是请求报文的 SNI 值,checkInterval 是周期检查时间
newServer({address="1.0.0.1:443",qps=500,name="CF-DNS-DoH",tcponly=true,tls="openssl",subjectName="one.one.one.one",validateCertificates=true,dohPath="/dns-query",useClientSubnet=true,checkInterval=10,checkTimeout=3000}) newServer({address="1.1.1.1:443",qps=500,name="CF-DNS-DoH",tcponly=true,tls="openssl",subjectName="one.one.one.one",validateCertificates=true,dohPath="/dns-query",useClientSubnet=true,checkInterval=10,checkTimeout=3000})
newServer({address="1.0.0.1:853",qps=500,name="CF-DNS-DoT",tcponly=true,tls="openssl",subjectName="one.one.one.one",validateCertificates=true,checkInterval=10,checkTimeout=3000}) newServer({address="1.1.1.1:853",qps=500,name="CF-DNS-DoT",tcponly=true,tls="openssl",subjectName="one.one.one.one",validateCertificates=true,checkInterval=10,checkTimeout=3000})
服务器之间的轮询方式,默认是顺序轮询,可以修改为 qps 均值轮询
-- QPS
setServerPolicy(firstAvailable)
2.5 、 EDNS 携带用户源地址
当我们希望用户请求得到的 DNS 是他们最近的服务器时,我们就需要携带用户源地址
这种请求方式经常会在 CDN 中体现,并且大多数隐私保护限制了携带位数。
-- EDNS 截断用户源地址的位数 setECSSourcePrefixV4(24) setECSSourcePrefixV6(64)
-- 执行 EDNS 查询时,保留查询者的 IP 信息并传递给来源服务器 newServer({address="8.8.8.8",useClientSubnet=true})
2.6 、安全防护
如果不限制用户请求数,那么 DNS 服务器很容易被打瘫
-- Limit
local dbr = dynBlockRulesGroup()
dbr:setQueryRate(100, 10, "Exceeded query rate", 60)
dbr:setRCodeRate(DNSRCode.NXDOMAIN, 20, 10, "Exceeded NXD rate", 60)
dbr:setRCodeRate(DNSRCode.SERVFAIL, 20, 10, "Exceeded ServFail rate", 60)
dbr:setQTypeRate(DNSQType.ANY, 5, 10, "Exceeded ANY rate", 60)
dbr:setResponseByteRate(100000, 10, "Exceeded resp BW rate", 60)
dbr:excludeRange({"192.168.0.0/16","fc00::/16"})
function maintenance()
dbr:apply()
end
- 10 秒内,请求数超过 100,则触发 60s 封禁,提示 "Exceeded query rate"
- 10 秒内,请求结果中包含 NXDOMAIN 的超过 20,则触发 60s 封禁,提示 "Exceeded NXD rate"
- 10 秒内,请求结果中包含 SERVFAIL 的超过 20,则触发 60s 封禁,提示 "Exceeded ServFail rate"
- 10 秒内,请求类型中包含 ANY 的超过 5,则触发 60s 封禁,提示 "Exceeded ANY rate"
- 10 秒内,回应总字节超过 100Kb ,则触发 60s 封禁,提示 "Exceeded resp BW rate"
- 白名单放行 192.168.0.0/16 和 fc00::/16 不进行限制。
由于 DNS 是基础服务之一,一旦出现安全隐患影响极大,所以会周期的检查最新版本
如果我们自己内部使用,则不需要频繁的检查,所以可以关闭安全更新。
-- 禁用安全更新通知
setSecurityPollSuffix("")
2.7 、服务器状态检查
newServer({address="1.1.1.1", healthCheckMode='lazy', lazyHealthCheckMinSampleCount=10, lazyHealthCheckThreshold=30, lazyHealthCheckSampleSize=100, checkInterval=10, checkTimeout=3000, rise=2, maxCheckFailures=3, lazyHealthCheckFailedInterval=30, lazyHealthCheckMaxBackOff=3600, lazyHealthCheckMode='TimeoutOnly'})
- 首次运行,前 lazyHealthCheckMinSampleCount=10 个包什么都不做
- 每当处理 lazyHealthCheckSampleSize=100 个包中有 lazyHealthCheckThreshold=30 个包出现问题时,视为服务器连接状态出现问题
- 当认为出现问题时,每 checkInterval=10 s 发送一个检查包,每个包的超时时间为 checkTimeout=3000 ms,发送 rise=2 次后依然不可用认为此服务器故障,标记为 down
- 当服务器被标记为 down 时,每 lazyHealthCheckFailedInterval=30 s 周期性进行服务器状态检查
- 当服务器被标记为 down 超过 lazyHealthCheckMaxBackOff=3600 s 时,服务器重新视为 up 状态。
- 服务器状态检测的内容为 TimeoutOnly 仅超时,还可以设置其他内容。
2.8 、测试服务器响应
配置完毕后想测试,可以使用以下测试命令(apt 安装包名分别为 curl 和 getdns-utils)
getdns_query @1.0.0.1~cloudflare-dns.com -m -s -L -A www.twitter.com
curl --doh-url https://1.0.0.1/dns-query https://www.twitter.com
curl -H 'accept: application/dns-json' 'https://1.0.0.1/dns-query?name=www.twitter.com&type=A'
推荐使用一个 DNS 压力测试小工具 dnsperf [ 链接 ],DNS 测试列表参考 [ 链接 ]
dnsperf -s 1.1.1.1 -p 53 -f inet -m udp -d opendns-top-domains.txt -c 100 -T 4 -t 30 -n 1 -l 10 -D -q 100 -Q 200