相信大家都接触过openwrt,从几年前仅4M的flash就能刷openwrt的tplink路由器,到现在大家玩的软路由,openwrt都在HomeLab里面扮演着“智能路由器”的角色,一个路由器能干的事情也越来越多,像是DNS、各种科学的插件、远程下载、甚至塞上docker等等。
而写这个系列探讨的事情恰恰相反,为了避免ALL IN BOOM的结局,追求最棒的性能和稳定性,在主路由器只当纯粹的路由器,很硬的硬路由的前提下,如何搭建局域网内的各种服务,实现更HiFi的网上冲浪。
或者说,在企业级的环境下,如何实现更好的“优化”方案,毕竟企业级的路由器就真的是路由器。实际上,方案也经过了实际的千人以上企业级环境使用验证,很久之前就想整理出来写文章。缺点也很明显,如果你是ALL IN BOOM的软路由用户,要低碳低功耗,那这个文章系列可能不太适合你的需求。
为啥需要递归DNS
谈起DNS,相信很多折腾软路由都不陌生,大家可能为了一个“最佳的DNS”想尽了各种办法,比如收集各种公共DNS服务器,对他们进行测速,看看哪个快;又或者使用类似SmartDNS这种工具,把返回的结果进行再测速。但无论如何,你查询的DNS的路径都是这样的(以查询ipsu.03k.org
为例):
那这个结果又是怎么来的呢?当然是114作为一个公共DNS服务器,帮你查询了权威DNS服务器(指域名所在的DNS托管服务器,用nslookup查询提示的非权威应答就是这个意思)得到的结果,而114这种公共DNS服务器我们把他叫做“递归DNS”服务器。他的查询过程是这样的:
如果你想知道114用了什么IP来帮你查询权威服务器,你可以运行下面的命令:
或者,你可以使用03k.org的服务:
以上过程只是一个简单的示例,实际上如果你还有CNAME记录套娃的话,这个查询过程也就一层层套下去了,如果你有一个畅通无阻的网络,你可以安装dig命令,使用dig +trace 域名
命令来执行一次原生的查询过程
~ dig +trace alexuhui.win
; <<>> DiG 9.10.6 <<>> +trace alexuhui.win
;; global options: +cmd
. 304 IN NS f.root-servers.net.
. 304 IN NS l.root-servers.net.
. 304 IN NS d.root-servers.net.
. 304 IN NS j.root-servers.net.
. 304 IN NS a.root-servers.net.
. 304 IN NS b.root-servers.net.
. 304 IN NS e.root-servers.net.
. 304 IN NS g.root-servers.net.
. 304 IN NS c.root-servers.net.
. 304 IN NS h.root-servers.net.
. 304 IN NS i.root-servers.net.
. 304 IN NS k.root-servers.net.
. 304 IN NS m.root-servers.net.
;; Received 251 bytes from 114.114.114.114#53(114.114.114.114) in 18 ms
win. 172800 IN NS a.nic.win.
win. 172800 IN NS b.nic.win.
win. 172800 IN NS c.nic.win.
win. 172800 IN NS ns1.dns.nic.win.
win. 172800 IN NS ns2.dns.nic.win.
win. 172800 IN NS ns3.dns.nic.win.
win. 86400 IN DS 29737 8 2 980F7CD5E938E5ACAA28B0B9A3A1504AC3839B3919CA6FDF9C352367 5D0FE875
win. 86400 IN RRSIG DS 8 1 86400 20250116050000 20250103040000 26470 . ZkWjiqn0e/ANc6lV548PTdFu3Q40VycjLZy0RGCw0KR9xkOgjvFXUC2p XPKHVBjCjlMQv11WeNXbd/+6yvZBa9xh6Jo6pN2BV73tD4U0nmf+wsXh rK0vDk0EzsDtxGejTyAzrcV/LG9iyH2MSIAzLnsSvXDB411Wmla+axcC g7NLqmRIUDc6o+mGc+9xXzB4T5K9pJayEatiB0PdJeJR9l14tGetBA8S HfceSFJuG7K/6GtchDu3RY15lfyg3untzo1fESsGEMZmgDi1W5p/JYWt fQSFxfzw9+bETAhfCyl+FiZI2DsECHJp2SPjrTLvxHge5E1EBYFlZMAp 0S+XmQ==
;; Received 750 bytes from 192.203.230.10#53(e.root-servers.net) in 150 ms
alexuhui.win. 3600 IN NS dns23.hichina.com.
alexuhui.win. 3600 IN NS dns24.hichina.com.
240qehmhc9uc2ndn31l6c7itp4d4npug.win. 900 IN NSEC3 1 1 0 - 248LMDHVDNVR1N37DIK6000MR25EQFGI NS SOA RRSIG DNSKEY NSEC3PARAM CDS CDNSKEY
reucosulpdcu2efe9ot2f4em5cboujk1.win. 900 IN NSEC3 1 1 0 - RG28PMBFFC9QAG1LDOO1DS6S9EMIDDVG NS DS RRSIG
240qehmhc9uc2ndn31l6c7itp4d4npug.win. 900 IN RRSIG NSEC3 8 2 900 20250115230006 20250101221919 16128 win. Xki5gOUH4DAKfpcTC2k5BQEPLtowBy+sk1C42X5TyEChAbcwiygPKXHi Rr7NvWIKbfUD/SBqn60fcN5MFGpROuLyQKqg8YpAOz+Q8pWICZpmsBgF VvjwNKyLHI97pqLKDGL0pqQQBHhWC1xLPto6Biu9RCykmGYwm7EvFRvs s41pFhb12YLAKx+lkyfVPKKs5USS0U5e23HucKZU1mHZ7w==
reucosulpdcu2efe9ot2f4em5cboujk1.win. 900 IN RRSIG NSEC3 8 2 900 20250117011356 20250103005233 16128 win. VvGu2oltpbWD1puP/TCBniLAy6makhcxvWuKudNbM1P/zWenQTgnaWBW BIHu1+ieFCL9InXaJ3voGuzAdcEBlONW4kscj6m9cadwFskXkXOjp854 msQPqY2mVnLCNeiEsujmqUYWZGjIrcdzlXVdyyOOuCbHfQ8yzSdqvS6h WouU9i9jl2VRV38i6/h5e+qsIyj7jWLKLziXyBNOX4xeFQ==
;; Received 642 bytes from 156.154.159.182#53(ns3.dns.nic.win) in 11 ms
alexuhui.win. 86400 IN A 138.2.56.147
;; Received 57 bytes from 47.118.199.220#53(dns23.hichina.com) in 17 ms
可以看到,这个过程比起直接查询公共DNS服务器,需要走更多的“路径”,而最后一行也就是权威服务器给的应答,dig也给出每一步所需要的耗时ms,前后加起来也有1秒多了,这也是为什么会有公共DNS服务器的存在,因为如果大家都这么查询的话,上网冲浪实在是比天翼3G还慢。
那既然大家都用公共DNS的话,选择哪个DNS有什么区别吗?
首先我们不考虑客户端到公共DNS服务器之间的延迟这个问题,就返回的解析结果来考虑,不同的DNS服务器可能返回不一样的结果。一个常见的场景是,很多域名使用了CDN,解析结果根据所在地理IP来返回,比如你是广东电信,返回在广东电信的服务器地址,而在北京联通就返回北京联通的地址,尽管公共DNS服务器一般都有Anycast(同一个IP有很多台服务器),DNS服务器的地理位置也可能不尽相同,IP的精度也不能面面俱到,有时候结果还不如当地运营商的公共DNS。虽然有ECS这种协议存在(DNS ECS是DNS协议的一个扩展,它允许递归DNS解析器在发送给权威DNS服务器的请求中包含终端用户IP地址数据的部分),但首先它是用在递归DNS上的,也就是部署在114这种公共服务器上的.
其次你请求的域名所在的权威DNS要支持ECS协议才可以,说白了这个协议对局域网用户来说没什么用(因为你什么都做不了)。
那么有什么完美的DNS服务器呢?答案是我们可以拥有属于自己的递归DNS服务器,说白了就是把114这样的DNS服务器装你家里。这样你的每个请求都非常的原生地到达了权威DNS服务器,获取的结果可谓是准确中的准确。你再也不需要对DNS服务器进行收集和测速,也不需要对解析结果进行测速(实际上,你已经获取了原生的准确解析结果了,测它没有什么意义,多个DNS解析结果本来就是为了能DNS轮询负载均衡故障转移,比如在一个大局域网大家都用同一个IP连接视频播放地址可能就不是一个好主意),实在是强迫症治愈良药。
除了以上原因,一个不太常见的问题就是,你觉得公共DNS服务器值得信任吗?在不考虑DNS解析结果被运营商劫持的情况下,你怎么能保证它返回的结果值得信任?毕竟你不是直接问的权威DNS服务器,自然结果也是凭良心了(虽然有DNSSEC这种东西存在,但它并不能解决DNS劫持和污染,你除了知道它结果可能是错的之外什么都做不了)。即使DNS结果值得信任,但你的查询记录可能会被公共DNS服务器记录日志,四舍五入你上了什么网站,什么时间上的,IP地址是什么,这些都可以入了公共DNS的大数据系统,也就是可能会造成一定的隐私问题。尽管有的公共DNS澄清自己是干净无记录的,但你怎么能保证他们能遵守诺言呢?是的,你不能保证。一个更加离谱的可能是,如果公共DNS服务器被攻击,那你的查询结果可能被引导到恶意网站,就算这种可能性比较低,公共DNS服务器也有故障的时候,你可能又开始考虑:我能保证谁的公共DNS服务器100%稳定性?是的,你不能保证。但当你拥有一台属于自己的递归DNS服务器的时候,稳定性和隐私问题将由你自己掌控。
如何搭建递归DNS服务器
这里推荐使用unbound作为递归DNS服务器程序,理由如下:
unbound是一个老牌安全的开源递归DNS服务器,主要由NLnet Labs,VeriSign Inc.,Nominet开发。它可以直接查询权威的DNS服务器,而不需要依赖其他上游DNS服务器,稳定性经过长久验证。
unbound支持redis缓存(redis版本要编译安装),性能表现优异,曾经对比过多款递归DNS服务程序,从压力测试结果来看unbound是非常不错的选择,如果说哪个DNS服务的缓存性能和算法值得信赖,unbound肯定是其中之一(well,没错我说的就是GitHub上各种奇奇怪怪的智能DNS服务的缓存会出现各种奇奇怪怪的bug)。
具有完美可控的缓存和预读取功能,DNS记录可以通过redis持久化和接近ttl过期的时候进行预读取,即使过期也能通过返回一个低值ttl的旧记录后秒刷新,实现无感知的“网页秒开”,当然,你内存越大能存的记录越多体验越好啦(不过一般来说就算是重度企业使用能存下2G也很厉害了)。
unbound支持完整的DNS协议实现,能保证各种结果的准确性和其他可能用不上的高级自定义。
具体的搭建可以参考官方文档:
https://unbound.docs.nlnetlabs.nl/en/latest/
以及优化指南:
https://www.nlnetlabs.nl/documentation/unbound/howto-optimise/
看到这里你是不是开始头疼了,要编译unbound,还要搞清楚一堆参数配置,还要配置redis,如果你在China大陆,你还得考虑境外DNS防污染的问题,实在头疼。
Well,这就是接下来要说的重点,为了能一键部署,我做了个docker镜像,一次满足你所有需求~!
PaoPao DNS docker
泡泡DNS是一个能一键部署递归DNS的docker镜像,它使用了unbound作为递归服务器程序,使用redis作为底层缓存,此外针对China大陆,还有智能根据CN分流加密查询的功能,也可以自定义分流列表,可以自动更新IP库,分流使用了mosdns程序,加密查询使用dnscrypt程序,针对IPv4/IPv6双栈用户也有优化处理。
为啥叫泡泡DNS,因为部署DNS就像吹泡泡一样简单(其实我写docker的时候刚好在看《泡泡》)。
Github 项目地址: https://github.com/kkkgo/PaoPaoDNS
更新日志 以下文档说明以Github最新文档为准
docker镜像: sliamb/paopaodns
泡泡DNS适合的使用场景:
场景一:仅作为一个纯粹准确的递归DNS服务器,作为你其他DNS服务程序的上游,替代
114.114.114.114
,8.8.8.8.8
等公共DNS上游场景二:作为一个局域网内具备CN智能分流、解决污染问题和IPv6双栈优化的DNS服务器,或者你的局域网已经从IP层面解决了“科学”的问题,需要一个能智能分流的DNS服务器。
使用方法和参数说明
简单来说,那么你可以运行:
如果你需要容器运行在同一个局域网段而不是单独映射端口,除了一些NAS有现成的界面点点点,原生docker你可以考虑使用macvlan如下的配置(假设你的网络是192.168.1.0/24):
如果你的网络端口没有冲突,也可以考虑使用docker host网络模式以获得最佳性能。
如条件允许建议使用docker compose部署
如果你的网络环境访问Docker Hub镜像有困难,可以尝试使用public.ecr.aws镜像:
示例:
docker pull public.ecr.aws/sliamb/paopaodns
示例:
docker run -d public.ecr.aws/sliamb/paopaodns
验证你的递归DNS正常运行(假设你的容器IP是192.168.1.8),可以执行以下命令:
或者,你可以使用03k.org的服务:
如果返回的IP和你宽带的出口IP一致的话,说明你的递归DNS服务正常运作了。
搭建完请简单验证所有DNS组件是否工作正常:
同时你可以查阅更新日志的最新版本公告时间,检查输出的镜像版本时间是否大于等于当前最新版本。
需要注意的是,如果你的网络有“自动分流IP”的功能,请把容器的IP加入不分流的名单,因为权威DNS需要准确的IP去判断,IP分流会影响权威DNS的判断。此外,一些软路由存在劫持DNS请求的情况,解决办法参见这个issue。
[DNS hijack]DNS劫持算是经常问的高频问题了,请参考
docker的运行家用建议内存2G,企业环境建议16G,再大了意义不太大(实际内存占用差不多达到4G已经很厉害了,当然,内存越大性能越好,命中率体验更棒),容器启动的时候会根据可用内存调整配置文件参数,占用内存不会超过上限。根据Github的使用者反馈,有最低512M内存的ARM路由器流畅使用的案例。实际上,在最低参数启动下的缓存也是能满足家庭使用需求的。
直接拉取镜像运行即可,你需要挂载容器的/data
目录,该目录会存放所有配置文件、DNS缓存文件和更新的数据,容器重新启动的时候也会直接读取。如果你是在隔离的网络中运行的容器,你需要映射53端口出来,需要注意的是你的宿主也可能运行着DNS服务,跟53端口冲突,这个需要你自己解决(比如禁用自带的DNS服务)。当然,最佳的运行网络环境是跟你的客户端在同一个网段设置一个静态IP,原生docker的话可以使用macvlan网络实现,或者某些NAS提供了docker的桥接到物理网卡的点点点选择(如果你要写docker compose的话,威联通的NAS的桥接网络驱动类型是qnet)。
附赠:PaoPao-Pref
这是一个让DNS服务器预读取缓存或者压力测试的简单工具,配合PaoPaoDNS使用可以快速生成redis_dns_v2.rdb
缓存。从指定的文本读取域名列表并调用nslookup命令查询记录,docker镜像默认自带了全球前100万热门域名。
详情:https://github.com/kkkgo/PaoPao-Pref
相关项目:PaoPaoGateWay
PaoPao GateWay是一个体积小巧、稳定强大的FakeIP网关,支持Full Cone NAT
,支持多种方式下发配置,支持多种出站方式,包括自定义socks5、自定义yaml节点、订阅模式和自由出站,支持节点测速自动选择、节点排除等功能,并附带web面板可供查看日志连接信息等。PaoPao GateWay配合PaoPaoDNS的CUSTOM_FORWARD
功能就可以完成简单精巧的分流。
详情:https://github.com/kkkgo/PaoPaoGateWay
附录:使用到的程序
unbound:
redis: https://hub.docker.com/_/redis
dnscrypt:
mosdns:
GEOIP:
原文
地址:https://blog.03k.org/post/paopaodns.html