上文介绍了用headscale、headscale webui 进行 Wireguard虚拟网络组网的配置方式。在实践中,通过tailscale网络在内网的Peer转发路由访问内部网络的其他主机时,仅靠访问内网的IP往往是不够的。很多服务要求使用域名访问,特别是内部域名,在外部也没有解析。这就需要进行DNS设置。
Tailscale网络的DNS设置
先来看Tailscale网络是如何处理DNS的:
官方已经说的很清楚了,文档在这里。
客户端(本机)发起Dns lookup,本地的tailscale客户端以100.100.100.100的名义返回DNS结果。其中,如果命中Tailnet的内建名称(Magic DNS, 由控制服务器push),返回内建名称对应的IP。
如果没有命中Tailnet的内建名称,则向Upstream DNS 提交查询(而不是向Headscale控制服务器查询!)
这个Upstream DNS是什么呢? 是客户端所在设备的本地DNS吗?
现在查看上篇中没有详解的,配置文件中关于DNS的内容:
dns_config: # Whether to prefer using Headscale provided DNS or use local. override_local_dns: true # List of DNS servers to expose to clients. nameservers: - 8.8.8.8 # NextDNS (see https://tailscale.com/kb/1218/nextdns/). # "abc123" is example NextDNS ID, replace with yours. # # With metadata sharing: # nameservers: # - https://dns.nextdns.io/abc123 # # Without metadata sharing: # nameservers: # - 2a07:a8c0::ab:c123 # - 2a07:a8c1::ab:c123 # Split DNS (see https://tailscale.com/kb/1054/dns/), # list of search domains and the DNS to query for each one. # # restricted_nameservers: # foo.bar.com: # - 1.1.1.1 # darp.headscale.net: # - 1.1.1.1 # - 8.8.8.8 # Search domains to inject. domains: [] # Extra DNS records # so far only A-records are supported (on the tailscale side) # See https://github.com/juanfont/headscale/blob/main/docs/dns-records.md#Limitations extra_records: # - name: "test1.example.com", # type: "A", # value: "1.2.3.4" # # # you can also put it in one line - { name: "dns.example.com", type: "A", value: "1.2.3.4" } - { name: "dns2.example.com", type: "A", value: "5.6.7.8" } # Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). # Only works if there is at least a nameserver defined. magic_dns: true # Defines the base domain to create the hostnames for MagicDNS. # `base_domain` must be a FQDNs, without the trailing dot. # The FQDN of the hosts will be # `hostname.user.base_domain` (e.g., _myhost.myuser.example.com_). base_domain: mydomain.com
可见,Headscale/Tailscale具有向客户端推送DNS服务器以及A记录(hosts)的功能。当然,在服务器打开override_local_dns 的同时,客户端也必须接受推送(–accept-dns=true)。
服务器支持向客户端推送的DNS方式有普通DNS,Nextdns(可以理解成是DOH,但是需要注册帐号),对限定的,Split DNS(指定FDQN向指定NS查询),以及直接返回hosts的指定A记录。
Tailnet Client Lookup on Local Network DNS
假设公司内网里有一个内部域名,指向一个内网地址,由一个该内网中的DNS服务器管理。
假设我们现在向客户端推送DNS设置为“普通DNS” 8.8.8.8,客户端在连接Tailnet后,即使可以通过在内网中的Tailnet 节点路由来连接内网的IP地址,也无法访问公司内部域名(如 service.mycompany.com)。
因为当Client查询service.mycompany.com时,Tailscale客户端将向被推送的”8.8.8.8“查询。如果客户端不接受推送的DNS服务,将使用客户端本地的DNS,同样不能得到目标内网中的内部DNS结果。
解决方法1,写入客户端的hosts(这办法很脏,手机也不行)
解决方法2,利用extra_records,在headscale控制服务器的配置文件中写入hosts,再下发给客户端。对于小型家庭网络,如果只需要较少的内部域名,这就足够了。
解决方法3,利用Split DNS,规定*.mycompany.com 全部由公司内网的DNS服务器解析。
问题:公司内网中的DNS服务器本身不在tailnet network中,其内网地址需要经过路由才能到达。在tailscale client的逻辑里,DNS查询如非指定,不会经过路由节点,因此指定的DNS服务器地址不可达。
可以在路由节点(或任何一个内网中以及加入Tailnet虚拟网络的,可以用虚拟网络地址直接访问的节点)上设置一个简单的dns缓存,如dnsmasq,由这个dnsmasq向内网中的DNS服务器请求。
通过Split DNS推送 100.64.0.1:53 处理 *.mycompany.com 的规则到客户端。客户端直接通过tailnet内部向路由节点上的dnsmasq查询。
Tailscale Client DNS behavior
下面研究一下,Tailscale/headscale 节点是否、什么时候需要接受控制服务器推送的DNS。
1,接受DNS推送(headscale推送的DNS配置以上文为准)–accept-dns=true
cat /etc/resolv.conf
resolv.conf(5) file generated by tailscale
For more info, see https://tailscale.com/s/resolvconf-overwrite
DO NOT EDIT THIS FILE BY HAND -- CHANGES WILL BE OVERWRITTEN
nameserver 100.100.100.100
search username.yourdomain.com
可见此时tailscale client 已经接管了主机的DNS服务器,服务器为100.100.100.100。
dig dns.example.com
#dig 一个不存在的域名,但在设置文件中有hosts A记录。
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.13 <<>> dns.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36030
;; flags: qr aa rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;dns.example.com. IN A
;; ANSWER SECTION:
dns.example.com. 600 IN A 1.2.3.4
;; Query time: 3 msec
;; SERVER: 100.100.100.100#53(100.100.100.100)
;; WHEN: Sun Apr 30 00:36:04 EDT 2023
;; MSG SIZE rcvd: 64
可见100.100.100.100 在3ms内就返回了结果 1.2.3.4,刚好是上文中配置文件中设置的hosts A记录。
2,关闭DNS推送 –accept-dns=false
dig www.youtube.com
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.13 <<>> www.youtube.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34301
;; flags: qr rd ra; QUERY: 1, ANSWER: 17, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;www.youtube.com. IN A
;; ANSWER SECTION:
www.youtube.com. 86253 IN CNAME youtube-ui.l.google.com.
youtube-ui.l.google.com. 153 IN A 216.58.203.78
youtube-ui.l.google.com. 153 IN A 142.250.66.78
...
;; Query time: 10 msec
;; SERVER: 1.1.1.1#53(1.1.1.1)
;; WHEN: Sun Apr 30 00:40:02 EDT 2023
;; MSG SIZE rcvd: 334
服务器向1.1.1.1查询了www.youtube.com,不再使用headscale/tailscale推送的DNS结果。
再查询 dns.example.com 返回错误的结果,也说明不再接受内部的DNS结果。
何时应接受tailnet推送的DNS
作为客户端需要主动发起连接的情况下,可能需要推送的DNS(包含hosts)。如人使用的mac windows或是手机,需要主动访问某些自定义的dns。
作为出口/路由节点的主机,不需要推送的DNS,推送的DNS反而容易引发各种问题,比如增加了DNS的延迟。