diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..1306c24 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,56 @@ + + + + + +NAS 文档索引 + + + +
+

NAS 文档索引

+

Synology DS925+(192.168.3.200)运维文档单一入口 · 仓库 ~/code/nas

+ +

设计方案

+ + + + + + + +
文档状态说明
家庭内网远程访问 —— frp 反向隧道 × pangolin × 分流 DNS待评审内外同域名访问 NAS/Windows:frpc(NAS)→frps(pangolin1) 反向隧道 + split-horizon DNS + pangolin 客户端分流
+ +

知识库 / 运维

+ + + + + + + +
文档状态说明
NAS 公网访问(Tailscale 方案)已上线Tailscale 部署记录与踩坑(mihomo fake-ip 劫持等);frp 方案上线后保留作应急后门与部署跳板
+
+ + diff --git a/docs/remote-access-frp.html b/docs/remote-access-frp.html new file mode 100644 index 0000000..b8ce9a6 --- /dev/null +++ b/docs/remote-access-frp.html @@ -0,0 +1,424 @@ + + + + + +家庭内网远程访问方案 —— frp 反向隧道 × pangolin × 分流 DNS + + + +
+← 文档索引 +

家庭内网远程访问方案
frp 反向隧道 × pangolin × 分流 DNS

+

2026-07-15 · 设计方案 · 待评审 · 相关:Tailscale 公网访问(现状,保留作应急后门)

+ +
+目标:①在家直连内网、不出外网;②在外开着 pangolin 就能穿透回家(不装第二个 VPN 客户端);③内外用同一套地址——git.yanmeiai.com(Forgejo)、nas.yanmeiai.com(DSM/Photos)、win.yanmeiai.com(Windows RDP/SSH)。
+核心思路:NAS 上的 frpc 主动外连 pangolin1 上的 frps 建反向隧道(家里零开放端口、不暴露家庭 IP);同一批域名公网解析到 pangolin1、家内 DNS 解析到内网 IP(split-horizon);pangolin 客户端配置加一条「这几个域名走本地 DNS、私网结果直连」的分流规则,保证 pangolin 常开时内外自动走对路。 +
+ +

1 · 现状盘点(事实依据)

+ + + + + + + +
对象现状与本方案的关系
NAS(DS925+)192.168.3.200;跑 Forgejo(:3000 HTTP / :2222 SSH)、mihomo 透明代理(:53 fake-ip DNS / TUN)、Tailscale(100.95.151.13装 frpc;mihomo 加 hosts 覆盖;Tailscale 作本次部署跳板
Windows192.168.3.88,网关/DNS 均指向 NAS;有 OpenSSH server(ssh windows基本不改;frpc 从 NAS 直接转发其 RDP/SSH
pangolin1103.119.13.48,Debian 12,2C/1G;:443 sing-box REALITY、:8080 控制面(仅 127.0.0.1)、:9443 gRPC、cloudflared 出站隧道;ufw 默认拒入站装 frps(~20MB 内存);7000/2222/3000/3389/5001/10022/10023 均空闲无冲突
家宽有真实公网 IP(动态),路由器无端口映射保持零开放端口,不做 DDNS
DNSyanmeiai.com 在 Cloudflare(管理走 cf-api);git.51yanmei.com 公网 A 记录指 192.168.3.200(仅家内可达)新增 3 条 DNS-only A 记录;旧 git 域名可选迁移
+ +

2 · 架构总览

+
+ + + 在外(pangolin 开) + + Mac / 手机 + pangolin TUN + + + + REALITY 隧道 + + + pangolin1 · 103.119.13.48 + + sing-box + + frps + + + 机内回环(hairpin):出口连自己 + 业务端口 ufw 不开放 → 外网扫不到 + + + + frp 反向长连接 + (家里主动拨出,TLS+token) + + + 家庭内网 192.168.3.0/24 + + NAS .200 + frpc·git·DSM·DNS + + Windows .88 + RDP 3389 · SSH 22 + + + 路由器零端口映射,家庭 IP 不出现在公网 DNS + + + 在家(pangolin 照样开) + + Mac / 手机 + pangolin TUN + + + 客户端分流规则(BuildClientConfig 下发) + 这 3 个域名 → 本地 DNS 解析 → 得内网 IP + 私网地址命中 LAN 直连规则 → 不进隧道 + + + + + + + 纯局域网直连 + + + NAS .200 / Windows .88 + mihomo hosts 把 3 个域名 + 解析成内网 IP + + + + 同一个域名,两处答案(split-horizon): + git.yanmeiai.com + 公网 DNS(Cloudflare)→ 103.119.13.48(pangolin1/frps) + 家内 DNS(NAS mihomo)→ 192.168.3.200(直连) + +
+ +

3 · 统一地址与端口规划

+

选端口的原则:内外端口号一致(同一个 URL 全球通用)。唯一例外是裸 SSH——:22 在 pangolin1 上被它自己的 sshd 占用,只能错位到 10022/10023,由 ~/.ssh/config 别名消化(见 §7)。

+ + + + + + + + +
服务统一地址frpc 转发目标(家内)pangolin1 remotePort备注
Forgejo Git SSHssh://git@git.yanmeiai.com:2222127.0.0.1:22222222内外一致 ✓
Forgejo Webhttp://git.yanmeiai.com:3000127.0.0.1:30003000内外一致 ✓
DSM / Photoshttps://nas.yanmeiai.com:5001127.0.0.1:50015001内外一致 ✓;Photos App 填这个地址
Windows RDPwin.yanmeiai.com:3389192.168.3.88:33893389内外一致 ✓
NAS SSH外:nas.yanmeiai.com:10022
家:192.168.3.200:22
127.0.0.1:2210022端口错位,ssh alias 兜底
Windows SSH外:win.yanmeiai.com:10023
家:192.168.3.88:22
192.168.3.88:2210023端口错位,ssh alias 兜底
+

不转发的:SMB/445(协议不适合过公网,文件走 Synology Drive/Photos 或 git);DSM 5000(明文 HTTP,只留 5001 HTTPS);mihomo 9090 管理口(有需要时临时经 NAS SSH 端口转发)。

+ +

4 · 安全模型(三层)

+
+

第一层:业务端口对公网「隐形」(关键设计)

+

pangolin1 的 ufw 默认拒绝入站。frps 的业务端口(2222/3000/3389/5001/10022/10023)一条 allow 规则都不加。你在外面走 pangolin 时,流量从 sing-box 出站连 103.119.13.48——目的地是本机自己的公网 IP,内核走 loopback 回环(hairpin)送达,而 ufw 对 lo 接口默认全放行。效果:这些端口只有「连着 pangolin 的你」能访问,公网扫描器看到的是全拒。RDP 这种绝不能裸奔的服务因此是安全的。

+

验证兜底:若实测 hairpin 未走 lo(§9 验收步骤 P1-4 会测),补一条 ufw allow from 103.119.13.48 等价实现,语义不变。

+
+
+

第二层:frp 控制通道加固

+

:7000 是唯一新开的公网端口(frpc 从家里拨入用,家宽 IP 动态、无法限源)。加固:auth.token(32 字节随机,存 Bitwarden)+ transport.tls.force = true(强制 TLS,未握手连接直接断)。

+
+
+

第三层:业务自身认证不放松

+

Forgejo 密钥登录、DSM 账号 2FA + 自动封锁、Windows RDP 强密码/NLA——原有认证全部保留,隧道只是网络通路。Tailscale 保留不动,作为 frp 链路故障时的应急后门。

+
+ +

5 · pangolin1 怎么配(装 frps)

+

机器:ssh pangolin1 · 人在外也能直接做(公网可达)

+

5.1 生成 token(本机 Mac 上)

+
# 生成 32 字节随机 token,存进 Bitwarden(条目名:frp home-tunnel)
+openssl rand -hex 32
+rbw add "frp home-tunnel"   # 密码字段贴上面的输出;两端配置从这里取,不落明文文件
+

5.2 安装 frps 二进制 + systemd

+
# ssh pangolin1 之后(root)。VER 以 frp releases 最新版为准,下例为示意
+VER=0.61.1
+curl -LO https://github.com/fatedier/frp/releases/download/v${VER}/frp_${VER}_linux_amd64.tar.gz
+tar xzf frp_${VER}_linux_amd64.tar.gz
+install -m 755 frp_${VER}_linux_amd64/frps /usr/local/bin/frps
+mkdir -p /etc/frp
+

/etc/frp/frps.tomltoken 不入 git,与 /etc/pangolin* 密钥同规矩):

+
bindAddr = "0.0.0.0"
+bindPort = 7000
+auth.method = "token"
+auth.token = "<Bitwarden: frp home-tunnel>"
+transport.tls.force = true          # 拒绝非 TLS 的 frpc
+# 不开 dashboard;业务端口按 frpc 请求动态监听
+allowPorts = [
+  { start = 2222, end = 2222 }, { start = 3000, end = 3000 },
+  { start = 3389, end = 3389 }, { start = 5001, end = 5001 },
+  { start = 10022, end = 10023 },
+]                                    # 白名单锁死,token 泄露也开不了别的口
+

/etc/systemd/system/frps.service

+
[Unit]
+Description=frp server (home reverse tunnel)
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
+Restart=always
+RestartSec=5
+User=nobody                          # 非特权端口,无需 root
+LimitNOFILE=65536
+# 1G 小机安全带:内存超限就重启,绝不拖垮 sing-box/控制面
+MemoryMax=128M
+
+[Install]
+WantedBy=multi-user.target
+
chmod 600 /etc/frp/frps.toml && chown nobody /etc/frp/frps.toml
+systemctl daemon-reload && systemctl enable --now frps
+ufw allow 7000/tcp comment 'frp control (frpc dial-in from home)'
+# 验证:只多了 7000 这一个对外口
+systemctl status frps --no-pager | head -5
+ss -tlnp | grep frps
+

⚠️ 台账提醒:pangolin1 续费到期 2026-07-19(baize billing.renew_at)——动工前先确认已续费。

+ +

6 · DNS 怎么配(Cloudflare + 家内 mihomo)

+

6.1 公网侧:Cloudflare 加 3 条 A 记录(本机 Mac,cf-api)

+

必须 DNS only(灰云,proxied:false——git SSH / RDP 是任意 TCP,CF 橙云只代理 HTTP。zone id 见 baize:2325730de45276d87180a8b66bd4cca0

+
cf-api -X POST "/zones/2325730de45276d87180a8b66bd4cca0/dns_records" \
+  --data '{"type":"A","name":"nas","content":"103.119.13.48","proxied":false,"ttl":300}'
+cf-api -X POST "/zones/2325730de45276d87180a8b66bd4cca0/dns_records" \
+  --data '{"type":"A","name":"git","content":"103.119.13.48","proxied":false,"ttl":300}'
+cf-api -X POST "/zones/2325730de45276d87180a8b66bd4cca0/dns_records" \
+  --data '{"type":"A","name":"win","content":"103.119.13.48","proxied":false,"ttl":300}'
+

代价说明:灰云记录会暴露 pangolin1 的 IP——它本来就是 VPN 数据面入口(客户端 endpoint 直连该 IP),无新增暴露。改完回写 baize domains.yaml

+

6.2 家内侧:NAS mihomo 加 hosts 覆盖

+

/volume1/docker/proxy/mihomo/config.yaml 两处改动(仓库 docker/proxy/mihomo/config.yaml 同步改):

+
hosts:                              # ① 家内答案:直接给内网 IP
+  nas.yanmeiai.com: 192.168.3.200
+  git.yanmeiai.com: 192.168.3.200
+  win.yanmeiai.com: 192.168.3.88
+  git.51yanmei.com: 192.168.3.200  # 兼容既有 git remote(可选,见 §10)
+
+dns:
+  fake-ip-filter:                   # ② 追加:不给这几个域名发 fake-ip,走 hosts 真实值
+    # ……既有条目保持不动(含 +.tailscale.com 等)……
+    - nas.yanmeiai.com
+    - git.yanmeiai.com
+    - win.yanmeiai.com
+    - git.51yanmei.com
+
# 热重载(不重启容器),沿用仓库既有做法
+ssh nas "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\"}'"
+# 验证:家内解析应返回 192.168.3.200
+ssh nas "nslookup git.yanmeiai.com 127.0.0.1"
+

6.3 路由器:DHCP 的 DNS 指到 NAS(回家后做)

+

Windows 已手动指 NAS;要让 Mac/手机在家也吃到 hosts 覆盖,把路由器 DHCP 下发的 DNS 改为 192.168.3.200(一处改动、全家生效)。取舍:NAS 由此成为家内 DNS 单点——不要配第二个公共 DNS 兜底(客户端会随机用它,覆盖时灵时不灵);NAS 重启的几分钟内家里断解析,可接受。

+ +

7 · NAS 怎么配(装 frpc)

+

仓库新增:docker/frp/docker-compose.yml + docker/frp/frpc.toml + docker/frp/.env.example · NAS 路径:/volume1/docker/frp/

+

沿用本仓库 docker-compose 惯例(forgejo/proxy 同款)。wangjia 不在 docker 组,部署用 sudo 或 Container Manager UI。

+

docker-compose.yml

+
services:
+  frpc:
+    image: snowdreamtech/frpc:0.61   # 多架构维护镜像;版本与 frps 对齐
+    container_name: frpc
+    network_mode: host                # 需直达 127.0.0.1 各服务 + 192.168.3.88
+    restart: unless-stopped
+    volumes:
+      - ./frpc.toml:/etc/frp/frpc.toml:ro
+

frpc.toml(token 同样不入 git,仓库放 frpc.toml.example 占位):

+
serverAddr = "103.119.13.48"
+serverPort = 7000
+auth.method = "token"
+auth.token = "<Bitwarden: frp home-tunnel>"
+transport.tls.enable = true
+
+[[proxies]]
+name = "git-ssh"
+type = "tcp"
+localIP = "127.0.0.1"
+localPort = 2222
+remotePort = 2222
+
+[[proxies]]
+name = "git-web"
+type = "tcp"
+localIP = "127.0.0.1"
+localPort = 3000
+remotePort = 3000
+
+[[proxies]]
+name = "dsm"
+type = "tcp"
+localIP = "127.0.0.1"
+localPort = 5001
+remotePort = 5001
+
+[[proxies]]
+name = "nas-ssh"
+type = "tcp"
+localIP = "127.0.0.1"
+localPort = 22
+remotePort = 10022
+
+[[proxies]]
+name = "win-rdp"
+type = "tcp"
+localIP = "192.168.3.88"       # 同网段代转 Windows,一个 frpc 管全家
+localPort = 3389
+remotePort = 3389
+
+[[proxies]]
+name = "win-ssh"
+type = "tcp"
+localIP = "192.168.3.88"
+localPort = 22
+remotePort = 10023
+
# 部署(经 Tailscale 跳板 ssh 到 NAS 后)
+sudo mkdir -p /volume1/docker/frp && cd /volume1/docker/frp
+# (把仓库里的两个文件放进来,frpc.toml 填真 token)
+sudo docker compose up -d
+sudo docker logs frpc --tail 20      # 应见 "login to server success" + 6 个 proxy 启动成功
+

Mac 的 ssh 别名收尾(~/.ssh/config

+
# 既有 Host nas / windows(内网 IP 直连)保持不动,家里继续用
+Host nas-r                            # 在外用:ssh nas-r
+  HostName nas.yanmeiai.com
+  Port 10022
+  User wangjia
+
+Host win-r                            # 在外用:ssh win-r
+  HostName win.yanmeiai.com
+  Port 10023
+  User wangjia
+ +

8 · Windows 怎么改(几乎不用)

+ + + + + + +
动作说明
网络不改网关/DNS 已指 NAS,天然吃到 hosts 覆盖
RDP确认已启用(设置 → 系统 → 远程桌面),保持 NLA 开启frpc 从 NAS(同网段 .200)连入,默认防火墙「专用网络」范围即放行
OpenSSH server已有ssh windows 在用),确认服务设为自动启动同上,无需改防火墙
防火墙仅当连不通时检查:RDP/SSH 入站规则的作用域含本地子网不需要也不要开「任意来源」
+ +

9 · pangolin 客户端分流(BuildClientConfig,让家里直连成立)

+

代码:server/internal/httpapi/clientconfig.go::BuildClientConfig · 单独一刀,四端零改动(配置服务端渲染下发)

+

pangolin TUN 的首条路由规则是 hijack-dns,所有 DNS 都被劫去隧道解析——家里也会拿到公网 IP、绕道外网。解法是给这几个域名开「本地解析 + 按结果路由」的侧门:

+
// 伪代码示意。域名清单走 server 配置(env PRIVATE_SPLIT_DOMAINS),默认空 = 行为不变,
+// 不影响其他用户。
+// ① dns.servers 增本地解析器
+{ "tag": "dns-local", "address": "local", "detour": "direct" }
+// ② dns.rules 前插:这 3 个域名用本地 DNS(家=mihomo hosts→内网IP;外=公网DNS→pangolin1)
+{ "domain": ["nas.yanmeiai.com","git.yanmeiai.com","win.yanmeiai.com"], "server": "dns-local" }
+// ③ route.rules 在 hijack-dns 之后插:先解析再按 IP 匹配后续规则
+{ "domain": ["…同上…"], "action": "resolve" }
+// ④ 解析结果为私网 → 命中既有 LAN 直连规则 → 家里直连;
+//    公网 IP(pangolin1)→ 不匹配 → 走默认代理出口 → 机内 hairpin 到 frps
+

实现时必须验证action: resolve 是 sing-box ≥ 1.11 语法——先确认四端内嵌 libbox 版本;不够就先升 libbox(scripts/build-libbox.sh 重编,产物 gitignore 需各端重新放置)。按仓库惯例先在 macOS 端端到端验证,再推 iOS/Android/Windows。

+

此刀未落地前的过渡用法:在外一切正常(隧道 DNS 拿公网 IP 本来就该走隧道);在家访问这几个域名会绕外网一圈(功能可用、不满足目标①),或临时关 pangolin 直连。

+ +

10 · 执行顺序(人在外,现在就能开工)

+

P0 · 前置(5 分钟)

+ +

P1 · pangolin1 装 frps(直接可做,§5.2)

+ +

P2 · Cloudflare 加 3 条灰云 A 记录(直接可做,§6.1)

+ +

P3 · 经 Tailscale 跳板配 NAS(§6.2 + §7)

+ +

P4 · 在外端到端验收(pangolin 开着)

+ +

P5 · 回家后收尾(§6.3)

+ +

P6 · pangolin 分流规则(单独一刀,§9)

+ +

P7 · 台账与文档回写

+ + +

11 · 风险与回滚

+ + + + + + + + + +
风险影响缓解 / 回滚
NAS 成家内 DNS 单点NAS 挂 → 全家断解析接受(NAS 稳定);应急把路由器 DHCP DNS 改回运营商
token 泄露他人可向 frps 注册代理allowPorts 白名单锁死端口范围;业务端口本就 ufw 隐形;换 token 重启两端即失效
frps 占用拖累 1G 小机控制面/数据面受影响systemd MemoryMax=128M,超限自杀重启,隔离爆炸半径
hairpin 假设不成立在外连不上业务端口P1-4 提前实测;兜底 ufw allow from 103.119.13.48
pangolin1 迁移/换 IP公网侧全断frps 配置随 deploy 脚本走;改 3 条 A 记录即恢复;家内直连不受影响
libbox 版本不支持 resolveP6 延期P1–P5 不依赖 P6;过渡期家里关 pangolin 或接受绕行
整体回滚NAS:docker compose down;pangolin1:systemctl disable --now frps + 删 ufw 7000;CF 删 3 条记录;mihomo 还原备份重载。全程不动业务服务
+ +

12 · 可选增强(不阻塞主线)

+ + +
+ +