关于DNS魔法及其他的史诗论文

之前有一篇译自Tailscale博客的关于DNS的技术文章,里面更主要的讨论的是终端设备上DNS配置管理的问题,今天这篇同样译自Tailscale博客的关于DNS的技术文章,可能更多的是讨论DNS的解析运作过程。那么下面是原博客文章内容:

命名产品是困难的,Tailscale的关键特性之一,MagicDNS,长期以来是词法争议的焦点。一些人认为我们应该叫它Magic DNS因为苹果将他们旗舰键盘和鼠标叫做Magic Keyboard和Magic Mouse。
但你注意到过没有,苹果也将它们的笔记本和无线耳机叫做MacBooks和AirPods?他们这样做是因为英语中一个晦涩(且呆板)的规则,当移除名词短语中的形容词会改变名词含义时,可以移除空格制造合成词。不含magic的Magic Keyboard依然是键盘,不含Mac的MacBook却不是本书了。由此,MagicDNS是一个词就是因为去掉magic不会只是DNS,它不会成为任何事物。Tailscale已经有了DNS和split DNS(两个词!)的配置,但MagicDNS是个不同的东西。

  • <Xe> 他们也可以为了商标这样做!相比“面包”、“键盘”、“书”等,非常规词更容易申请商标。
  • <Avery> 另一方面,为了避免商标雷区,有时大型公司会采用<商标><通用词>的形式命名产品,如微软Word或谷歌Mail。如果一个产品名字包含公司名,你可以相当确信没别人能那样叫他们的产品。

Tailscale允许你管理你机器的DNS配置,这使你可设置机器对整个互联网或者匹配特定域(split DNS)首选DNS服务器。这是整洁并且使得本地DNS配置更如人们在/etc/resolve.conf添加多条DNS解析器通常所期待的。
但那不是MagicDNS,仅仅是稍好点的DNS。MagicDNS在这些特性之上构建,完全改变名字解析工作方式令新使用场景DNS安全。它是Tailscale和你的基础设施可以构建在其上的构件。
今天我们看看DNS的问题空间,这些年复杂性怎么被层层叠加,MagicDNS怎样去除全部的复杂性并在过程中使得一切更可靠。

DNS悲剧:命名事物但缓存失效

DNS是种听起来简单的服务之一。将名字映射到数字,是吧?但在现代互联网背后它是更复杂的一件事。它早于现代互联网,我们今天先不讨论这点。

解释DNS如何工作的图。笔记本向DNS服务器询问tailscale.com的IP地址得到82.58.46.8的回复

  • <Xe> 如果你要以一种更可视化的方法学习更多关于DNS如何查询,可以看Pingdom的这个解释。它是复杂的,有许多我们在下面将讨论的单独的委托步骤。

我们可以将DNS认为是第一个全球分布式数据库。DNS设计成全局收敛的(随着时间推移整个系统同意什么名字指向什么IP地址),所以无论请求源自渥太华、旧金山、西雅图还是帕劳,查询google.com将总指向同样的IP。

  • <Avery>好,实际上google.com可能是互联网上最坏的例子了,因为他们采用了太多的泛播和DNS技巧令你永远不会知道你将得到什么IP地址。但为了我们的目的,让我们想象Google像一般人一样使用DNS。

DNS是全局收敛的,因为随着时间推移,如缓存过期,每个互联网上DNS服务器可以最终对同样查询同样回答。每个DNS记录有存活时间(TTL)设置,指定回答在多长时间有效。不幸的是,DNS客户端和服务器可能选择忽略建议的TTL值并使用他们自己的TTL。一些ISP声称这样是为了“减少网络流量”,但这样违背DNS RFC导致产生非常难以debug的微妙问题。

  • <Avery> 此外,任何基于轮询(相对于基于推送)的带静态时间周期的缓存总会有这样的问题:该选择怎样的缓存超时。如果太短,会增加查询延迟和服务器负载,如果太长,修改会需要时间(有时是数小时或数天)来传播。这就是为何中介人介入并试图通过扰乱TTL来“修复”这个问题。但他们总是最终产生不同的问题。

这种一个名字对一个 IP 地址的模式在互联网只有一块大陆那么大并且不经常重新布线时候运行良好。但当你在全世界都有服务器,希望用户直接访问最近的一个或者忽略刚好宕机区域时,就会失效了。所以运营者拉取DNS服务器到他们的负载均衡基础设施,将用户指向最近的应用服务器而非任何一个正确答案。

一个DNS服务器为位于不同物理位置的两个笔记本提供不同的回答

这有时会导致 ISP 设置的过于热心的缓存解析器出现问题,使得路由器无法使用符合规范的解析器,或者当您使用其他地方托管的 DNS 服务器时,无法从负载平衡器获得最佳本地化解决方案。但总体上它是可以奏效的。
  • <Xe> 当然这里假设你的政府、ISP或是当地咖啡馆WiFi没有不怀好意地劫持DNS。

作为一个社会,我们放弃世界上每处都要将每个DNS名字总映射到相同的IP地址这一规则。实践中这通常不会伤害我们,除了我们试图调试它时。然后它可以是简单的,也可以是非常困难以至于使得你开始重新考虑你的职业抱负并考虑进入农业需要多少开销。奶牛出奇的贵

  • <Xe> 将DNS变化如何被全球人们观察到的方法称作”传播“是个常见的误解。这技术上不正确。大多数情况下,你等待的是缓存过期,然后下个请求被转发到上游 DNS 服务器以获得准确的信息。这就是为什么人们称DNS为“全局一致”:随着时间的推移,整个互联网将逐渐在哪个名字指向哪个IP地址的回答集合上趋于一致。然而实践中ーー考虑到数据实际上是如何在互联网上移动的ーー说是 DNS 查询具有从原始 DNS 服务器传播出去的效果并非完全错误。

DNS加密(并不是)

DNS是非加密、无鉴别的协议。查询和响应在互联网上以明文发送。这意味着无论谁只要发送正确的名字就能得到IP地址。

  • <Xe> 通过设置私有DNS服务器(通常称之为“split horizon”DNS)可以将发布私有主机名到公共DNS的隐私风险最小化,私有DNS服务器拥有与公共互联网不同的域名相应集合。你可以通过VPN(例如Tailscale管理员控制台的DNS页签)连接那些,但你会失去DNS的全局一致性和缓存特性。在许多情形,你可以通过在公共DNS服务器返回私有IP段,但这取决于你偏执的程度。并且有时公共DNS服务器安全特性会拒绝私有IP段

由于DNS回复没有加密和签名,你也从不会十分确定你得到的DNS响应在中途被篡改。攻击者可以轻易地嗅探并返回个将google.com指向badgooglephish.comIP地址的包。你的iPhone也不会察觉。有一套叫做DNSSEC的扩展试图使用我没有资格解释的有趣的密码学去修复这些问题。但这才是缺点真正显露出来的地方。Slack最近有个相当严重的生产中断,完全可以追溯到为了FedRAMP合规而启用DNSSEC的尝试。

DNS被劫持,展现劫持者可以扰乱DNS结果

  • <Avery> DNSSEC并不像听起来那么好。 @tqbf 有个详尽的呼吁叫做 反对DNSSEC 系统地驳斥了你可能使用DNSSEC的每个原因。我猜,除了FedRAMP。

DNSSEC看起来并不会推广开。所以在空缺环境中,有一些至少通过加密信道携带(部分)DNS的新协议。但作为进程中的一部分,你的机器通常创建到Google、Cloudflare或者其他的HTTPS会话。中间人可以看到(并且理论上可以篡改)明文的DNS请求和响应。根据你的威胁profile,那通常不能解决你全部的安全和隐私担忧。

  • <Xe> 有像GNS这样的新项目开启端到端的请求隐私。但它们有其他缺点并且没有广泛部署。对于使用它的人们很好,但大多数人不用它。

所以在一般部署中,DNS没有中途加密、真实性或验证机制。这也意味着没有途径去指出一个客户端是否被授权访问一条给定的DNS记录。没有原生方法去建立身份和DNS请求之间的关联。这意味着更新DNS记录(例如动态DNS)不能通过DNS自身完成,就需要委托给使用非标准API的第三方。没有自动化DNS修改的好API,只有我们别无选择而容忍的API。

  • <Xe> 存在像DNS UPDATE requests 允许你通过DNS更新DNS记录,但这几乎只在部署Windows的Active Directory时使用。它也未修复以明文发送身份验证凭据的问题,所以只在公司私有网络有用,而不是互联网可通用的构件。

委托(可能危险)

当你在注册商注册一个域名,他们创建一条记录,将你的域名委托给在如.com顶域权威机构下一些其他域名服务器负责。(这就是域名注册和查询如何工作)然后,你可以将该域名的一个子集的责任委托给另一个第三方,它们需要和他们的注册商设置。例如,你在你的DNS注册商注册你的网站example.com,他们委托它给.com注册商。但你想委托对你域名子集的控制cdm.example.com给你的CDN供应商,以使他们可以不打扰你的情况下尽快做出他们需要的改变,cdn.example.com就有了它自己的DNS记录。

DNS委托流程,这些往返以数十到数百毫秒计,想想这个累加起来。

读这个大多数人可能从没听说过像这样委托子域的子域,因为实际上它如此复杂和脆弱,除非DNS是双方的核心能力。当大企业将域名外包给其他企业,他们通常使用如googleusercontent.com完全独立的顶级域名,或者类似的以减少混乱。这也帮助防止在合作企业数据泄露导致一些人使用facebook.com发出惊人数量的垃圾邮件而产生的名誉受损。

  • <Avery> 子域的子域也不时兴了,因为在受信的父域和不信任的子域间意外共享HTTP cookie。另外DNS 中的每级子域委托都会带来额外的网络往返,以进行递归名称解析,从而增加延迟。结果是麻烦多过价值。

反向DNS(另一个完整的DNS)

接下来是反向DNS,将IP地址翻译回一个域名。在电子邮件中,反向DNS仍被用作垃圾邮件过滤的风险评估,因为大多数配置良好的邮件服务器的正向和反向DNS名字相匹配。这也是互联网服务运营者判断IP是否住宅地址的方式。

  • <Avery> 不要忘记ssh的前身rlogin!以及TCP Wrappers。在过去,我们习惯单纯依靠(当然是未加密的)反向DNS的回答来接受或者拒绝连接。我们也曾习惯认为绑定在少于1024的端口更安全。网络安全已经取得了长足的进步!

曾经每个公司拥有一整个分配给他们的IPv4子网,所以他们也拥有他们自己的反向DNS域。当互联网分配公司一个IP地址块时,会是以下三类中的一个:

  • A类:/8网络拥有1600万个地址
  • B类:/16网络拥有6万5千个地址
  • C类:/24网络拥有256个地址

这些分类不再使用了,但你可以在反向DNS的实现中看到它们残留的痕迹。
IPv4地址是32位(bit)数字,通常写成每8位数字用点分隔的序列,如:

82.58.46.8   

这用来表示一个严格的层级结构,自82.0.0.0/8块的拥有者,到82.58.0.0/16块的拥有者,最终是82.58.46.0/24块的拥有者。同样的层级被DNS委托用来分发反向DNS名字的拥有者。为了委托这个,你需要反转IP地址如下:

反向DNS反转IP地址

这是反向DNS查询如何运作的核心,也是我们之所以称它是另一个完整的DNS的原因。它和DNS同样符号,但是反向。实现上有很多有意思的地方。

  • <Xe> Tailscale在MagicDNS中实现了反向DNS。但Tailscale没有使用旧的典型地址,使用了100.64.0.0/10,比/8少了两位。这与只进行8位跳转的子网委托方式相冲突。为解决这点我们设置了一批反向DNS路由。可以在运行Tailscale和systemd的机器上执行resolvectl,或者在运行Tailscale的Mac上执行scutil --dns进行查看。下面是在我开发机上的输出:
DNS Domain: telethia-pirhanax.ts.net example.com.beta.tailscale.net ~0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa ~100.100.in-addr.arpa ~101.100.in-addr.arpa ~102.100.in-addr.arpa ~103.100.in-addr.arpa ~104.100.in-addr.arpa ~105.100.in-addr.arpa ~106.100.in-addr.arpa ~107.100.in-addr.arpa ~108.100.in-addr.arpa ~109.100.in-addr.arpa ~110.100.in-addr.arpa ~111.100.in-addr.arpa ~112.100.in-addr.arpa ~113.100.in-addr.arpa ~114.100.in-addr.arpa ~115.100.in-addr.arpa ~116.100.in-addr.arpa ~117.100.in-addr.arpa ~118.100.in-addr.arpa ~119.100.in-addr.arpa ~120.100.in-addr.arpa ~121.100.in-addr.arpa ~122.100.in-addr.arpa ~123.100.in-addr.arpa ~124.100.in-addr.arpa ~125.100.in-addr.arpa ~126.100.in-addr.arpa ~127.100.in-addr.arpa ~64.100.in-addr.arpa ~65.100.in-addr.arpa ~66.100.in-addr.arpa ~67.100.in-addr.arpa ~68.100.in-addr.arpa ~69.100.in-addr.arpa ~70.100.in-addr.arpa ~71.100.in-addr.arpa ~72.100.in-addr.arpa ~73.100.in-addr.arpa ~74.100.in-addr.arpa ~75.100.in-addr.arpa ~76.100.in-addr.arpa ~77.100.in-addr.arpa ~78.100.in-addr.arpa ~79.100.in-addr.arpa ~80.100.in-addr.arpa ~81.100.in-addr.arpa ~82.100.in-addr.arpa ~83.100.in-addr.arpa ~84.100.in-addr.arpa ~85.100.in-addr.arpa ~86.100.in-addr.arpa ~87.100.in-addr.arpa ~88.100.in-addr.arpa ~89.100.in-addr.arpa ~90.100.in-addr.arpa ~91.100.in-addr.arpa ~92.100.in-addr.arpa ~93.100.in-addr.arpa ~94.100.in-addr.arpa ~95.100.in-addr.arpa ~96.100.in-addr.arpa ~97.100.in-addr.arpa ~98.100.in-addr.arpa ~99.100.in-addr.arpa   

但现在随着IP地址的稀缺和频繁重新分配,一组IP的反向DNS域通常属于你的云厂商,而不是你。因此提供“正确的”反向DNS回答需要许多我们不想牵扯的人大量协作。

  • <Avery> 并且别忘了,结合HTTP Host: headerTLS SNI,一个IP可以有许多名字!正向DNS没问题,你只要设置多个DNS名字翻译到同样的IP。但是反向DNS,每个IP只能翻译回一个名字。这在现代互联网中运作不好。
  • <Xe> 有相当数量的人会经历在IRC上显示一个像gimme-your.nickserv.pw这样滑稽的反向DNS名字痛苦。随着IRC慢慢从公众视野淡出,这是个在消亡的艺术。

总是DNS

所有的这些甚至没有覆盖每个设备和操作系统上的DNS客户端配置。DNS客户端配置是每个平台独特的,可能微不足道也可能漫无止境,取决于你使用哪个平台以及已经有多少人有关于如何配置的意见。多数时候你希望不必关心它。下一个大问题是当你需要关心的时候,有一个操作系统原生 API 来解决这个问题。最后一个问题是,你必须弄清楚在运行中操作系统发生了什么,然后将所有东西拼凑在一起,使事情像人们期望那样“Just Work TM
所有的这一切疯狂就是为什么当你看到一个大网站挂掉时,通常是因为DNS服务器又失败了导致一起都宕机了。当你私有内部网络运行异常或者缓慢时,经常是本地DNS错误(或者旧缓存值、或节点间DNS配置不一致、或者月相产生的潮汐影响了海底光缆)。
DNS带来了许多关于停机时间本质的文化基因、艺术创作和哲学文献,比如下面这个:

MagicDNS是DNS,但有不同

MagicDNS使用DNS作为它的查询协议,因此你可能认为它有同样的缺陷。但在MagicDNS中,方程式完全颠倒了。
在Tailscale中,协调服务有一个你tailnet中一切东西的列表。你有端到端的加密,所以你可以完全相信一个人所有的机器确实被那个人使用,并且从哪个机器来的数据包都与那个人相关。通过Tailscale加密强制执行的ACL,你只能访问你拥有权限的机器。由身份提供者进行的用户鉴别可以防止整体攻击。这一切使得网络层安全。——

  • <Avery> ……就像我们假装回到rlogin和TCP Wrappers时代!

——是的,就像从前。但是一旦网络安全了,我们就可以在其上构建更酷的机制。
MagicDNS在DNS客户端配置中设置了一个相对罕见的称之为搜索域的特性。这允许你在你的tailnet中不使用IP地址或是完整域名,而是使用简单主机名去连接个人机器。假如你的主服务器叫pandoria,你可以使用pandoria代替完整的域名pandoria.example.com.beta.tailscale.net(或者你配置了HTTPS pandoria.telethia-pirhanax.ts.net)直接连接。这使你可以不用额外输入更简单的连接你关心的机器。你不需要设置SSH别名,而是直接使用ssh pandoria登录。
MagicDNS自动使用设备的机器名作为DNS入口的一部分。如果你改变了你的设备名字,MagicDNS入口自动改变。如果你有想用来指代你设备的特殊名字,你可以编辑机器名

MagicDNS由Tailscale的控制服务器提供,所以请求永远不用离开你的机器

每个机器是他自己的DNS服务器

传统 DNS 最大的可靠性缺点之一是,如果 DNS 服务器出现故障,客户端就无法再在该 DNS 服务器上查找主机,除非名称被缓存。然后,当缓存过期时,一切都会遇到更多的问题。这会把小的中断变成大的中断,让你登上 CNN 和 Reddit 的头版。
MagicDNS通过在tailnet中的每个机器上使用虚拟地址100.100.100.100本地运行MagicDNS服务器解决了这个问题。DNS服务器不会宕掉。它不会在负载中挂掉(除非你自己的机器也这样了) ,而且当你的机器由于某种原因宕掉时,其他所有机器都不会受到影响。
因为MagicDNS总是本地运行,你不必信任端到端加密:MagicDNS流量从不出你的机器。它是虚拟网络上的虚拟服务。

如果你的浏览器不带顶级域名询问pandoria,操作系统会依次尝试这些域以得到可用的

  • <Xe> 当DNS服务器跑在你网络中的每台机器上,你不用担心DNS服务器宕掉。如果你设备的DNS宕了,是因为你自己的设备不工作——你有了更大的问题。但愿不是火灾,火灾对计算机不好。

MagicDNS对Tailscale特定的DNS名称使用委托,但是所有的委托发生在你自己的内部,这意味着委托的延迟几乎为0,你无法把它配置错误。
MagicDNS从不需要担心授权更新或是篡改:更新来自通过控制平面的安全通道。
MagicDNS中反向DNS默认生效,因为每个Tailscale机器有它自己独特的私有IP,MagicDNS为子网管理反向DNS域名。
MagicDNS不必忍受延迟问题,延迟与设备发包到localhost一样低。

MagicDNS记录总在设备上,因此你不用等待DNS服务器回应

透明地提升你操作系统能力

因为Tailscale在每台机器上运行一个本地DNS服务器,MagicDNS可以规范和升级你tailnet中每台机器的DNS能力。
例如,MagicDNS可以尽可能多的将DNS查询透明地升级为DNS-over-HTTPS,以使对外部的DNS请求不被中途嗅探或篡改。这不能保护你的每条DNS请求与在技术上可以被Google、Cloudflare、Quad9或其他DNS-over-HTTPS服务商看到,但它应该可以从你的ISP运营商或着咖啡店WiFI网络中的脚本小子那里保护你的非MagicDNS查询。这点甚至可以在如Windows7那样太老旧不支持DNS-over-HTTPS操作系统的机器上工作。

  • <Xe> 是的,我们支持Windows 7!

如果你创建了split DNS路由本地DNS服务器还可以委托子域,即使你的操作系统并不原生支持。当你在管理员控制台配置split DNS,路由自动推送到你tailnet中的所有设备,并允许你将流量路由到你想要的任意子域或是虚拟顶域。比如你可以使用这点从非AWS的Tailscale节点来访问AWS VPC域名。即使是Windows 7上。

基于推送的缓存失效

显而易见MagicDNS由于我们控制平面会立即推送更新到每个设备(DNS将周期轮询变更),而可以完全修复缓存失效问题,并且因为它直接运行在设备上,我们消除了中间环节扰乱缓存参数的可能性。这意味着你可以相信你的内部DNS总是最新的,永远不用担心又配另一个内部DNS TTL。

  • <Avery> 这意味着MagicDNS可以用作服务发现工具。推送你的代码到名为git的服务器。在http://pastebin分享文本。有些创业公司在做主要基于绕过DNS限制的新的服务发现机制。我们有在服务发现中不要信任DNS的教训,但那从不是DNS协议的问题。而是缓存、延迟、轮询。

细节不可见

有人可能会说“啊,那只是个动态DNS服务器。我可以在一个周末做出来,至少这些笨蛋写的一半代码”。但是没有像MagicDNS做的那样在每个机器上运行的DNS服务器,更新延迟会再次成为一个问题。安全和缓存不可用会再次成为问题。DNS服务器的启动和负载会成为问题。那将成为故障点而不是弹性点。
几十年来的常规DNS,即使你做了负载均衡也演变成了一个故障点。我们没有修复DNS近40年的设计缺陷,我们也不想在修复接下来40年的。MagicDNS以惊人少的代码,在惊人的深度解决了关键问题。
这边是我们之所以叫它MagicDNS:因为没有魔法就不只是DNS。MagicDNS建立在完全不同的基础上消除了DNS的大多数问题。如同魔法。

原文地址:https://tailscale.com/blog/magicdns-why-name/