之前我写过关于使用smartdns将china
和其他地区分流的案例,在正常使用很长时间后,最近出现了查询结果混入被污染的IP的问题。
问题描述
DNS系统架构为:一台adguardhome服务器,上游为smartdns,adg提供去广告、访问控制和日志查询功能,方便排错,smartdns负责分流。
问题是最近出现了被污染的结果,从adg控制台上可以看到是返回了属于fackbook的随机IP及常见的几个污染黑名单IP,但不经常发生,一小时出现数次,也可能出现一段时间后消失。
检查了一下smartdns的配置,其查询流程严格按照如下:
客户端发起查询后,如果符合 -group china
的规则,则发给 223.5.5.5
处理,-group china
规则为felixonmars的DNSMASQ CHINA LIST (项目地址)。-group china
设置了 –exclude-default-group
以确保来自洼地的结果不会污染外溢。
此外我设置了两个私有的DOH服务器(DOH1、DOH2),除了 -group china
外的其他域名全部发给这两个服务器处理。
server 223.5.5.5 -group china -exclude-default-group server-https https://doh1.myserver.com/dnsquery server-https https://doh2.myserver.com/dnsquery
我想可能是某种原因导致DOH查询失败后,以某种形式回落到普通服务器上,进而返回了污染的结果。
将adg中缓存时间降低,以便让其多次尝试解析。实验证明降低了缓存时间后,情况有所好转,出现被污染IP的概率极大下降了,但仍然有小概率出现。
问题原因
去smartdns的github上查询有关默认组(default group)的内容,没有查到smartdns在默认组查询失败后的行为逻辑,但在github论坛上看到这样一条issue:
因为我的DOH服务器本身也需要解析域名(doh1.myserver.com),所以需要设置一个bootstrap DNS 服务器来解析DOH服务器自己的域名:
server 223.6.6.6 -group bootstrap
我的doh1.myserver.com
会单独从223.6.6.6
解析。如果发生一个需要从DOH1服务器上解析的请求example.com,应该是先从223.6.6.6解析到doh1.myserver.com
的IP,再建立到DOH1的https链接,解析example.com。
那么如果在DOH1、DOH2都因为某些原因查询失败的情况下,由于bootstrap组没有-exclude-default-group
,在bootstrap组中的223.6.6.6也会被用来直接查询 example.com,污染结果就是从223.6.6.6来源的。
也就是说,对于:
server 223.6.6.6 -group bootstrap server 223.6.6.6 -group bootstrap -exclude-default-group
smartdns的行为是不同的。在没有 -exclude-default-group
时,对doh1.myserver.com
的查询一定会从-group bootstrap组里的服务器查询,因为命中了server 223.6.6.6 -group bootstrap
。
但对于不在group china组,也不在group bootstrap组里的域名的查询,有可能会使用bootstrap 组的服务器查询。因为-group bootstrap
是让在bootstrap组里的域名一定使用这个组的服务器,但也未禁止其他域名使用这个组的服务器。要禁止其他的域名使用这组服务器,必须加上-exclude-default-group
。
而这个问题以前之所以不明显,是因为最近因为显而易见的原因,DOH服务器可能受到了某种干扰(显然),导致短时间内DOH1和2同时查询不成功,smartdns自动选择了唯一一个可用的服务器(当然只有bootstrap了),污染就这么出现了。
解决方案和提升
解决方案当然很简单,要么禁止其他请求使用bootstrap 组 -exclude-default-group
,要么直接把DOH1和DOH2的IP写死在host里,后者更简单,因为自己的DOH服务器嘛IP肯定不会变,而且还省出了查询时间,速度更快了。
但是,如果DOH1和DOH2同时查询失败,岂不是会直接返回错误?
再回到整个DNS体系里来思考:
后面还有一个adg呢,主要是提供访问控制(如去广告),日志(安全目的)和方便的控制台,其只有唯一的一个上游,上游是smartdns。那可以认为adg实际上就是smartdns的缓存服务器。那smartdns本身就没有缓存的必要了,smartdns的工作主要应该是分流、防污染,其他任务交给adg。
那再进一步思考,smartdns的测速功能也没有意义了。因为不在china的IP,本身也要过代理,测速要么是无代理直连的速度,要么根本不通(IP都墙了你还测个屁呀)。而且测速会造成潜在的安全问题(频繁发出大量ICMP请求,对于特殊国家来说总是潜在隐患),那么关闭测速。
进而,smartdns的缓存也意义不大,cache 可以减少到 512-1024(按实际情况)。既然cache小了,TTL也没必要大,把TTL也减小为1-2分钟(最小TTL1分钟,最大2分钟)。
如果DOH服务器查询失败,只要不是DOH服务器被墙,则过几分钟再查也行,这几分钟里还有adg缓存呢,于是关闭提供过期缓存的功能、关闭持久化缓存。
向下游adg提供的结果TTL1分钟。
增加一组私有DOH服务器提升稳定性(没有也可以用公共的,不过估计都被墙完了)
cache-size 512 cache-persist no prefetch-domain yes serve-expired no serve-expired-ttl 3600 speed-check-mode none rr-ttl-min 60 rr-ttl-max 120 rr-ttl-reply-max 60 log-level warn server 223.6.6.6 -group bootstrap -exclude-default-group #这里也可以直接写死IP server 223.5.5.5 -group china -exclude-default-group server 114.114.114.114 -group china -exclude-default-group server-https https://doh1.myserver.com/dnsquery server-https https://doh2.myserver.com/dnsquery server-https https://doh3.myserver.com/dnsquery
而缓存的任务则交给adg:
缓存大小1024000为最多缓存2000个结果(每个DNS结果为512字节),如果最多缓存1000个就是512000。
最小TTL 1分钟,最大2分钟(保持和smartdns一致)
乐观缓存为开启过期缓存,假设smartdns查询失败,还能从过期缓存中提取结果。(而smartdns就要关闭过期缓存,以便重复查询)。