feat(frp): 远程访问一键部署脚本(frpc+mihomo分流DNS) + 配置模板;文档标执行中

This commit is contained in:
wangjia
2026-07-15 00:53:56 +08:00
parent 67c02458c2
commit 9aa5edbdef
6 changed files with 274 additions and 3 deletions
+8
View File
@@ -0,0 +1,8 @@
services:
frpc:
image: snowdreamtech/frpc:0.70.0
container_name: frpc
network_mode: host # 需直达 127.0.0.1 各服务 + 192.168.3.88Windows
restart: unless-stopped
volumes:
- ./frpc.toml:/etc/frp/frpc.toml:ro
+49
View File
@@ -0,0 +1,49 @@
# 复制为 frpc.toml 并把 token 换成真值(Bitwarden「frp home-tunnel」)。
# frpc.toml 含密钥,不入 git(部署由 scripts/setup-frp-remote.sh 自动完成)。
serverAddr = "103.119.13.48" # pangolin1frps
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"
localPort = 3389
remotePort = 3389
[[proxies]]
name = "win-ssh"
type = "tcp"
localIP = "192.168.3.88"
localPort = 22
remotePort = 10023
+2 -2
View File
@@ -37,8 +37,8 @@
<tr><th>文档</th><th>状态</th><th>说明</th></tr>
<tr>
<td><a href="remote-access-frp.html">家庭内网远程访问 —— frp 反向隧道 × pangolin × 分流 DNS</a></td>
<td><span class="tag warn">待评审</span></td>
<td>内外同域名访问 NAS/Windowsfrpc(NAS)→frps(pangolin1) 反向隧道 + split-horizon DNS + pangolin 客户端分流</td>
<td><span class="tag warn">执行中</span></td>
<td>内外同域名访问 NAS/Windowsfrpc(NAS)→frps(pangolin1) 反向隧道 + split-horizon DNS + pangolin 客户端分流NAS 侧一键脚本 <code>scripts/setup-frp-remote.sh</code></td>
</tr>
</table>
+1 -1
View File
@@ -51,7 +51,7 @@
<div class="wrap">
<a class="back" href="index.html">← 文档索引</a>
<h1>家庭内网远程访问方案<br>frp 反向隧道 × pangolin × 分流 DNS</h1>
<p class="sub">2026-07-15 · <span class="tag warn">设计方案 · 待评审</span> · 相关:<a href="public-access.md">Tailscale 公网访问(现状,保留作应急后门)</a></p>
<p class="sub">2026-07-15 · <span class="tag info">执行中</span> P0P2 已完成(frps@pangolin1 运行中、hairpin 实测 OK、CF 三条 A 记录已生效);P3 待跑 <code>scripts/setup-frp-remote.sh</code> · 相关:<a href="public-access.md">Tailscale 公网访问(现状,保留作应急后门)</a></p>
<div class="lead">
<b>目标:</b>①在家直连内网、不出外网;②在外<b>开着 pangolin</b> 就能穿透回家(不装第二个 VPN 客户端);③内外用<b>同一套地址</b>——<code>git.yanmeiai.com</code>Forgejo)、<code>nas.yanmeiai.com</code>DSM/Photos)、<code>win.yanmeiai.com</code>Windows RDP/SSH)。<br>
+188
View File
@@ -0,0 +1,188 @@
#!/bin/bash
# 在 NAS 上执行(由 setup-frp-remote.sh 上传并调用,也可手动跑):
# ① 写 frpc 配置 + docker compose 拉起(需 sudo,会提示输 DSM 密码)
# ② mihomo 加 hosts 覆盖 / fake-ip-filter / use-hosts(幂等,自动备份,热重载)
# ③ 端到端验证
# 前提:/tmp/frp-setup/token 里是 frp auth token(由远端脚本从 Bitwarden 送来)。
set -euo pipefail
FRP_DIR=/volume1/docker/frp
CFG=/volume1/docker/proxy/mihomo/config.yaml
TOKEN_FILE=/tmp/frp-setup/token
[ -s "$TOKEN_FILE" ] || { echo "!! 缺 token 文件 $TOKEN_FILE(应由 setup-frp-remote.sh 生成)"; exit 1; }
read -r TOKEN < "$TOKEN_FILE"
echo "==> 1/6 写 frpc.toml + docker-compose.ymlsudo"
sudo mkdir -p "$FRP_DIR"
sudo tee "$FRP_DIR/frpc.toml" > /dev/null <<EOF
serverAddr = "103.119.13.48"
serverPort = 7000
auth.method = "token"
auth.token = "$TOKEN"
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"
localPort = 3389
remotePort = 3389
[[proxies]]
name = "win-ssh"
type = "tcp"
localIP = "192.168.3.88"
localPort = 22
remotePort = 10023
EOF
sudo chmod 600 "$FRP_DIR/frpc.toml"
sudo tee "$FRP_DIR/docker-compose.yml" > /dev/null <<'EOF'
services:
frpc:
image: snowdreamtech/frpc:0.70.0
container_name: frpc
network_mode: host
restart: unless-stopped
volumes:
- ./frpc.toml:/etc/frp/frpc.toml:ro
EOF
echo "==> 2/6 拉起 frpc 容器"
cd "$FRP_DIR"
if sudo docker compose version >/dev/null 2>&1; then
sudo docker compose up -d
else
sudo docker-compose up -d
fi
echo "==> 3/6 mihomohosts 覆盖 + fake-ip-filter + use-hosts(幂等)"
cp "$CFG" "$CFG.bak.frp"
echo " 备份: $CFG.bak.frp"
python3 - "$CFG" <<'PY'
import re, sys
p = sys.argv[1]
text = open(p).read()
orig = text
HOSTS = {
"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
}
lines = text.splitlines(True)
# ---- ① fake-ip-filter 追加域名(防 fake-ip 吞掉 hosts 真实值)----
missing = [d for d in HOSTS if ("'" + d + "'") not in text and ('"' + d + '"') not in text and ("- " + d) not in text]
out = []
inserted_filter = False
for i, ln in enumerate(lines):
out.append(ln)
if (not inserted_filter) and re.match(r"\s*fake-ip-filter:\s*$", ln):
# 探测下一行列表项缩进
indent = " "
for nxt in lines[i+1:]:
m = re.match(r"(\s*)- ", nxt)
if m:
indent = m.group(1)
break
for d in missing:
out.append(indent + "- '" + d + "'\n")
inserted_filter = True
if missing and not inserted_filter:
print(" !! 没找到 fake-ip-filter: 段,请人工检查", file=sys.stderr)
sys.exit(1)
lines = out
# ---- ② hosts 段(家内答案:域名 → 内网 IP)----
text = "".join(lines)
have_hosts = re.search(r"^hosts:\s*$", text, re.M)
need = {d: ip for d, ip in HOSTS.items() if not re.search(r"^\s+" + re.escape(d) + ":", text, re.M)}
if need:
if have_hosts:
out = []
done = False
for ln in text.splitlines(True):
out.append(ln)
if (not done) and re.match(r"^hosts:\s*$", ln):
for d, ip in need.items():
out.append(" " + d + ": " + ip + "\n")
done = True
text = "".join(out)
else:
block = "\nhosts:\n"
for d, ip in need.items():
block += " " + d + ": " + ip + "\n"
text += block
# ---- ③ dns.use-hosts: truemihomo 默认 false,不开 hosts 对 DNS 响应不生效)----
if re.search(r"^\s*use-hosts:", text, re.M):
text = re.sub(r"(^\s*use-hosts:\s*)false", r"\1true", text, flags=re.M)
else:
out = []
done = False
for ln in text.splitlines(True):
out.append(ln)
if (not done) and re.match(r"^dns:\s*$", ln):
out.append(" use-hosts: true\n")
done = True
if not done:
print(" !! 没找到 dns: 段,请人工检查", file=sys.stderr)
sys.exit(1)
text = "".join(out)
if text != orig:
open(p, "w").write(text)
print(" 已写入(hosts %d 条新增 / fake-ip-filter %d 条新增)" % (len(need), len(missing)))
else:
print(" 无需改动(已是目标状态)")
PY
echo "==> 4/6 热重载 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 "==> 5/6 验证家内解析(期望 192.168.3.200"
sleep 1
nslookup git.yanmeiai.com 127.0.0.1 2>&1 | tail -3
echo "==> 6/6 验证 frpc 隧道"
sleep 2
sudo docker logs frpc --tail 20 2>&1 || true
rm -f "$TOKEN_FILE"
echo ""
echo "完成。检查上方日志:应有 'login to server success',且 git-ssh/git-web/dsm/nas-ssh/win-rdp/win-ssh 六个 proxy 'start proxy success'。"
echo "若 login 失败:token 不对或 pangolin1:7000 不通;若个别 proxy 失败:对应本地端口没起。"
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# 在 Mac 上执行的一键部署入口:
# rbw 取 token → 送到 NAS → 上传并执行 setup-frp-on-nas.shsudo 会提示输 DSM 密码)
# 用法:
# bash scripts/setup-frp-remote.sh # 默认走 Tailscale IP(人在外)
# bash scripts/setup-frp-remote.sh wangjia@192.168.3.200 # 在家走局域网
# 前提:Mac 已连 Tailscalepangolin 先断开);rbw 已 unlock。
set -euo pipefail
NAS_HOST="${1:-wangjia@100.95.151.13}"
cd "${0%/*}" # scripts/ 目录
echo "==> 从 Bitwarden 取 frp token"
TMPTOKEN=/tmp/frp-token.$$
rbw get "frp home-tunnel" > "$TMPTOKEN"
[ -s "$TMPTOKEN" ] || { echo "!! rbw 取 token 失败(先 rbw unlock"; rm -f "$TMPTOKEN"; exit 1; }
chmod 600 "$TMPTOKEN"
echo "==> 上传 token 与脚本到 $NAS_HOST:/tmp/frp-setup/"
ssh "$NAS_HOST" 'mkdir -p /tmp/frp-setup && chmod 700 /tmp/frp-setup'
scp -q "$TMPTOKEN" "$NAS_HOST:/tmp/frp-setup/token"
scp -q setup-frp-on-nas.sh "$NAS_HOST:/tmp/frp-setup/"
rm -f "$TMPTOKEN"
echo "==> 在 NAS 上执行(sudo 步骤会提示输入 DSM 密码)"
ssh -t "$NAS_HOST" 'bash /tmp/frp-setup/setup-frp-on-nas.sh'