国内主要公共 DNS 支持 ECS 情况测试 – 20210315

本次测试仅通过黑盒测试分析不同公共 DNS 当前(20210315)是否支持 ECS,及方案上的一些差异,相关方案的优劣不进行额外说明。

结论

厂商IP是否支持ECS
腾讯云/DNSPod119.29.29.29、119.28.28.28
阿里223.5.5.5、223.6.6.6
DNS派101.226.4.6、123.125.81.6等
OneDNS117.50.11.11、52.80.66.66
114114.114.114.114、114.114.115.115
CNNIC1.2.4.8、210.2.4.8
百度180.76.76.76

细节差异简析

腾讯云/DNSPod

  1. 绝大部分后端递归节点是支持 ECS 的节点,少部分递归节点为不支持 ECS 的节点;其中不支持 ECS 的节点仅会对本省本运营商的请求进行递归, 支持 ECS 的递归节点可为所有线路进行递归。
  2. 支持 ECS 的递归节点携带用户的实际 IP 作为 ECS IP 向权威 DNS 进行请求,为防源 IP 泄漏,统一格式化为 x.x.x.1/32。
  3. 缓存层按照省份运营商线路(如广东电信)进行缓存,减少缓存量。

阿里

  1. 部分后端递归节点是支持 ECS 的节点,部分递归节点为不支持 ECS 的节点;;其中不支持 ECS 的节点仅会对本省本运营商的请求进行递归, 支持 ECS 的递归节点为未部署后端递归节点的线路进行递归。
  2. 支持 ECS 的递归节点不会携带实际用户的 IP 向权威 DNS 请求,而是携带相关线路固定的 IP 作为 ECS IP 向权威 DNS 进行请求,并格式化为 x.x.x.0/24,目的可能是为了减少递归 DNS 节点的缓存量。
  3. 中国移动线路在未选择到后端递归节点的省份中,似乎都选择了相同的一个移动 IP 作为 ECS IP,没有为不同省份选择不同的 ECS IP。
  4. 缓存层按照省份运营商线路(如广东电信)进行缓存,减少缓存量。

DNS 派

  1. 绝大部分后端递归节点是支持 ECS 的节点,少部分递归节点为不支持 ECS 的节点。
  2. 支持 ECS 的递归节点携带用户的实际 IP 作为 ECS IP 向权威 DNS 进行请求,为防源 IP 泄漏,统一格式化为 x.x.x.0/24。
  3. 无缓存的应答结果可能返回多个 ECS 段。
  4. 缓存层按照省份运营商线路(如广东电信)进行缓存,减少缓存量。
  5. 海外转发至其他公共 DNS,如 CloudFlare 的 1.1.1.1。

OneDNS

  1. 本身的后端递归节点不支持 ECS,部分省份运营商会转发至腾讯云/DNSPod 的公共 DNS 进行解析。
  2. 海外请求全部转发至腾讯云/DNSPod 的公共 DNS 进行解析。

其他

  1. 测试的114、CNNIC、百度等公共 DNS 暂不支持 ECS。
  2. 支持 ECS 的公共 DNS 部分不支持 ECS 的递归节点不一定是真的不支持,不排除 DNS 请求被重定向的可能。

Nginx TCP 多证书透明代理及 Linux/F-Stack(FreeBSD) 路由相关设置

某个 TCP 服务对外有多个域名提供相同的服务,且每个域名都是基于 TLS 的,需要通过 Nginx 对 TLS 进行卸载后转发到实际的上游服务,且上游服务必须使用客户端的源 IP,所以 Nginx 需要使用透明代理。分别需要对Nginx 和系统路由进行配置。

Nginx 配置

需要 Nginx 1.15.9 以上版本,简化配置如下所示,

stream {       
  upstream up_server {
      server 192.168.1.3:8081;
  }
   
  # 通过 map 配置不同域名(SNI)使用不同的证书文件
  # 证书为泛解析证书, 匹配泛解析域名
  # 会降低性能
  map $ssl_server_name $targetCert {
      ~*domain1.com$ /usr/local/cert/domain1.crt;
      ~*domain2.com$ /usr/local/cert/domain2.crt;
      ~*domain2.com$ /usr/local/cert/domain3.crt;
      default /usr/local/cert/domain1.crt;
  }
   
  map $ssl_server_name $targetCertKey {
      ~*domain1.com$ /usr/local/cert/domain1.key;
      ~*domain2.com$ /usr/local/cert/domain2.key;
      ~*domain2.com$ /usr/local/cert/domain3.key;
      default /usr/local/cert/domain1.key;
  }
   
  server {
      listen 8080 ssl reuseport;
   
      ssl_certificate $targetCert;
      ssl_certificate_key $targetCertKey;
      ssl_protocols TLSv1.2 TLSv1.3;
      ssl_session_tickets off;

      proxy_pass up_server;
      proxy_bind $remote_addr transparent; # 透明代理
  }
}

Linux 系统路由配置

因为透明代理的源 IP 是实际客户的 IP,在实际服务接受处理完响应包返回时会返回给实际的客户 IP,所以需要配置将回包发到 Nginx 进行处理,这里的上游服务为本机服务,需进行如下配置。如上游在其他服务器上,可以查看参考资料中文章并进行对应配置。

# 新建一个 DIVERT 给包打标签
iptables -t mangle -N DIVERT;
iptables -t mangle -A DIVERT -j MARK --set-mark 1;
iptables -t mangle -A DIVERT -j ACCEPT;

# 把本机 TCP 服务的回包给 DIVERT 处理
iptables -t mangle -A OUTPUT -p tcp -m tcp --sport 8081 -j DIVERT

# 有标签的包去查名为 100 的路由表
ip rule add fwmark 1 lookup 100

# 100的路由表里就一条默认路由,把所有包都扔到lo网卡上去
ip route add local 0.0.0.0/0 dev lo table 100

F-Stack(FreeBSD) 路由配置

F-Stack(FreeBSD) 上游回包路由配置,

# upstream 为本机时
# 假设 f-stack-0 的 IP 为 192.168.1.3,将 upstream 往外发的所有出包都转发到 F-Stack Nginx 监听的 IP 和端口即可
# 因为转发到本机地址时目的端口会被忽略,可不设置端口
ff_ipfw add 100 fwd 192.168.1.3,8080 tcp from 192.168.1.2 8081 to any out

# upstream 为其他机器时
# 将 upstream 通过设置网关或者 IP 隧道(需额外进行隧道配置)等方式发过来的所有入包都转发到 F-Stack Nginx 监听的 IP 和端口即可
# 因为转发到本机地址时目的端口会被忽略,可不设置端口
ff_ipfw add 100 fwd 192.168.1.3 tcp from 192.168.1.2 8081 to any in via f-stack-0

参考资料

  1. nginx TLS SNI routing, based on subdomain pattern
  2. 使用nginx的proxy_bind选项配置透明的反向代理
  3. [sslh] Using sslh transparent proxy on FreeBSD?

过去的2020

又到了本命年,过去的一年大事发生了很多事,不管是疫情、水灾、航天、矿难,虽一直在默默关注,但个人 能做的有限,还是只简单总结下自己过去的一年吧。

家庭关系好多了,前两年陪孩子太少,现在陪的多了,明显亲密多了,当然想达到孩他妈的程度还是差远了。另外发生了一件比较伤心伤身的事情,孩他妈辛苦了。 身体急剧变差,上半年疫情严重时期长时间在家呆着一直不出门,吃得多,运动少,工作也太拼,造成整个人持续失眠、焦虑、压力大、暴躁易怒、体重剧增20斤,基本每天都是忙到凌晨,躺下睡不着,经常要差不多天亮才能睡着。下半年对工作和生活作息做了很大的调整,精神状态算是慢慢好了不少,身体又出毛病了,经常性的头疼、颈椎疼、腰疼,尤其是腰,弯一会能疼一天,还好各种检查虽然有点小异常,但没啥大问题,经过多休息、调整坐姿站姿少做剧烈运动,换了个有泳池的健身房,大幅减少了跑步,游泳占了运动的一多半,到年底算是有好转,没那么严重了。当然也有好消息,下半年把20斤的体重又减下去了,回到了去年底的水平。

工作上两个主要产品权威DNS和公共DNS/HTTPDNS各开发发布了一个大版本,以及N个小功能小版本,遗憾的是F-Stack未能抽出时间去做一直计划要做的一些大改动,只有一些小改动,发布了一个小版本。21年还会有各自的大版本开发发布,也会在F-Stack上投入更多一点精力。另外因为整个人的精神状态太差,在工作中明显会出现多次急躁不耐烦,语气有时候比较冲的情况,向所有人说句对不起了,会努力调整自己的状态。

IOS14对使用8.8.8.8进行DNS(CDN)调度的影响

2023-12-21更新:虽然8.8.8.8对MX、CAA以及其他类型等依然不支持ecs(符合RFC标准),但是在cname链中已经不会对A/AAAA等其他记录类型造成影响,看上去像是单独处理了,已经修复了该问题。

但是依然存在ecs失效的可能,比如8.8.8.8的递归到权威的请求偶发超时、请求权威跨国且请求了同权威IP被污染的域名等场景,依然可能造成递归判断权威的IP不支持ecs,导致后续一段时间内的ecs失效,暂无太好的办法。


2021-04-02 更新: Google 公共 DNS 已经对 HTTPS 和 SVCB 支持了 ECS,其他类型正在评估,测试正常。


2021-03-26 更新:标准化组织正在对该问题进行讨论,大体方案可能为:

  1. 如果递归 DNS 准备解析 HTTPS 记录指向的域名,并且在返回给客户端的应答附加段中添加 A/AAAA 记录,则递归在向权威请求HTTPS 记录时应携带 /0,表示递归支持 ECS,但对 HTTPS 记录类型禁用,此时权威在响应时不能在附加段中添加相关 A/AAAA 记录,以便递归 DNS 携带正常的 ECS 进行 A/AAAA 记录的请求。
  2. 如果递归 DNS 不准备执行上面的方案,则递归在向权威进行 HTTPS 类型的请求时应携带与请求 A/AAAA 类型相同的 ECS。

方案1实现较复杂,递归 DNS 需要对 HTTPS 记录类型进行单独处理,此处也要注意 HTTPS 记录类型也包括对应的 CNAME,但是可以减轻缓存压力;方案2简单粗暴,对应增加的就是递归 DNS 的缓存压力。

具体信息可以参考: https://issuetracker.google.com/issues/181593657https://github.com/MikeBishop/dns-alt-svc/pull/308


我们一直的观点是在国内不要使用 Google 的公共 DNS(8.8.8.8),这里再从技术层面讨论下 IOS 14 更新后使用 8.8.8.8 进行 DNS 解析对 CDN 调度的影响。

结论

  1. 在国内任意地区都不应该系统上直接设置或转发到 8.8.8.8 进行 DNS 解析,这个结论依然有效。
  2. IOS 14 更新后会对域名自动发出 TYPE HTTPS (TYPE65) 类型的请求,这会导致 8.8.8.8 对于包含 CNAME 记录的解析结果 ECS 判断失效的情况极大增多,调度不准确的情况会远多于 IOS 14 更新之前,CDN 调度受影响比较严重。
  3. 仅限于当前时间(2021.03.04)有效,如果后续相关 DNS 请求行为有变化,则结论可能不再准确。

解析过程

公共 DNS 的解析准确度一般是通过在尽量多的地区和运营商部署递归节点,以及是通过 ECS (EDNS Client Subnet,常简称为 EDNS) 技术来提示解析准确度,最主要的使用场景就是 CDN 的调度,所以一旦 ECS 失效,对 CDN 调度的影响也是最大的。

虽然任何 DNS 都不能保证完全解析准确,但是此前 8.8.8.8 做的还是不错的,但是从 20 年 IOS 14 更新后,解析准确度每况愈下,尤其是对部分使用 CDN 进行调度的域名影响较大,经常发生国内解析到国外、不同地区使用多家 CDN 时解析结果不符合预期等问题,下面分析下 IOS 14 对该现象的具体影响过程。

假设在权威 DNS 上对typehttps.example.com配置以下记录, 并且权威 DNS 对 ECS 的支持正确,8.8.8.8 的自动检测也可以正确检测到权威 DNS 是支持 ECS 的。

typehttps   默认  600 CNAME   test1.example.com.
typehttps   境外 600 CNAME   test2.example.com.
test1       默认 600 A       8.8.8.8
test2       默认 600 A       8.8.4.4

当我们从国内向 8.8.8.8 请求 typehttps.example.com或携带国内IP(如 119.29.0.0/24)的 ECS IP 时,正常过程是这样的,

dig @8.8.8.8 typehttps.example.com +subnet=119.29.0.0/24

正常的结果应该是类似这样的,因为权威 DNS 是根据 8.8.8.8 请求时携带的119.29.0.0/24进行解析的,

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 119.29.0.0/24/24
;; QUESTION SECTION:
;typehttps.example.com.           IN     A

;; ANSWER SECTION:
typehttps.example.com. 550 IN CNAME   test1.example.com.
test1.example.com       600 IN A       8.8.8.8

但是现在你有很大概率拿到会是以下的错误结果,

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 119.29.0.0/24/24
;; QUESTION SECTION:
;typehttps.example.com.           IN     A

;; ANSWER SECTION:
typehttps.example.com. 590 IN CNAME   test2.example.com.
test1.example.com       600 IN A       8.8.4.4

解析到了境外的 CNAME 及其 IP,以前虽然也可能出现这种解析错误的情况,但是相对少很多,那是什么原因呢,此时如果我们指定请求 CNAME 类型再来看下有什么区别,

dig @8.8.8.8 typehttps.example.com +subnet=119.29.0.0/24 CNAME
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 119.29.0.0/24/0
;; QUESTION SECTION:
;typehttps.example.com.           IN     CNAME

;; ANSWER SECTION:
typehttps.example.com. 590 IN CNAME   test2.example.com.

此时可以看到 ECS 段中的 scope 为 0,这就表示 8.8.8.8 的缓存中将境外线路的 CNAME 记录作为全局结果缓存了,那为什么会出现这种错误的解析和缓存结果呢?

通过抓包分析可以发现,8.8.8.8 对权威 DNS 发起的查询请求中,即使权威是支持 ECS 的,8.8.8.8 也并不会对所有类型的请求记录携带 ECS 段,大部分常见的请求类型,如 A 记录(IPv4)、AAAA 记录(IPv6)、CNAME 记录等并不会出现问题,但是有部分请求类型则不会携带 ECS 进行请求,而这其中有部分类型可能会返回 CNAME 记录,比如 MX 、CAA 以及 HTTPS 类型,权威 DNS 就会按照 8.8.8.8 的递归 DNS 的 IP 所在地返回境外线路的 CNAME 记录,而后 8.8.8.8 就会按照全局 /0 的 scope 缓存该 CNAME。

所以这里就破案了,具体过程是如下两图所示,

A 记录请求过程
HTTPS 记录请求过程
  1. IOS14 升级后会支持 TYPE HTTPS 的记录类型,解析一个域名时应同时请求 A/AAAA/HTTPS 记录类型。
  2. 则 A 和 AAAA 记录可以正常获得境内的 CNAME 和 IP 并正常按照 119.29.0.0/24 的 IP 段缓存,但该缓存仅对该 IP 段有效.
  3. HTTPS 类型的返回的错误 CNAME 结果就会因为上述原因作为全局 /0 的缓存。
  4. 后续所有非119.29.0.0/24网段 请求都会命中全局缓存的错误 CNAME 记录,然后再对 CNAME 记录进行正常的递归查询,客户端最后拿到的就是错误的境外结果。
  5. 虽然 MX 、CAA、HTTPS 类型都会造成此问题,但是在实际的网络环境中,普通域名的 MX 和 CAA 记录类型都是很少被请求到的,所以 8.8.8.8 虽然一直存在类似的问题,但是并不严重。直到 IOS14 更新后, HTTPS 记录类型默认被请求,该问题就变得比较严重了。

影响范围

对不同地区设置了不同 CNAME 记录的域名会有较大影响,一般常见于 CDN,所以对 CDN 影响比较大,具体分析话大概是这样的,

  1. 如果境内和境外使用了不同的 CNAME 记录,如国内使用国内的 CDN,国外使用国外的 CDN,则国内使用 8.8.8.8 会大概率解析到国外线路的 CNAME,很可能解析到国外节点影响访问体验。
  2. 如果未设置其他国家、大洲、境外等线路,只是不同省份运营商使用不同的 CNAME 记录进行负载均衡,则国内使用 8.8.8.8 会大概率会解析到默认线路上,虽然会造成负载均衡效果不符合预期,但在客户端的访问体验上总体影响不大。
  3. 对境外用户影响有限,因为虽然也存在一样的 ECS 失效问题,但根据递归 DNS IP 获取到的 CNAME 结果范围不会差距很大。

规避方法

  1. 在国内避免使用 8.8.8.8,当然这里有部分运营商已经帮忙搞定这一步了,移动端则可以考虑使用 HTTPDNS 等技术手段规避。
  2. 如果条件允许,不同地区应使用相同的 CNAME,如果必须使用不同的CNAME,则需要关注默认和境外相关线路的容量等问题,如境外线路的 CDN 能提供境内 CDN 节点则可以很大的缓解该问题。

其他公共DNS

  1. OpenDNS 208.67.222.222 仅 CAA 记录可能存在此问题;腾讯云/DNSPod 119.29.29.29,各请求类型均不受影响;阿里云 223.5.5.5 不受影响。
  2. 1.1.1.1、9.9.9.9、180.76.76.76、114.114.114.114,默认未支持 ECS ,仅能通过后端递归节点的部署进行调度,不涉及该问题。

参考资料

draft-ietf-dnsop-svcb-httpssvc-03.