Mac Mini的配置

家里有一个Mac Mini,M4 Pro,24GB RAM,512GB,长期闲置。

想要做的事情:FRP、NAS、Docker服务…

主要需要解决的问题:如何在不在家的情况下保持对Mac Mini的访问。

NAT网络限制

~ ❯ brew install stuntman

~ ❯ stunclient –mode basic –localport 50000 stun.l.google.com 19302

Binding test: success
Local address: 198.X.X.X:50000
Mapped address: 111.X.X.X:12321
~ ❯ stunclient –mode basic –localport 50000 stun.qq.com 3478

Binding test: fail
~ ❯ stunclient –mode behavior stun.l.google.com 19302

Binding test: success
Local address: 198.X.X.X:50507
Mapped address: 111.X.X.X:32141
Behavior test: fail
~ ❯ stunclient –mode filtering stun.l.google.com 19302

Binding test: success
Local address: 198.X.X.X:42142
Mapped address: 111.X.X.X:21341
Filtering test: fail
~ ❯ stunclient –mode basic –localport 50000 stun.l.google.com 19302

Binding test: success
Local address: 198.X.X.X:50000
Mapped address: 111.X.X.X:31231
~ ❯ stunclient –mode basic –localport 50000 stun.l.google.com 19303

Binding test: fail

根据STUN检测的结果,这个网络处于对称NAT(Symmetric NAT)的范围内,果然是移动大局域网,不过好在有IPv6地址(移动默认的DNS不支持IPv6)。

  • Full Cone:Ineternal IP + PORT <-> External IP + PORT <- Random Internet Endpoint
  • Restricted Cone:Ineternal IP + PORT <-> External IP + PORT <- Vsited IP + Random Port
  • Port-Restricted Cone:Ineternal IP + PORT <-> External IP + PORT <- Visted IP + Visited Port
  • Symmetric:Ineternal IP + PORT <-> External IP + Random PORT <- Current-only Internet Endpoint

换言之:

  • Full Cone:(NAT公网端口) → (本地IP:本地端口)
  • Restricted Cone:(NAT公网端口) → (本地IP:本地端口) + 过滤IP
  • Port-Restricted Cone:(NAT公网端口) → (本地IP:本地端口) + 过滤IP:PORT
  • Symmetric:(NAT公网端口) → (本地IP:本地端口, 远程IP:远程端口)

FRP服务本身需要一个公网设备作为中继(我比较喜欢说代理),这个代理设备可以提供NAT所要求的外网IP和PORT,如果NAT设备主动访问这个外网端点,那么中继将会将P2P设备的信息转发到真正的目的地,目的地的回传信息则被改写为中继设备的回传信息;在这个过程中中继就相当于NAT客户端的透明代理,代替他访问了对应的端点。

如果是Cone类型的NAT,则可以通过UDP打洞的方式。Cone类型由于端口都是固定的,所以双方只有知道对方的NAT公网地址与端口号就可以构建连接。第一次连接的时候,双方都连接到一台中继服务器,这台服务器告诉想要构建连接的双方想要访问的人对应的端点在哪里,所以第二次连接的时候就不需要连接STUN服务器,而是直接报从中继服务器那里获得的对方的NAT公网IP和端口号;接着双方都主动访问对方的公网IP+Port,由于两者都没有被访问过,所以第一次通讯都会被拦截,但这之后NAT会相互记住已经访问过的远程地址,然后随后相互的NAT会误以为对方在回传新的数据。对于对称型而言,由于端口号是不固定的,STUN交换的IP+Port都没有办法给别的连接使用,接下来的相互访问更是没有办法实现。

这里的测试结果中:第一项是localhost:50000向stun.l.google.com:19302建立连接,那么我拿到了12321这个NAT端口号;向腾讯的STUN请求,表示请求端口被屏蔽了,腾讯的STUN服务器用不了;测试让不同的IP去访问同一个NAT端口号,结果失败了;要stun.l.google.com:19302向我建立连接,访问我localhost的不同端口,被拒绝了。总而言之,对称型NAT将其端口和内网和外网的两个IP:PORT强制绑定,你有内网和外网和记录完全对应才会开放这个端口。

Tailscale的问题

Tailscale的做法是结合了UDP打洞和中继代理,先尝试打洞,打不了洞就走中继。Tailscale的中继就是DERP服务器,可以是自有的也可以使用Tailscale给你提供的,但是一般国内没有Tailscale的服务器,如果需要稳定就可以考虑建立自有DERP。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 安装Tailscale
curl -fsSL https://tailscale.com/install.sh | sh

# 启动Tailscale
tailscaled start

# 上线和登陆
tailscale up

# 下线
tailscale down

# 查看服务状态
tailscale status

# 测试到某个端点的连接
tailscale ping XXX

# 测试和DERP节点的连接
tailscale netcheck


我的问题是,每次打开Tailscale,我的DNS就会被修改。

一开始我寄希望于Google Gemini可以帮我解决这个问题,但是更新后的Gemini有些毛病,尤其是——当我让他给我诊断的时候,他总是以为我要让他解决问题;当我让他解决问题的时候,他会有一些先入为主的错误理解;当我按照他的指示,运行对应的修改之后,他会逐渐忘记一开始的指令,很容易从修复下一个问题变到修复最初的问题,也就是闭环了。

为什么解决这个问题,必须要理解Tailscale启动之后做了什么。

  1. 创建一块虚拟网卡:utun
  2. 后台常驻一个服务:tailscaled
  3. 添加Tailscale内网专用系统路由:100.64.0.0/10
  4. 添加一个Magic DNS(100.100.100.100),让设备可以通过Tailscale专用域名互联

由于我的本地并没有预设dns,所以Magic DNS加入之后成为了电脑唯一的DNS,但是100.X.X.X只是一个内网DNS,完全无法查询到外网的域名,所以导致了网络坏了。

1
2
3
# 不启用Magic DNS加入tailscale
sudo tailscale up --accept-dns=false

如果有手动的dns设置,Tailscale也不会动。我有两台设备遇到了这个问题。还有一种办法,就是自己手动设置全局DNS,只要DNS里面不是只有100.100.100.100,那么DNS解析还是会正常。