新增 Tailscale 公网访问方案并落地

- 调研对比 Tailscale / Cloudflare Tunnel / frp / QuickConnect,
  自用+大文件+隐藏IP 场景下选定 Tailscale(避开 CF 免费版 100MB 上限)
- NAS 已上线 tailnet(ds925 / 100.95.151.13),Gitea 与 Synology Photos
  公网访问已验证(含 >100MB 视频上传)
- 修复关键坑:mihomo fake-ip 劫持 Tailscale 控制面域名导致 tailscale up
  卡住,在 fake-ip-filter 加入 +.tailscale.com 等并提供修复脚本
- 文档:docs/public-access.md(部署/踩坑/验证)
- 可选件:docker/derp(自建 DERP 中继)
- 同步更新 CLAUDE.md(修正 git 公网地址、新增公网访问小节、端口、TODO)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-05 21:17:46 +08:00
parent d6692b8776
commit 3fc6b402cd
5 changed files with 212 additions and 2 deletions
+24 -2
View File
@@ -51,9 +51,10 @@ NAS 上实际路径:`/volume1/docker/<服务名>/`,与仓库 `docker/` 目
### ForgejoGit 服务)
- **外网地址**`http://git.51yanmei.com/`
- **`git.51yanmei.com` 当前仅局域网可达**:DNS 记录指向 `192.168.3.200`(内网私有 IP、未走 CF 代理),公网打不开
- **局域网直连**`http://192.168.3.200:3000/`
- **SSH Clone**`ssh://git@git.51yanmei.com:2222/`
- **公网访问**:经 Tailscale`http://100.95.151.13:3000`(或 `https://ds925.tail6e8719.ts.net`),见 `docs/public-access.md`
- **SSH Clone(局域网)**`ssh://git@192.168.3.200:2222/`
- **配置方式**:优先通过 docker-compose.yml 的环境变量(`GITEA__section__key`)管理,避免直接改 app.ini
- **app.ini 路径**`/volume1/docker/forgejo/data/gitea/conf/app.ini`(属于 uid=1000wangjia 只读,需 root 修改)
- **数据库**SQLite`/volume1/docker/forgejo/data/gitea/gitea.db`
@@ -126,6 +127,7 @@ ssh nas "ls -lh /volume1/docker/backups/jiu-db/"
| 6690 | Synology Drive |
| 7890 | mihomo 混合代理(HTTP/SOCKS5 |
| 9090 | mihomo 管理 API |
| 41641/udp | Tailscale WireGuard |
| 445 / 139 | SMB 文件共享 |
## Synology 反向代理
@@ -151,8 +153,28 @@ ssh nas "ip route show table 2022 | head -5"
ssh nas "ip addr show Meta"
```
## 公网访问(Tailscale,已上线)
NAS 通过 **TailscaleWireGuard 网格 VPN** 对外提供 Gitea 与 Synology Photos 访问:
零公网暴露、无文件大小限制、不泄露家庭公网 IP。部署与踩坑详见 `docs/public-access.md`
Cloudflare Tunnel 因免费版 100MB 上限不适合 Photos 大文件,故选 Tailscale。)
- **节点**`ds925`Tailscale IP `100.95.151.13`MagicDNS `ds925.tail6e8719.ts.net`
- **身份**Google SSO`wang880812@gmail.com`);Google 仅登录/进后台时需要,日常用不需要
- **CLI 路径**`/var/packages/Tailscale/target/bin/tailscale`(已设 `--operator=wangjia`,免 sudo
- **关键依赖**mihomo 的 `fake-ip-filter` 必须含 `+.tailscale.com` 等(见 mihomo 配置注释),
否则 fake-ip 劫持会导致 `tailscale up` 卡住。修复脚本:`scripts/fix-mihomo-tailscale.sh`
```bash
ssh nas "/var/packages/Tailscale/target/bin/tailscale status" # 查看节点与对端连接
```
## TODO
- [x] NAS 安装 Tailscale 套件并登录,Gitea/Photos 公网访问已验证(含 >100MB 上传)
- [ ] 后台对 `ds925` 关闭 key expiry(否则 ~180 天掉线需重新过 Google 登录)
- [ ] (可选)`tailscale serve` 给 Gitea 套 HTTPS(解决 Arc 等强制 HTTPS 浏览器超时)
- [ ] (可选)新加坡 EC2 自建 DERP 中继 / 把 Tailscale UDP 排除出 mihomo,从香港中继提速到 P2P 直连(`docker/derp/`
- [ ] wangjia 用户加入 docker 组,方便 SSH 管理容器
- [ ] dnsmasq 启用(全局 DHCP 分发,让其他设备也自动走透明代理)
- [ ] Forgejo app.ini 的 DOMAIN/SSH_DOMAIN/ROOT_URL 改为 `git.51yanmei.com`(需 rootdocker-compose 环境变量已覆盖,改 app.ini 仅为保持一致)
+21
View File
@@ -0,0 +1,21 @@
# 自建 Tailscale DERP 中继节点(可选)
# 部署位置:新加坡 EC218.136.60.128),不是 NAS。
# 作用:P2P 打洞失败时,让 Tailscale 回退中继走自己的新加坡节点,缓解国内默认 DERP 慢的问题。
#
# 前置:
# 1. 在 EC2 安全组放行 TCP 443、UDP 3478STUN)。
# 2. 准备一个解析到 EC2 的域名(如 derp.51yanmei.com,走灰云/DNS-only),用于自动签发证书。
# 3. 在 Tailscale 管理后台的 ACL 中通过 derpMap 登记本节点后才会生效。
services:
derper:
image: ghcr.io/yangchuansheng/derper:latest
container_name: derper
restart: unless-stopped
network_mode: host
environment:
DERP_DOMAIN: derp.51yanmei.com # 改为实际域名
DERP_ADDR: ":443"
DERP_STUN: "true"
DERP_VERIFY_CLIENTS: "false" # 如需仅限自己 tailnet 使用可设 true(需本机也跑 tailscaled
volumes:
- ./certs:/root/.cache/tailscale # 证书缓存
+5
View File
@@ -30,6 +30,11 @@ dns:
- 'time.*.com'
- 'ntp.*.com'
- '+.pool.ntp.org'
# Tailscale 控制面/DERP 必须解析为真实 IP,否则 fake-ip 会导致 tailscaled
# 连不上控制服务器、tailscale up 卡住不打印登录 URL。详见 docs/public-access.md
- '+.tailscale.com'
- '+.tailscale.io'
- '+.tailscale-dns.com'
nameserver:
- 114.114.114.114
- 223.5.5.5
+110
View File
@@ -0,0 +1,110 @@
# NAS 公网访问(Tailscale 方案)
让外网设备(移动网络、外出、公司)安全访问 NAS 上的 **Forgejo/Gitea**
**Synology Photos**(含 >100MB 视频 / RAW 大文件上传)。
> 选型与评分对比见仓库根目录评审记录。结论:自用/家人 + 需大文件上传 + 隐藏家庭 IP 的
> 场景下,**TailscaleWireGuard 网格 VPN)** 是最优解 —— 零公网暴露、无文件大小限制、
> 不泄露家庭真实公网 IP。Cloudflare Tunnel 因免费版单文件 100MB 上限不适合 Photos 大文件。
## 现状提醒
- `git.51yanmei.com` 的 DNS 记录指向 `192.168.3.200`(局域网私有 IP,未走 Cloudflare 代理),
**目前仅在局域网可达,公网打不开**
- 家宽 WAN 实测有公网 IPnetcheck 显示 `221.223.98.165`UPnP 端口映射可用),但仍用
Tailscale 走隧道,不暴露任何端口。
## 实际部署记录(本 NAS
- 节点:`ds925`Tailscale IP **`100.95.151.13`**MagicDNS 名 `ds925.tail6e8719.ts.net`
- 账号身份:Google SSO`wang880812@gmail.com`)。**Google 仅在「设备登录 / 进管理后台」时需要**
(国内需 Shadowrocket 等代理过一下);登录后日常用 VPN 不碰 Google。NAS 已登录,
**务必在后台对 `ds925` 关闭 key expiry**,否则 ~180 天后掉线需重新过 Google。
- 当前 iPhone ↔ NAS 走 **DERP 香港中继**(~115ms),够用;想要 P2P 直连更快见下方「自建 DERP / 优化」。
### ⚠️ 关键坑:mihomo fake-ip 会劫持 Tailscale 登录
本 NAS 出网经 mihomo 透明代理(fake-ip DNS)。默认情况下 `controlplane.tailscale.com`
会被解析成 fake-ip`198.18.x.x`),导致 **tailscaled 连不上控制面、`tailscale up` 卡住不打印登录 URL**
(日志表现为 `StartLoginInteractive: url=false``Shutdown` 死循环)。
**解决**:把 Tailscale 域名加入 mihomo 的 `dns.fake-ip-filter`,让其解析为真实 IP。
已封装为脚本 **`scripts/fix-mihomo-tailscale.sh`**(幂等、自动备份、热重载)。在 NAS 上执行:
```bash
ssh nas 'sh -s' < scripts/fix-mihomo-tailscale.sh
```
加入的过滤项:`+.tailscale.com` / `+.tailscale.io` / `+.tailscale-dns.com`
### 浏览器注意
Gitea 是纯 HTTP`:3000`)。**Arc 等会强制 HTTPS-upgrade 的浏览器访问会超时**——
要么手动打全 `http://`,要么用 `tailscale serve` 给服务套上 `*.ts.net` 的有效 HTTPS 证书:
```bash
/var/packages/Tailscale/target/bin/tailscale serve --bg https / http://127.0.0.1:3000
# 之后用 https://ds925.tail6e8719.ts.net/ 访问,全浏览器无告警
```
## 架构
```
外网设备(手机/笔记本,装 Tailscale App
└─ WireGuard 加密隧道(P2P 直连,打洞失败时回退 DERP 中继)
└─ NASDSM 套件 TailscaleTailscale IP 100.x.y.z
├─ Forgejo/Gitea :3000 (HTTP) / :2222 (SSH)
└─ Synology Photos (走 NAS Tailscale IP,无大小限制)
```
无任何对外开放端口,家庭公网 IP 不出现在任何公网记录中。
## 部署步骤
### 1. NAS 安装 Tailscale
- DSM → **套件中心** → 搜索安装官方 **Tailscale** 套件。
- 打开套件 → 登录 Tailscale 账号(`tailscale up`)。
- 在 [Tailscale 管理后台](https://login.tailscale.com/admin) → DNS → 开启 **MagicDNS**
- 不需要开 subnet router(直接访问 NAS 本机即可),DSM 防火墙保持不变。
### 2. 各访问设备
- 手机 / 笔记本安装 Tailscale App,登录**同一账号**,进入同一 tailnet。
### 3. 访问服务
| 服务 | 访问方式 |
|------|----------|
| Gitea Web | `http://nas:3000`MagicDNS)或 `http://100.x.y.z:3000` |
| git clone (HTTPS) | 走 `:3000` |
| git clone (SSH) | `ssh://git@100.x.y.z:2222/...` |
| Synology Photos | App「自定义地址 / IP」填 NAS 的 Tailscale IP`100.x.y.z`),可上传大文件 |
`nas` 为 MagicDNS 主机名(以管理后台实际显示为准)。
## 可选增强
### A. tailnet 内 HTTPS(免自签证书告警)
```bash
# 在 NAS 上,把 Gitea 3000 端口以 https 暴露到 tailnet(自动签发 *.ts.net 证书)
tailscale serve https / http://127.0.0.1:3000
```
### B. 偶尔对外公开分享(对方无需装 Tailscale)
```bash
# 把服务发布到公网 443(受 Tailscale 带宽限制,适合临时分享单个链接)
tailscale funnel 3000
```
> Funnel 仅支持 443 / 8443 / 10000 端口、且仅 HTTPS。
### C. 提升中国网络稳定性:自建 DERP 中继
P2P 打洞失败时会回退到 Tailscale 官方 DERP,国内可能慢。可在已有的新加坡 EC2
`18.136.60.128`)上自建 DERP 节点(见 `docker/derp/`),并在管理后台 ACL 的
`derpMap` 中登记,让回退中继走新加坡而非默认节点。
## 安全加固
- 用 Tailscale **ACL** 限制「哪些设备能访问 NAS」,最小授权。
- 保留 DSM 的账号 2FA 与自动封锁(IP auto-block)。
- 不在路由器上做任何端口转发。
## 端到端验证
1. 手机断开家庭 WiFi → 切 4G/5G(确保走公网)→ 开 Tailscale。
2. 浏览器开 `http://nas:3000` → Gitea 登录页能打开,能 push/clone。
3. Synology Photos App 连 NAS Tailscale IP → **上传一个 >100MB 的视频**,确认成功。
4. 公网侧 `nslookup git.51yanmei.com` 仍解析到内网 IP → 确认未暴露家庭公网 IP。
5. `tailscale status` 查看连接是 `direct` 还是 `relay`,评估链路质量。
+52
View File
@@ -0,0 +1,52 @@
#!/bin/sh
# 让 Tailscale 域名在 mihomo 里解析为真实 IP(绕开 fake-ip 劫持),
# 否则 tailscaled 连不上控制服务器、登录时拿不到 URL。
# 该脚本在 NAS 上运行;config.yaml 归 wangjia 所有,无需 root。
# 幂等:重复执行不会重复插入。改前自动备份,并热重载生效(不重启容器)。
set -e
CFG=/volume1/docker/proxy/mihomo/config.yaml
echo "==> 1/4 备份配置"
cp "$CFG" "$CFG.bak.tailscale"
echo " 备份: $CFG.bak.tailscale"
echo "==> 2/4 写入 fake-ip-filter(幂等)"
python3 - "$CFG" <<'PY'
import sys
p = sys.argv[1]
text = open(p).read()
if "+.tailscale.com" in text:
print(" 已存在,跳过插入")
sys.exit(0)
lines = text.splitlines(True)
add = [
" - '+.tailscale.com'\n",
" - '+.tailscale.io'\n",
" - '+.tailscale-dns.com'\n",
]
out, done = [], False
for ln in lines:
out.append(ln)
if (not done) and "+.pool.ntp.org" in ln:
out.extend(add)
done = True
if not done:
print(" !! 没找到锚点 '+.pool.ntp.org',未修改,请人工检查")
sys.exit(1)
open(p, "w").writelines(out)
print(" 已插入 3 条 tailscale 域名")
PY
echo "==> 3/4 热重载 mihomo"
curl -s -X PUT 'http://127.0.0.1:9090/configs?force=true' \
-H 'Content-Type: application/json' \
-d '{"path":"/root/.config/mihomo/config.yaml"}'
echo " reload 已请求"
echo "==> 4/4 验证解析(期望是真实公网 IP,不再是 198.18.x"
sleep 1
nslookup controlplane.tailscale.com 2>&1 | tail -3
echo "==> 完成。现在回到卡住的窗口按 Ctrl+C,重新执行:"
echo " sudo /var/packages/Tailscale/target/bin/tailscale up --reset --operator=wangjia"