
关键词: macOS、Stash、Shadowrocket、Tailscale、Subnet Router、SSH、Auth Key
适用环境: macOS、Stash、Shadowrocket、Tailscale、Linux、NAS。Stash 与独立 Tailscale 是排障过程中的方案;Mac 最终使用 Shadowrocket 内置 Tailscale,独立客户端已经卸载。文中功能版本说明不代表本次环境的完整版本记录。
最终结果: Shadowrocket 启用内置 Tailscale、填写 Auth Key,并配置家庭网络分流 Module。最后在「设置 → 隧道 → 隧道」中,先开启「包含所有网络」,再开启「包含本地网络」,家庭内网访问恢复正常。Mac 上的独立 Tailscale 已卸载,由一个客户端处理普通代理与家庭网络访问。
适用边界: 这是本次实际环境中的成功结果。Stash 绑定 utun 的中间方案曾验证成功,但接口编号可能变化,重启、重连和切网后的稳定性未充分验证。Shadowrocket 最终方案的逐项复测日志和长期稳定性记录也未完整保留,本文不将整体使用确认扩大为所有版本、所有协议的兼容性保证。
脱敏说明: 文中设备名、用户名、IP、端口和域名均为教学示例;协议网段与官方公开文档地址保留。Auth Key 使用 <YOUR_AUTH_KEY> 占位,不包含真实主机密钥、指纹、登录授权链接或认证 token。日志只保留诊断所需片段,配图均为示意图。
一、最初的需求:离家后访问家庭内网
我的家庭网络里有几台长期运行的设备,包括 NAS、Linux 服务器以及一些 Docker 服务。
平时在家里,通过局域网地址就能访问:
192.168.50.1 路由器
192.168.50.10 NAS
192.168.50.20 Linux 服务器
192.168.50.30 其他内网设备
但离家之后,Mac 连接的是另一个网络,例如:
192.168.10.0/24
这时自然无法直接访问家里的 192.168.50.x。
我希望实现的效果很简单:
无论 Mac 在哪里,都能像在家一样访问局域网设备,同时不影响已有的 Stash 代理。
最初希望把普通互联网代理与家庭网络访问安排好,避免修改路由器端口映射,也不把 SSH、NAS 管理页面直接暴露到公网。
实际经历了三个阶段:
| 阶段 | Mac 端方案 | 结果与取舍 |
|---|---|---|
| 第一阶段 | Stash 内置 Tailscale | 遇到认证状态、规则命中和协议连接问题 |
| 第二阶段 | Stash + 独立 Tailscale,绑定 utun | 普通代理、Tailnet IP、Ping、SSH、家庭 LAN 均确认正常,但固定接口名增加维护成本 |
| 最终方案 | Shadowrocket 内置 Tailscale + Module + 隧道接管设置 | 按先所有网络、再本地网络的顺序启用后,家庭内网访问正常;独立客户端已卸载 |
下面先记录完整的 Stash 排障脉络,最终设置集中在第十五节。
二、第一种方案:使用 Stash 内置 Tailscale
一开始,我发现新版 Stash 已经支持 Tailscale。
这看起来是一个非常理想的功能。
Stash 不再只是普通代理客户端,还可以通过 type: tailscale 创建一个 Tailnet 节点。
官方文档显示,Stash macOS 4.2+ 支持 Tailscale,4.3+ 进一步支持自动 Tailnet 路由。参见Stash 协议文档。
如果能通过 Stash 内置 Tailscale 实现全部需求,就不必再安装额外的 VPN 客户端。
1. 使用 Override 添加 Tailscale
由于我的 Stash 主配置通过服务器在线更新,所以不适合直接修改主 YAML。
否则下一次订阅更新,就有可能覆盖本地修改。
Stash 提供了 Override(配置覆写)功能。
它可以把本地配置叠加到在线订阅上,而且对于 proxies、rules 等数组,Override 的内容会插入原配置前面。参见Stash Override 文档。
创建一个本地 Override:
name: Home Tailscale
proxies:
- name: Home-Tailscale
type: tailscale
hostname: macbook-home
proxy-groups:
- name: Home Network
type: select
proxies:
- Home-Tailscale
- DIRECT
rules:
- IP-CIDR,192.168.50.0/24,Home Network,no-resolve
其中:
type: tailscale
创建一个 Stash 内置的 Tailscale 节点。
proxy-groups:
创建一个独立的家庭网络策略组。
rules:
- IP-CIDR,192.168.50.0/24,Home Network,no-resolve
则负责把家庭局域网流量送给 Tailscale。
这里踩到了第一个小坑。
坑一:新增节点不代表它会自动出现在策略组里
当时的界面操作中,我最初只添加了:
proxies:
- name: Home-Tailscale
type: tailscale
配置保存后,在 Stash 的代理选择界面里却找不到 Tailscale。
后来把节点加入策略组,才在当时使用的策略选择界面中找到它。这里记录的是当时的界面表现,不代表所有版本的节点列表都必须通过策略组展示。
定义一个代理节点,与把它加入某个策略组,是两件不同的事情。
添加 proxy-groups 后,终于可以在该策略组中选择 Home-Tailscale。规则也可以直接引用节点;后来的 Stash 指定网卡方案就不需要额外的策略组。

图 2:Stash Override 与策略组关系(历史内置方案)。
三、第二个坑:Tailscale 注册成功,却显示超时
配置完成后,Stash 能够识别 Tailscale 节点,但节点始终显示:
NeedsLogin
点击开始认证,也没有自动打开浏览器。
我尝试过两种认证方式。
第一种是 Stash 内置的交互式登录。
第二种是在 Tailscale Admin Console 生成 Auth Key:
proxies:
- name: Home-Tailscale
type: tailscale
hostname: macbook-home
auth-key: <YOUR_AUTH_KEY>
但状态依然没有立即改变。
最开始怀疑是 Tailscale 控制服务器无法正常连接。
于是进行网络测试:
curl -Iv --connect-timeout 10 \
https://controlplane.tailscale.com/
Stash 开启时,DNS 解析结果类似:
198.18.x.x
这个地址属于常被代理软件用作 Fake-IP 地址池的范围。
说明请求很可能经过了 Stash 的 DNS 与代理处理。
关闭 Stash 后重新测试,DNS 返回了真实的 Tailscale 控制面地址,并且 TLS 握手成功。
这个测试排除了一个错误方向:
至少在当时的网络环境中,Tailscale 控制服务器并非完全无法直连。
但这并不能证明 Stash 内置的 Tailscale 引擎使用了相同的出站路径。
最后,重启 Stash 后,认证状态变成了已完成,Tailscale 管理后台也出现了新的设备。
这意味着节点注册成功。
不过,节点延迟测试仍然显示超时。
这里还有一个容易误判的地方:
代理节点的公网 HTTP 延迟测试失败,并不等于 Tailnet 内的所有目标都无法访问。
是否能正常使用,需要针对实际设备测试。
四、第三个坑:Tailscale 注册成功,却访问不了设备
假设 NAS 的 Tailscale IP 为:
100.101.102.103
完成认证后,我尝试通过这个地址访问 NAS。
结果无法连接。
经过排查,在 Stash Override 中添加一条明确的路由规则:
rules:
- IP-CIDR,100.101.102.103/32,Home Network,no-resolve
访问立即恢复。
这一现象提示,此前的规则命中或路由状态值得检查;没有抓包证据,不能仅凭这一次恢复确定全部根因。
对于新版 Stash,官方文档说明它能够自动处理 Tailnet Peer 地址。但实际排障中不能只根据功能说明判断转发是否生效,还需要结合客户端版本、运行状态及最终命中的代理规则确认。参见Stash 自动 Tailnet 路由。
这次显式增加 /32 规则,是一个有效的诊断手段。
之后配置变成:
rules:
- IP-CIDR,100.101.102.103/32,Home Network,no-resolve
- IP-CIDR,192.168.50.0/24,Home Network,no-resolve
NAS 可以访问了,但新的问题很快出现。
五、第四个坑:SSH 端口能连接,却无法正常 SSH
当时的网络结构是:
Mac
│
▼
Stash 内置 Tailscale
│
▼
NAS
│
▼
Subnet Router
│
▼
Linux Server
NAS 能通过 SSH 访问 Linux,iOS 设备也能连接 Linux。
但 Mac 无法正常 SSH。
先测试 TCP 22 端口:
nc -vz 192.168.50.20 22
输出:
Connection to 192.168.50.20 port 22 [tcp/ssh] succeeded!
看起来网络已经通了。
但真正执行:
ssh -vvv user@192.168.50.20
却卡在:
Connection established.
Local version string SSH-2.0-OpenSSH_10.x
有时还会出现:
kex_exchange_identification: read: Connection reset by peer
这是整个排障过程中最容易产生误导的地方。
TCP 连接成功,不等于 SSH 协议正常
nc -vz 只能说明从客户端所见的 TCP 建连成功;存在本地代理时,它甚至不足以单独证明远端 SSH 服务已经完整响应。
而 SSH 后面还有:
TCP 建立
↓
SSH Banner 交换
↓
密钥协商
↓
服务端身份验证
↓
用户认证
↓
Shell
当日志停在:
Local version string SSH-2.0-...
说明日志已进入本地 SSH 版本交换阶段,但没有看到正常的远端版本信息。仅凭这一行,不能证明数据已经完整到达目标服务。
这与 SSH 用户名或公钥错误不是同一个层面的问题。

图 3:SSH 排障流程(历史故障示意,非最终状态)。
六、第五个坑:SSH Jump Host 也不是万能的
为了绕过子网路由,尝试让 NAS 充当 SSH 跳板。
例如:
ssh -J nasuser@100.101.102.103 \
user@192.168.50.20
但 NAS 的 Tailscale SSH 接管了默认的 22 端口,并要求浏览器身份检查。
而 NAS 原生 OpenSSH 服务实际上运行在另一个端口,例如 2222。
因此需要明确指定端口:
ssh -J nasuser@100.101.102.103:2222 \
user@192.168.50.20
这次成功进入了 NAS 的普通 SSH 服务。
但接下来又出现:
channel 0: open failed:
administratively prohibited: open failed
原因是 SSH 跳板需要服务端允许 TCP forwarding。
NAS 的 SSH 服务端拒绝了转发请求。
不过换成两次 SSH,就可以正常访问:
ssh -t -p 2222 nasuser@100.101.102.103 \
'ssh user@192.168.50.20'
两种方式看起来相似,但机制不同。
ProxyJump 使用 SSH 连接转发。
而后一种方式,是先登录 NAS,再由 NAS 主动运行 SSH 客户端连接 Linux。
这再次证明:不同连接方式成功或失败,不能简单归结为 Tailscale 网络不通。
七、第六个坑:直接给 Linux 安装 Tailscale,问题依旧
为了排除 NAS 的 Subnet Router,我决定让 Linux 服务器直接加入 Tailnet。
在 Debian 上按 Tailscale Linux 安装文档安装:
curl -fsSL https://tailscale.com/install.sh | sh
启动服务:
sudo systemctl enable --now tailscaled
加入 Tailnet:
sudo tailscale up \
--hostname=linux-home \
--accept-dns=false
通过网页登录后查看 IP:
tailscale ip -4
假设得到:
100.110.20.30
然后从 Mac 直接连接:
ssh user@100.110.20.30
按理说,现在的数据路径已经变成:
Mac
│
▼
Stash 内置 Tailscale
│
▼
Linux 独立 Tailscale
完全绕过了 NAS。
但 SSH 仍然偶尔在握手阶段被重置。
这说明问题不只局限于 NAS 的子网转发。
后来重启 Stash,SSH 握手恢复正常。
从现象看,Stash 内置 Tailscale 的运行状态或连接处理值得怀疑。
不过,单凭这些现象还不能证明某个特定版本存在软件缺陷。要确定根因,仍然需要抓包、日志或者源码层面的进一步验证。
八、第七个坑:Tailscale SSH 和 OpenSSH 不是一回事
解决握手问题后,SSH 又出现一条提示:
Tailscale SSH requires an additional check.
随后要求访问一个网页完成认证。
认证成功后可以登录,但重复连接时可能再次触发身份检查。
查阅官方文档才明确:
Tailscale SSH 和 Linux 原生 OpenSSH 是两套不同的认证机制。
Tailscale SSH 可以接管 Tailnet IP 上的 TCP 22 端口,通过 Tailnet 身份和访问策略管理登录。
其中 check 模式会要求定期重新认证,默认检查周期为 12 小时,也可以配置为每次连接都检查。参见Tailscale SSH 文档。
这种方式适合需要集中身份管理的环境。这里并不是说 Tailscale SSH 不能用于开发,而是其身份检查策略与我现有的公钥登录习惯不同。
但我的需求是个人服务器远程开发,需要稳定支持:
SSH
SCP
rsync
Git
VS Code Remote SSH
自动化脚本
所以更希望 Tailscale 只负责网络加密与组网,SSH 认证仍交给 OpenSSH。
先确认 Debian 的 OpenSSH 正常运行:
sudo systemctl status ssh
然后关闭 Tailscale SSH:
sudo tailscale set --ssh=false
注意,这不会关闭 Tailscale VPN,只是不再由 Tailscale SSH 接管端口 22。
最后配置 SSH 公钥:
ssh-copy-id -i ~/.ssh/id_ed25519.pub \
user@100.110.20.30
如果本机没有 ssh-copy-id,也可以手动将公钥追加到服务器用户的 ~/.ssh/authorized_keys。
配置完成后,Linux 依然通过 Tailscale IP 访问,但不需要 Tailscale SSH 的网页认证。
这更符合我的日常开发习惯。
九、真正需要重新考虑的是:应该使用哪种 Tailscale 架构?
经历前面的折腾后,我意识到最初其实混淆了两个不同的需求:
需求一:通过代理节点访问 Tailnet 设备。
需求二:让 Mac 在系统网络层面真正加入 Tailnet。
Stash 内置 Tailscale 可以满足第一种需求,而且官方还提供自动 Tailnet 路由功能。
但我更在意第二种需求。
例如,我希望以下操作都尽可能正常工作:
ping 100.110.20.30
ssh user@100.110.20.30
scp file.txt user@100.110.20.30:~/
以及访问:
http://192.168.50.10:8080
在之前的 Stash 内置方案中,SSH 曾经能够恢复,但系统 ping 仍然不通。
这并不能直接证明 Stash 存在 ICMP Bug。
因为代理节点与系统级 IP 路由并不完全等价。ICMP 不通的具体原因还需要在数据包层面验证。
因此,我决定改用 macOS 独立 Tailscale 客户端。
结果是:关闭 Stash 的网络接管、单独运行官方 Tailscale 后,之前无法正常使用的远程访问恢复了。
这也为后续双客户端共存提供了一个明确的基准环境。
十、中间方案:独立 Tailscale + Stash 分流
在这个阶段,需要解决的是:
Stash 和独立 Tailscale 如何在 macOS 上同时运行,并且互不干扰?
Tailscale 官方明确指出,与其他 VPN 同时运行可能产生路由、防火墙规则和地址空间冲突,通常需要通过分流等方式处理。参见Tailscale 与其他 VPN 共存。
Stash 官方也提供了一个专门用于 VPN 共存的功能:
DIRECT with Specified Interface
它允许创建一个 direct 节点,并使用 interface-name 绑定指定网络接口。参见Stash 指定网卡直连。
这正是我们需要的机制。
1. 停用 Stash 内置 Tailscale
首先,停用之前包含:
type: tailscale
的本地 Override。
不要同时保留内置 Tailscale 和独立客户端处理同一个 Tailnet,否则会增加排障复杂度。
2. 确认系统 Tailscale 网卡
保持独立 Tailscale 正常连接,暂时停用 Stash 的网络接管。
执行:
route -n get 100.110.20.30
可能得到:
route to: 100.110.20.30
destination: 100.64.0.0
mask: 255.192.0.0
interface: utun4
flags: <UP,DONE,CLONING,STATIC>
重点看:
interface: utun4
这里的 utun4 是 macOS 为 VPN 使用的虚拟网络接口。
可以理解为:
100.64.0.0/10
↓
utun4
↓
Tailscale
↓
目标设备
需要注意,utun4 并不是永久固定的设备名称。
系统重启、VPN 重连后,接口编号可能变化。因此实际配置时应以本机路由查询结果为准。
同时确认家庭内网路由:
route -n get 192.168.50.20
它也应该能够通过独立 Tailscale 的子网路由到达家庭网络。
3. 新建 Stash 共存 Override
建议不要直接修改在线订阅配置。
在 Stash 中新建本地 Override,命名为:
Tailscale External
配置如下:
name: Tailscale External
desc: Stash 与独立 Tailscale 客户端共存
proxies:
- name: Tailscale-VPN
type: direct
interface-name: utun4
rules:
# Tailscale IPv4 网络
- IP-CIDR,100.64.0.0/10,Tailscale-VPN,no-resolve
# 家庭局域网
- IP-CIDR,192.168.50.0/24,Tailscale-VPN,no-resolve
这是一份最小共存配置。
它没有修改原有代理节点,也没有覆盖在线订阅的 DNS 配置。
4. 配置原理
首先创建一个代理:
proxies:
- name: Tailscale-VPN
type: direct
interface-name: utun4
这里的 direct 并不是简单地让流量通过默认网络接口出去。
由于指定了:
interface-name: utun4
Stash 会尝试将命中的流量通过对应网络接口发送。
然后添加规则:
rules:
- IP-CIDR,100.64.0.0/10,Tailscale-VPN,no-resolve
这里的 100.64.0.0/10 是 Tailscale 默认使用的 IPv4 地址范围。
再加上家庭 LAN:
- IP-CIDR,192.168.50.0/24,Tailscale-VPN,no-resolve
便可以让匹配这些目标地址、由 Stash 接管的流量通过指定接口交给独立 Tailscale。no-resolve 表示不要为匹配这条 IP 规则而主动解析域名;它不负责配置 MagicDNS。
这份 Override 不会自行建立家庭子网路由。NAS 需要已开启 IP 转发、发布 192.168.50.0/24,并在 Tailscale 管理端获得路由批准(或由自动批准策略批准);访问策略也需要允许相应连接。macOS 客户端默认接收子网路由。详见 Tailscale 子网路由文档。
它也不是 ICMP 转发能力的声明。系统 ping 仍需单独测试,不能从 Stash 的 TCP/UDP 规则命中直接推断。
Tailscale 的 IPv6 Tailnet 地址空间为:
fd7a:115c:a1e0::/48
如果实际启用了 IPv6 且需要处理对应流量,可以根据验证情况再补充:
- IP-CIDR6,fd7a:115c:a1e0::/48,Tailscale-VPN,no-resolve
不建议一开始就把所有 DNS、IPv6、Fake-IP 设置同时改掉。
先让最基本的 IPv4 访问工作,再逐项扩展配置,才容易定位问题。
另外,100.64.0.0/10 属于共享地址空间,并非全都专属于 Tailscale。如果本机还有其他服务使用这个网段,应进一步缩小匹配范围,避免误路由。

图 4:Stash 与独立 Tailscale 的流量分流(历史中间方案)。
十一、阶段验证:Stash 双客户端方案成功,以及 utun 的维护问题
完成新的 Override 后,保持独立 Tailscale 连接、Stash 使用规则模式,并停用旧的内置 Tailscale Override,再检查实际访问。
这一步已经完成:普通互联网代理、Tailnet IP、系统 Ping、SSH 和家庭局域网访问全部正常。 这是本次实际使用中确认的结果,不能扩大为所有 macOS、Stash 和 Tailscale 版本的兼容性保证。
| 检查项 | 本次结果 |
|---|---|
| Stash 普通互联网代理 | 正常,继续按原订阅规则处理 |
| 通过 Tailnet IPv4 访问设备 | 正常 |
系统 ping |
正常 |
| SSH 登录 | 正常 |
| 家庭局域网访问 | 正常,通过 NAS 子网路由到达 |
| macOS 重启、客户端重连、切换 Wi-Fi 后持续可用 | 尚未充分验证,需要继续观察 |
下面保留脱敏后的复查命令,便于读者按自己的环境逐项检查。它们是可复用的检查步骤,不是本次完整测试日志:
# 独立 Tailscale 连接后,核对目标路由和接口
route -n get 100.110.20.30
route -n get 192.168.50.20
# 系统 ICMP
ping -c 4 100.110.20.30
# Tailnet IP 上的 SSH
ssh user@100.110.20.30
# 家庭局域网目标:端口测试之后还要实际登录
nc -vz 192.168.50.20 22
ssh user@192.168.50.20
也可以打开 NAS 的实际 Web 服务,例如 http://192.168.50.10:8080,再访问日常需要代理的网站,并在 Stash 连接记录中确认匹配了原有策略。示例端口应替换为实际服务端口。
如果本机已有 Tailscale 命令行,还可使用:
tailscale ping 100.110.20.30
tailscale ping 与系统 ping 验证的层面不同,不能相互替代;本文不把未单独记录的命令输出补写成测试证据。
后续维护:不要把 utun4 当成固定值
utun4 是这次确认的 Tailscale 接口。macOS 重启、VPN 重连、其他 VPN 的启停或切换网络后,接口编号与路由状态可能变化。
如果以后突然无法访问,先保持独立 Tailscale 已连接,暂时停用 Stash 的网络接管,重新运行:
route -n get 100.110.20.30
route -n get 192.168.50.20
确认 Tailnet 和家庭子网的路由,按实际结果更新 interface-name,再启用 Stash 复查。不能因为某个接口仍叫 utun4,就认定它仍属于 Tailscale。
这个阶段的共存成功已经确认;重启、重连和切网后的稳定性仍需后续充分验证。
随后,固定接口名带来的维护问题变得突出:utun4 可能在以后变成另一个编号,配置中写死的接口名就需要重新检查和修改。
我们讨论过删掉 interface-name、依赖系统路由,也考虑过动态检测网卡、更新 Override、触发重载的自动化。前者没有保留可用性验证,后者没有实施;它们属于当时讨论过的方向,不是最终方案。
最后,我选择更换客户端,让 Shadowrocket 的内置 Tailscale 处理家庭网络访问,见第十五节。
十二、Stash 阶段的 DNS 和 Fake-IP:另一个潜在冲突点
除了路由,DNS 也是 Stash 与 Tailscale 共存时可能出现的问题。
Stash 在 Fake-IP 模式下,可能返回:
198.18.x.x
这类虚拟地址由 Stash 接管并映射回域名。
但 Tailscale 的 MagicDNS 希望通过自己的 DNS 机制解析 Tailnet 设备名称。
例如:
linux-home.example-tailnet.ts.net
如果 DNS 请求被 Stash 的 Fake-IP 机制接管,就可能影响基于设备名称的访问。
因此,建议先使用 Tailscale IP 验证连接。
确认 IP 访问稳定之后,再考虑在 Stash DNS 配置中对:
*.ts.net
做 Fake-IP 排除,并检查系统 DNS 是否仍能正确解析 MagicDNS。
这里需要特别强调:
Fake-IP 排除不等于自动实现 MagicDNS。
前者只是避免某些域名被映射成虚拟 IP。
后者还需要确保相应 DNS 请求最终由正确的解析器处理。
由于每个人的 Stash 订阅 DNS 配置不同,不建议直接复制一份可能覆盖现有设置的完整 DNS YAML。
十三、几个容易混淆的概念
在这次排障中,还有几个概念需要分清。
Tailscale Peer 与 Subnet Router
如果 Linux 自己安装了 Tailscale:
Mac
↓
Tailscale
↓
Linux 100.x.x.x
就可以直接访问 Linux。
而如果设备没有安装 Tailscale,则可以通过家庭 NAS 的 Subnet Router 访问:
Mac
↓
Tailscale
↓
NAS
↓
192.168.50.0/24
↓
局域网设备
家庭 NAS 必须正确发布子网路由、开启 IP 转发,并确保路由已被批准、访问策略允许连接。管理端的自动批准策略也可以完成批准;参见 Subnet routers。
Tailscale SSH 与 OpenSSH
Tailscale SSH 负责通过 Tailnet 身份和策略进行 SSH 认证。
OpenSSH 则使用原有的公钥、密码等机制。
如果主要需求是自动化开发,使用原生 OpenSSH 加 Tailscale 组网通常更容易与现有工具集成。
TCP 连通与 ICMP 连通
nc -vz 成功,只能说明客户端所见的 TCP 建连成功,仍需结合代理路径判断。
它并不能证明 SSH 握手完整成功,也不能说明 ICMP 或 UDP 一定正常。
排查时应根据协议逐层验证。
Stash 的 DIRECT 与系统直连
Stash 的普通 DIRECT 不一定能够解决双 VPN 的网络接口选择问题。
而:
type: direct
interface-name: utun4
是明确指定出站网卡的方式。
这也是本文共存配置的关键。
十四、这次折腾最大的收获
回顾整个过程,真正花费时间的并不是安装 Tailscale。
安装本身可能只需要一条命令。
难的是理解不同网络层之间的关系。
最初只想解决:
Mac 离家后如何访问家里的 Linux?
结果依次遇到了:
在线订阅覆盖本地配置
↓
Stash Override
↓
Tailscale 注册与认证
↓
Fake-IP / DNS
↓
Tailnet 地址分流
↓
Subnet Router
↓
SSH Banner 握手
↓
Tailscale SSH Check Mode
↓
ICMP 与 TCP 行为差异
↓
macOS 双 VPN 共存
这些问题表面上都表现为“连不上”,但实际发生在完全不同的层面。
其中最值得记住的三个经验是:
第一,先明确自己需要的是代理访问,还是完整的系统级 IP 网络。
如果只是访问几个 TCP 服务,代理方式可能已经足够。
如果需要 SSH、ICMP、各种开发工具以及更广泛的内网协议,那么应当优先考虑系统级组网能力。
第二,排障时尽量一次只改变一个变量。
这次同时改动了 Stash、Tailscale、NAS 路由和 SSH,导致不少测试结果很难直接归因。
比较稳妥的方法应该是:
先让 Tailscale 单独正常
↓
再让 Stash 单独正常
↓
最后解决两者共存
第三,能连接端口,不代表应用协议已经正常。
TCP 建立成功之后还有 SSH Banner、密钥交换、用户认证等步骤。
遇到问题时,详细日志往往比反复重装软件更有价值。
十五、最终方案:Shadowrocket 内置 Tailscale,一个客户端搞定
最终没有继续维护 Stash 的固定 utun 绑定,也没有开发动态网卡脚本。我换用了 Shadowrocket,并启用了它的内置 Tailscale 支持。
开始时,我以为启用 Tailscale、填入认证 Key 就已经完成。但随后发现,家庭局域网地址仍然访问不了。即使添加了明确指向 TAILSCALE 的 Module,仍需要处理隧道接管范围。
最后生效的关键操作是:在「设置 → 隧道 → 隧道」中,先开启「包含所有网络」,再开启「包含本地网络」。完成这一步,配合内置 Tailscale 与家庭分流 Module,家庭内网才正常可用。Mac 上的独立 Tailscale 客户端已经卸载。
这次选择减少了客户端侧的维护工作。Tailscale 仍然承担私有网络组网,但在 Mac 端由 Shadowrocket 内置模块接入;家庭 Linux 和 NAS 上的 Tailscale 节点继续提供目标设备与子网访问。
1. 前提:家庭端已有可用的 Tailnet
Linux 与 NAS 已加入 Tailnet;需要访问未安装 Tailscale 的家庭设备时,NAS 的子网路由仍需正确发布、获得批准,并具备允许访问的策略。客户端换成 Shadowrocket,不会替代这些家庭端配置。
Shadowrocket 的开发者发布说明记录了内置 Tailscale 隧道、MagicDNS、子网路由等功能。具体可用功能应以所安装版本为准,参见 Shadowrocket App Store 发布说明。本次实际配置还包括家庭网络 Module 和下文的隧道接管设置。
2. 启用内置 Tailscale,并填写 Auth Key
在 Shadowrocket 的设置中找到 Tailscale 功能,启用支持,并在认证 Key 输入项中填入自己的 Tailscale Auth Key。界面入口与名称可能随版本不同,本文没有补写未经记录的逐级菜单路径。
脱敏后的设置摘要如下,这是操作说明,不是可导入配置文件:
客户端:Shadowrocket
Tailscale 支持:启用
认证 Key:<YOUR_AUTH_KEY>
Auth Key 是用于把客户端注册到 Tailnet 的凭据;需要在自己的 Tailscale 管理端生成。它与普通 API 访问令牌用途不同。密钥属性应按实际使用选择,不必为了复现本文强制设为可复用。详见 Tailscale Auth keys。
同时开启 Tailscale 设置中的「使用 Tailscale 子网」,让客户端使用家庭 NAS 发布的子网路由。家庭端仍需发布并批准相应路由。
3. 添加家庭网络分流 Module
创建并启用本地模块,例如 Home-Tailscale.sgmodule。下面统一使用脱敏示例网段 192.168.50.0/24;使用时替换为自己的家庭子网:
#!name=Home Tailscale Routing
#!desc=家庭局域网通过内置 Tailscale 访问
[Rule]
IP-CIDR,192.168.50.0/24,TAILSCALE,no-resolve
IP-CIDR,100.64.0.0/10,TAILSCALE,no-resolve
IP-CIDR6,fd7a:115c:a1e0::/48,TAILSCALE,no-resolve
DOMAIN-SUFFIX,ts.net,TAILSCALE
使用配置分流模式。家庭子网规则明确将匹配流量交给 Shadowrocket 内置 Tailscale,而不是普通代理或一般的 DIRECT。TAILSCALE 策略与模块规则优先级可参考 Shadowrocket 手册维护者的配置示例;这是社区维护资料。
后面三条覆盖 Tailnet IPv4、IPv6 和设备域名访问。它们属于配置内容;本次没有保留 IPv6 和 MagicDNS 的单独测试结果。若其他服务也使用共享地址空间 100.64.0.0/10,应按实际目标缩小范围;设备域名规则也可缩小到自己的 Tailnet 后缀。
4. 最后一个坑:规则写了,流量仍未按预期进入隧道
Module 可以决定已进入分流引擎的流量走哪一个出口,却无法保证系统一定把目标流量交给这个引擎。
本次在 Shadowrocket 中找到的有效操作路径为:
设置 → 隧道 → 隧道
按顺序执行:
- 先开启「包含所有网络」。
- 再开启「包含本地网络」。
- 保持内置 Tailscale 已连接、子网使用选项已开启、家庭分流 Module 已启用,重新访问家庭内网目标。
执行后,家庭内网访问恢复正常。「先所有、再本地」是本次环境中实际成功的操作顺序,不据此认定所有版本都必须遵循同样顺序。
从现象推断,之前的问题与隧道接管范围有关:流量如果绕过 Shadowrocket,Module 规则就没有匹配和转发的机会。这里记录的是设置变化与访问恢复的实测关联,没有抓包证据证明每个失败请求的具体路径,也不能据此断言 Module 本身毫无作用。
最终设置摘要:
| 项目 | 设置 |
|---|---|
| 内置 Tailscale | 启用并连接,Auth Key 使用自己的凭据 |
| 使用 Tailscale 子网 | 开启 |
| 家庭网络分流 Module | 启用,家庭子网指向 TAILSCALE |
| 包含所有网络 | 先开启 |
| 包含本地网络 | 再开启 |
| Mac 独立 Tailscale 客户端 | 已卸载 |
隧道接管范围扩大后,日常局域网设备和切网后的行为也需要按实际需求复查;本文没有把这些未记录的项目写成已通过。
5. 卸载独立客户端,收束为一个软件
这一步已经完成:Mac 上的独立 Tailscale 客户端已卸载。 最终使用的是 Shadowrocket 内置 Tailscale,而非 Shadowrocket 与独立 Tailscale 两个客户端同时运行。
因此,前面 Stash Override 的 interface-name: utun4 不再是最终方案的配置项,也不需要为了维护那一行配置建立 launchd 自动更新脚本。

图 1:最终架构。Mac 只使用 Shadowrocket;家庭 LAN 流量先进入网络隧道,经 Module 交给内置 Tailscale,再通过 NAS 子网路由到达。隧道设置需要先启用「包含所有网络」,再启用「包含本地网络」。
6. 结果与记录边界
最终的实际使用反馈是:启用内置 Tailscale、配置家庭 Module,并按顺序开启两个隧道选项后,家庭内网可以正常使用,一个软件搞定。前面 Stash + 独立 Tailscale 阶段的 Ping、SSH、LAN 等逐项成功结果保留为历史记录。
Shadowrocket 切换后没有保存一套同样完整的逐项复测输出,因此不把旧阶段的日志当成新方案的测试证据。后续复查可按实际需求执行:
# Tailnet IP 上的真实 SSH 会话
ssh user@100.110.20.30
# 家庭子网目标
ssh user@192.168.50.20
# 系统 ICMP 单独检查
ping -c 4 100.110.20.30
同时查看 Shadowrocket 连接记录,确认新建的家庭 LAN 连接命中 TAILSCALE,再访问 NAS Web 服务与日常需要代理的网站。规则命中和实际服务访问应一起检查;最好在离家网络下测试,排除直接通过家庭 Wi-Fi 访问的影响。系统 ICMP、MagicDNS、IPv6以及重启、重连、切网后的行为,应按实际使用逐项确认。客户端的内置支持不等于所有工具和协议的行为都完全相同。
最后:把维护成本也算进方案里
Stash + 独立 Tailscale 的指定网卡方案曾经工作正常,这段排障仍然有价值:它说明了规则、系统路由、SSH 握手和身份认证分别解决什么问题。
但日常使用时,我不希望继续关注 utun 编号,也没有继续给它增加自动化。换成 Shadowrocket,启用内置 Tailscale、填写 Auth Key,添加家庭 Module,再按顺序开启「包含所有网络」和「包含本地网络」之后,家庭内网访问恢复。独立客户端也已卸载,Mac 端配置收束到了一个软件。
这篇记录的最终选择是 Shadowrocket 内置 Tailscale。 选择依据是本次环境中的实际使用效果与维护成本;它不是对所有客户端和版本的普遍评判。
参考资料
本文涉及的配置与技术机制参考以下文档;Shadowrocket 规则示例为社区维护资料,其余为厂商文档或开发者发布说明:
- Stash — Conflict with Other VPNs
- Stash — Proxy Types:DIRECT with Specified Interface
- Stash — Override
- Tailscale — Can I use Tailscale alongside other VPNs?
- Tailscale — Subnet Routers
- Tailscale — Tailscale SSH
- Tailscale — Linux Installation
- Tailscale — Auth keys
- Shadowrocket — App Store 发布说明
- Shadowrocket — 手册维护者的配置示例(社区)
最终结果: Shadowrocket 内置 Tailscale + 家庭网络 Module,配合先开启「包含所有网络」、再开启「包含本地网络」,家庭内网访问已确认正常。独立客户端已卸载,Mac 端由一个软件处理普通代理与家庭网络访问。
最后更新:2026-10-10。