Browsed by
Tag: dns

dnsmasq + stubby

dnsmasq + stubby

前段时间因为 DNS 污染(或者拦截)的原因,在路由器中改成配置 DoT,问题得到了极大改善,上网速度和质量很明显好很多。但是还依然存在一些让人困惑的瑕疵:(1)有时候 DNS 查询过程会突然缓慢卡顿;(2)路由器上的 DNS 会缓存多少记录。

对于问题(1),我怀疑是路由器配置的 DoT 服务器可能有若干个工作不正常。我在路由器中配置了四个DoT服务器,分别对应阿里和腾讯的主备DoT服务器。路由器没有提供相应的log,因此无从证实这个猜测。对于问题(2),路由器也没有提供配置或者log,同样不得而知。

为彻底弄明白这些情况,我决定还是自己搭一套环境测试一下,采用 dnsmasq + stubby。其中 dnsmasq 负责发起 DoU(DNS over UDP) 查询和缓存,stubby 负责 DoT(DNS over TLS) 请求,网络拓扑是以下方式:

设备 <-- DoU --> dnsmasq <-- DoU --> stubby <-- DoT --> DNS server

采用以下命令安装:

sudo apt install dnsmasq stubby

启动或者查询状态采用以下命令:

sudo systemctl restart dnsmasq
sudo systemctl restart stubby
sudo systemctl status dnsmasq
sudo systemctl status stubby

修改 /etc/stubby/stubby.yml 文件:

listen_addresses:
- 127.0.0.1@53000
- 0::1@53000
upstream_recursive_servers:
# alidns dot
- address_data: 223.5.5.5
tls_auth_name: "dns.alidns.com"
- address_data: 223.6.6.6
tls_auth_name: "dns.alidns.com"

文件中只配置了阿里DNS的服务器地址,后面会详述原因。另外,stubby 设置为启动本地 53000 端口接收 DNS查询,这个端口可以随意设置,但要与后面 dnsmasq一致。

接下来在 /etc/dnsmasq.d 目录下创建一个自定义conf文件,例如mydns.conf,内容如下:

# stubby address and port
server=127.0.0.1#53000
no-resolv

# cache configuration
min-cache-ttl=300
max-cache-ttl=3600
cache-size=10000

# 纯主机名不转发外网
domain-needed
# 拦截私有内网地址反向解析,不发外网
bogus-priv
# 内网.lan后缀完全本地解析
local=/lan/

server 对接 stubby,因此要注意对接前面设置的 53000 端口。cache 部分的配置可以很直观地控制 ttl 和具体的cache容量,这样就很清楚地解决了前面提到的问题(2)。

对于问题(1),修改 stubby 的配置,单独指向腾讯DoT服务器和阿里DoT服务器进行测试,也搞清楚原因了。据说腾讯服务器限定了20QPS(每秒不超过20个DNS查询),声称这个值足够家庭用户使用,其实完全不对。打开腾讯的官网(qq.com)并用 wireshark 抓包,一个首页的DNS查询就已经超过20QPS,然后IP就被屏蔽,DNS结果全部是「server failure」。这还仅仅是一个用户、打开一个页面的DNS查询结果,如果是家庭多个用户(经由同一台路由器)同时上网,铁定超过20QPS。腾讯免费的 public DNS (DoT)服务测试结果很好,但是实际上根本就不适合家庭使用、甚至也不适合个人使用。

然而阿里的DNS(DoT)让人很惊喜。据说也有 QPS 限制,但是明显比腾讯要好很多。在路由器上删除腾讯DoT服务器、只保留阿里 DoT 服务器的配置后没发现卡顿现象,网络速度很快、质量很稳定。

测试结果显示:国外知名的公共 DoT 服务器全部不可用,所有DNS查询结果都是「server failure」。不知道是DoT服务器限制了中国IP地址的访问,还是某神秘力量屏蔽了对这些DoT服务器的访问,好难猜。

肉身跑到隔壁的香港测试了 Google 和 Cloudflare 的 DoT 服务器。无论是 IPv4 还是 IPv6,速度都极快、质量极稳定,好羡慕。

对了,阿里的镜像服务也比腾讯好很多,速度又快又稳,我们在国内的所有 Debian 节点都是采用阿里的镜像服务。我觉得我们应该对淘宝好一点(虽然淘宝的app和服务都很烂),支持这些好的 public services 继续支撑下去。

公共 DoT 服务器

公共 DoT 服务器

初代 DNS 协议采用明文定义、基于 UDP 传递,在复杂的网络环境下,DNS 被污染、被篡改的情况尤其让人头痛。为改善这种情况,采用加密传输 DNS 内容不失为一个稳妥的方式。

目前有两种方式加密传输 DNS:(1)HTTPS,以及 (2)TLS。

HTTPS 方式通过加密的 HTTP 连接传递 DNS 报文, 而 TLS 显然就是通过加密的 TLS 连接传递报文。两种方式对 DNS 本身没有影响,只是采用了加密的传输而已。从效率的角度看,DNS over TLS (DoT)比 DNS over HTTPS (DoH)更好,毕竟 DoH 多了一层 HTTP 协议封装。

目前 Windows 系统默认都不支持 DoT,需要采用第三方的软件(某种程度上又变相引入了不安全因素)。比较好的解决方式是选择支持 DoT 的路由器,国内华为、荣耀等家用路由器不支持 DoT,而华硕、TPLink 等家用路由器支持 DoT。

公共 DNS 服务器一般都支持 DoT 服务,国内采用腾讯和阿里两家的公共 DNS 服务即可,默认的 DoT 服务器如下:

dot.pub      -- 腾讯 DNS 服务
223.5.5.5 -- 阿里 DNS 服务

香港地区(或者海外地区)建议采用 google 和 Cloudflare 的公共 DNS 服务即可,默认 DoT 服务器如下:

1.1.1.1 -- Cloudflare 的 DNS 服务
8.8.8.8 -- Google 的 DNS 服务
公共 DNS 服务器

公共 DNS 服务器

在路由器配置中,通常可以指定 DNS 服务器,如果不单独指定,那默认就是运营商给定的 DNS 服务器。考虑到国情在此,建议指定 DNS 服务器。

国内有很多家服务商能提供公共 DNS 服务,例如腾讯(DNSPod)、阿里等。综合这几年的使用情况看,采用腾讯和Cloudflare 两家的 DNS 服务器作为主备,对于我(坐标:深圳电信)是个比较好的选择。

通常,我设置 Cloudflare 家的 DNS 服务器作为主服务器,腾讯家的 DNS 服务器作为备用服务器。这两家也都支持 IPv6 以及 DoH 等,充分满足需要,下面列示这两家的信息。

Cloudflare:

IPv4: 1.1.1.1
IPv6: 2606:4700:4700::1111
DoH:  https://cloudflare-dns.com/dns-query

腾讯:

IPv4:119.29.29.29
IPv6: 2402:4e00::
DoH:  https://doh.pub/dns-query

如果使用 Firefox 浏览器并采用 DoH,默认就是 Cloudflare 的 DoH,无需额外配置。

Firefox 中的 DoH 配置
DoH in Firefox

DNS over HTTPs

DNS over HTTPs

传统的 DNS 基于 UDP 协议,并且采用明文方式传递请求和结果。在互联网早期,这样做没什么问题。而现在网络越来越复杂,网络实体越来越多,各种看得见、看不见的手都可能修改 DNS 的结果。

简单的解决方式就是对 DNS 进行加密。 Mozilla 联合 Cloudflare 通过 HTTPs 来传递 DNS 消息,也就是 DoH (DNS over HTTPs)。这种方式从设计上看,不如 DNS over TLS 高效,但是胜在简单、无需过多更改网络设备和软件。

Firefox 已经内置了对 DoH 的支持,而且非常简单。问题是默认的两个 DoH 服务商:Cloudflare 和 NextDNS,在国内由于某种不可描述的原因,访问速度都很受影响。实际上国内腾讯和阿里的公共 DNS 服务也支持 DoH,以腾讯为例,它提供的 DoH 访问地址是:

https://doh.pub/dns-query

Firefox 的网络设置中,按下图进行配置即可:

Firefox 中设置腾讯 DoH
Firefox 中设置腾讯 DoH

另外,阿里提供的 DoH 访问地址为:

https://dns.alidns.com/dns-query
NAME、CNAME 格式

NAME、CNAME 格式

总体而言, 早期 DNS 协议是个比较简单的协议。受限于 UDP 大小和数据量, 基于 UDP 的 DNS 协议限制了包大小,并且采用二进制编码方式力求减少数据量占用。

二进制编码方式相比文本方式(例如HTTP、SIP、SMTP等)最明显的缺点就是不直观,再用一些技巧减少数据量,DNS 协议在定义、实现方面总显得有些弯弯绕绕。

比如 NAME、CNAME 等结构的编码。

本文记录了这些古怪的编码方式,方便以后查找,并帮助后续的开发者不会被代码给绕晕。作为我个人来说,还是尽快忘记这些吧。


方式一:纯压缩编码方式

这种方式应用很普遍,以“0xc0”标记开始,加上一个字节的 offset,进行消息包内的寻址即可。如下图所示:

DNS offset 编码
offset 方式编码

其中, 0x0c 就是偏移量。


方式二:直接编码方式

这种方式比较直接,占用数据量也相对大一些,将域名完整信息存在数据包中。如下图所示:

直接编码
直接编码方式

需要说明的是:并不是直接将所有字符存进去就 OK 了。对于域名数据,以“.”符号分割成几个单元,每个单元数据采用 “length + value” 方式进行编码,最后以0x00结束编码。以上述数据为例:

08 6e 73 69 6e 61 69 6d 67 04 67 73 6c 62 08 73
69 6e 61 65 64 67 65 03 63 6f 6d 00

比如,第一个 0x08,说明后面的“nsinaimg”(即 6e 73 69 6e 61 69 6d 67)长度;然后是 0x04,说明后面的“gslb”(即67 73 6c 62)长度……其他单元类推。


方式三: 混合编码方式

这种方式其实就是综合了上述两种方式。以直接编码方式开始,然后以压缩(偏移)编码方式结尾。如下图所示:

混合编码方式
混合编码方式

对图中的数据码流描述如下:

03 6b 6c 6e 04 67 72 69 64 c0 38

0x03 描述了“kln”(即 6b 6c 6e)的长度,0x04 描述了“grid”(即 67 72 69 64)的长度,而域名的后续部分,通过“c0 38”编码可以知道:需要偏移0x38字节获取。

Cloudflare 返回的DNS结果

Cloudflare 返回的DNS结果

最近查一个客户遇到的 SIP 呼叫问题,发现在检查 DNS 记录时有问题,没有正确获取 DNS 记录。切换了几个 DNS 服务器测试,只有 Cloudflare DNS 服务器(1.1.1.x 系列) 返回的DNS记录会触发问题。

这并不是说 Cloudflare 的DNS记录有错误,而是我们自己实现的 DNS 库没有考虑到该记录的不同寻常之处。Cloudflare 的返回结果确实有点与众不同。

下图是Google DNS 以及 Ali DNS 返回的DNS结果,请注意其中的 Name 字段的内容(0xC0 0x0C)

Google DNS 结果
Google / Ali DNS 返回结果

很明显,这种DNS结果采用了 compress 方式,通过 offset 来获取真正的Name。图中的 0x0C 实际就是偏移量,指向了包中的另一处地址。Compress 方式最直接的好处就是节省了包大小。

而 Cloudflare 的 DNS 结果如下图所示,直接将域名包含在 Name 中,以 0x03 标注开始,以 0x00 表示字符串结束。不再需要偏移指向其他地址。

Cloudflare 的 DNS 记录
Cloudflare DNS 返回的结果

这种方法的好处是够直接,不再需要跳转获取真实的域名信息。当然坏处是增加了数据包的大小。

我们测试了网络上大部分的 DNS 服务器,目前仅发现 Cloudflare 采取了这种方式,其他服务器都是采用 compress 方式。

站在“人”的体验角度,似乎 Cloudflare 更合理一些。但是要注意,compress 方式更符合计算机的处理,毕竟前面的 Queries 部分已经解析过 Name 了,没必要再解析一次。另一方面,直接 offset 寻址,也远远快于再一次通过判断0x00来解析Name 字符串。

因此我个人认为 Cloudflare 的处理方式,即拖慢了速度,又增加了数据包消耗。当然,以目前计算机的处理能力以及网络速度,这些都是浮云。我们的解决方式也是淡定地接受这个结果,并修改 DNS 库进行适配即可。

在本次查问题中,测试了网上公布的香港 DNS 服务器,全部拒绝了来自大陆 IP 地址的 DNS 请求,真有意思。

设置SPF记录

设置SPF记录

最近发现的一个问题,如果邮件没有 SPF 记录,某些邮件服务器可能会过滤为垃圾邮件甚至直接拉入黑名单,比如 outlook 的服务器。相对而言,gmail 服务器要聪明很多,似乎在没有 SPF 记录的情况下,会反查MX记录及IP地址并进行匹配检验。

SPF的全称是:Sender Policy Framework, 由RFC7208文档定义详细规格。具体细节就不介绍了,基本意思就是让接收方的邮件服务器对发送者的域名和IP地址等进行检查。

在Linode的DNS管理中,仅仅添加MX记录是不够的,需要单独添加一条TXT记录,如图所示:

添加SPF的DNS记录
添加SPF的DNS记录

根据RFC7208的定义,可以进行很细致的控制。如果想省事,就如上图一样,设置一条”-all”的记录即可:

v=spf1 mx -all
网站启用IPv6应注意的事项

网站启用IPv6应注意的事项

网站支持IPv6的步骤比较简单,基本上目前常用的软件,例如Apache、Postfix、dovecot等,都已经支持IPv4和IPv6双栈,只要服务商的设备提供IPv4和IPv6连接,这些软件本身无需为IPv6做特别配置。

需要注意的是服务商处的DNS配置。如果DNS配置不全面,某些服务器在鉴权时会拒绝接受来自IPv6地址的消息。例如gmail服务器在收到 email 消息时,会鉴定DNS和IPv6地址是否匹配,如果不匹配,则视为垃圾邮件直接拒绝,可以看到如下错误警示:

Our system has detected that this 550-5.7.1 message does not meet IPv6 sending guidelines regarding PTR records 550-5.7.1 and authentication.

下面以Linode中的配置做简单介绍(对DO而言也基本如此)。假设IPv4地址是“1.2.3.4”,IPv6地址是“1:2:3:4::5:6”,域名是“demo.com”。

Reverse DNS

Linode 为每个节点分配的DNS名默认是类似的成员名,例如“1234.members.linode.com”。在 Linux 系统 ping 域名”demo.com”时,首先显示的是”1234.members.linode.com”,然后才是跳转到”demo.com”。某些服务应用会认为是PTR错误。在linode中需要设置“ reverse DNS”记录,将节点的DNS名直接指定为“demo.com”。

具体措施请参考linode的在线文档

如果同时支持IPv4和IPv6,上述文档里指导的操作需要单独操作两次,分别指定IPv4地址和IPv6地址。

MX 记录

另外一个需要注意的是 MX 记录,即邮件服务器记录。如果我们将 MX 记录设置为“mail.demo.com”,那么在DNS的A和AAAA记录中,要分别对“mail.demo.com”设置IPv4和IPv6地址,例如以下DNS记录结果:

mail.demo.com. 300 IN AAAA 1:2:3:4::5:6
mail.demo.com. 300 IN A 1.2.3.4

仅仅指定一个地址,对端有可能以域名和地址不匹配为由拒绝消息请求。

TTL

Linode 默认的TTL是3600秒,建议修改为300秒。TTL 太长太短都不好,要根据自己的实际情况调整,以方便自己网络部署、同时又不增加太多网络负担为准则。

解决DNS污染

解决DNS污染

DNS污染可能是一个比较普遍的问题,主要表现在:(1)DNS查询返回的IP地址根本是错误的。“错误”意思是IP地址要么根本不存在,要么就是个和域名完全无关系的地址。(2)IP地址虽然可能是对的,但是其他参数被莫名修改,例如TTL参数等。

测试了各类DNS服务器,例如阿里DNS、DNSPod(鹅厂)、Google DNS等。无一例外,最终的结果都或多或少被一些看不见的手给修改、甚至屏蔽了。从技术角度上讲,DNS协议本身非常随意、非常粗糙,是典型的互联网蛮荒时代的产物,比如面向UDP、无加密、无鉴权等,因此网络上任何一只手都有可能修改DNS的查询结果。网络进化到IPv6阶段,当然能解决这个问题,不过既然目前绝大部分设备还只支持IPv4协议栈,因此我们还是不得不修修补补来解决这个污染问题。

最根本的解决方法就是加密DNS查询。目前有些DNS服务商提供了私有的加密DNS方式,不过不太通用,需要私有的客户端程序配合。实际上可以不用搞这么复杂,自己建立加密通道,传递DNS消息即可。例如,参考下图的拓扑逻辑:

实现简单DNS透传的逻辑单元
实现简单DNS透传的逻辑单元

在这个网络中,有两个关键程序:dnsProxy和dnsAgent。

dnsProxy顾名思义就是个Proxy,本身并不负责DNS协议的解析,也不保存DNS的查询结果等信息,只是单纯地将DNS消息传递给真正的DNS服务器,并返回相应的结果即可。dnsProxy另一个功能是对外提供加密的数据连接,例如TLS、SSL加密等,甚至可以只是简单地对数据包进行自定义的异或运算即可。另外就是对外提供非标准连接接口,这点非常重要。DNS采用标准UDP53接口作为DNS服务器接口,网络上那些看不见的手,往往就是扫描并篡改53接口的数据包。这个小程序跑在境外(大家都懂的)的一台VPS设备上,推荐采用DigitalOcean,专业的云计算服务商,采用SSD硬盘,价格公道,我们一直用TA,如果你有兴趣的话,请点击这里自行了解细节。

dnsAgent是另一个小程序,主要负责建立与dnsProxy的加密连接,接收普通设备的DNS请求并将其传递给dnsProxy,同时返回DNS结果给普通计算设备。对于网内设备而言,dnsAgent就是个伪装的DNS服务器。同样,dnsAgent其实也不需要关心、也不需要解析DNS协议细节。在我的网络中,dnsAgent跑在一台常年吃灰的树莓派上(还是第一代的)。

实现这些仅仅需要一点UDP、TCP的网络知识,甚至不需要了解DNS协议的细节,无需对DNS数据包做修改。完成后可以愉快地打开很多以前打不开的网站。当然,有些网站始终是打不开的,这是另一个与DNS无关的话题了。

访问onedrive

访问onedrive

一个好端端的网站,被莫名其妙地干掉了。经网友介绍,还好只是DNS污染。网友推荐使用DNSCrypt解决这个污染问题。不过有个更简单点的方法,直接将onedrive的IP地址写入hosts即可。

修改C:\Windows\System32\drivers\etc\hosts文件,增加以下两行记录:

134.170.108.26 onedrive.live.com
134.170.108.152 skyapi.onedrive.live.com

只有在天朝才会明目张胆出现这种不要脸的事吧?