Tailscale Split DNS:tailnet 内直连内网服务

目录

场景

家里一台服务器跑着不少服务,没有公网 IP。域名的 A 记录指向一台公网 VPS,VPS 上的 Caddy 反向代理回家庭服务器。tailnet 内的设备访问这些域名也要绕 VPS 一趟,多一跳延迟。

想让 tailnet 在线时,域名直接解析到家庭服务器的 Tailscale IP;tailnet 断开时,自动回落到 VPS 公网 IP。同一个域名,两种解析结果。

家庭服务器上跑一个 dnsmasq

dnsmasq 负责应答 tailnet 内发来的域名查询,就装在家庭服务器上,应答的就是它自己的 Tailscale IP。安装后写一个配置(Arch 用 pacman -S dnsmasq,Debian 系用 apt install dnsmasq):

# /etc/dnsmasq.d/services.conf
address=/app1.example.com/<tailnet-ip>
address=/app2.example.com/<tailnet-ip>
# 只监听 tailscale 网卡和本机
listen-address=<tailnet-ip>,127.0.0.1
bind-dynamic
# 显式指定上游,绕过 tailscale 的 100.100.100.100,防止解析环
no-resolv
server=<gateway-ip>
# 阿里云公共 DNS
server=223.5.5.5
server=223.6.6.6

address= 一行一个域名,有几个服务写几行。没写进 address= 的域名照常转发上游解析。

no-resolv 是必须的。tailscale 会把服务器的 /etc/resolv.conf 改成 nameserver 100.100.100.100。如果 dnsmasq 按默认读 resolv.conf 转发上游,加了 split DNS 之后,服务器自己解析 *.example.com 会发给 100.100.100.100,tailscale 的解析器又把这条查询转发回这台 dnsmasq,形成环。显式指定路由器或公共 DNS 作上游就绕开了。

启动并设为开机自启:

sudo systemctl enable --now dnsmasq

建议再加一个 drop-in,让 dnsmasq 排在 tailscaled 之后启动:

# /etc/systemd/system/dnsmasq.service.d/tailscale.conf
[Unit]
After=tailscaled.service
Wants=tailscaled.service

管理后台加一条 split DNS

  1. 打开 https://login.tailscale.com/admin/dns
  2. Nameservers → Add nameserver → Custom
  3. 填家庭服务器的 Tailscale IP(<tailnet-ip>)
  4. 域名填 example.com,匹配所有子域

一条规则覆盖全部子域名。之后 tailnet 内查询 *.example.com 都会发给这台 dnsmasq。

用了 exit node 时 split DNS 默认不生效,需要在该 nameserver 上勾选 Use with exit node。

证书

直连路径上,客户端直接连家庭服务器的 443 端口,TLS 必须由家庭服务器自己终结。服务器没有公网入站,证书只能走 DNS-01 质询,Caddy 配合 Cloudflare DNS-01 的做法见 Caddy Cloudflare DNS-01:获取通配符 TLS 证书。通配符 *.example.com 一张证书盖住所有服务,Caddy 自动签发和续期。

验证

Windows 上分别开、关 tailscale 各跑一次:

nslookup app.example.com

tailscale 开着应返回 <tailnet-ip>,关掉后返回 VPS 公网 IP。结果没变就 ipconfig /flushdns 清一次缓存。

dnsmasq 对本地配置的应答 TTL 为 0,客户端不缓存,切换即时生效。公共记录那边的 TTL 也可以调低(比如 60 秒),减少回落侧的缓存滞后。浏览器开了 Secure DNS(DoH)会绕过本地解析器,这些域名会一直走公共 DNS 的 VPS 链路,功能不受影响,只是不直连。

编辑此页

目录