feat(frp): 远程访问一键部署脚本(frpc+mihomo分流DNS) + 配置模板;文档标执行中
This commit is contained in:
@@ -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.88(Windows)
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./frpc.toml:/etc/frp/frpc.toml:ro
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# 复制为 frpc.toml 并把 token 换成真值(Bitwarden「frp home-tunnel」)。
|
||||||
|
# frpc.toml 含密钥,不入 git(部署由 scripts/setup-frp-remote.sh 自动完成)。
|
||||||
|
serverAddr = "103.119.13.48" # pangolin1(frps)
|
||||||
|
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
@@ -37,8 +37,8 @@
|
|||||||
<tr><th>文档</th><th>状态</th><th>说明</th></tr>
|
<tr><th>文档</th><th>状态</th><th>说明</th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="remote-access-frp.html">家庭内网远程访问 —— frp 反向隧道 × pangolin × 分流 DNS</a></td>
|
<td><a href="remote-access-frp.html">家庭内网远程访问 —— frp 反向隧道 × pangolin × 分流 DNS</a></td>
|
||||||
<td><span class="tag warn">待评审</span></td>
|
<td><span class="tag warn">执行中</span></td>
|
||||||
<td>内外同域名访问 NAS/Windows:frpc(NAS)→frps(pangolin1) 反向隧道 + split-horizon DNS + pangolin 客户端分流</td>
|
<td>内外同域名访问 NAS/Windows:frpc(NAS)→frps(pangolin1) 反向隧道 + split-horizon DNS + pangolin 客户端分流;NAS 侧一键脚本 <code>scripts/setup-frp-remote.sh</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<a class="back" href="index.html">← 文档索引</a>
|
<a class="back" href="index.html">← 文档索引</a>
|
||||||
<h1>家庭内网远程访问方案<br>frp 反向隧道 × pangolin × 分流 DNS</h1>
|
<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> P0–P2 已完成(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">
|
<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>
|
<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>
|
||||||
|
|||||||
Executable
+188
@@ -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.yml(sudo)"
|
||||||
|
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 mihomo:hosts 覆盖 + 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: true(mihomo 默认 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 失败:对应本地端口没起。"
|
||||||
Executable
+26
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 在 Mac 上执行的一键部署入口:
|
||||||
|
# rbw 取 token → 送到 NAS → 上传并执行 setup-frp-on-nas.sh(sudo 会提示输 DSM 密码)
|
||||||
|
# 用法:
|
||||||
|
# bash scripts/setup-frp-remote.sh # 默认走 Tailscale IP(人在外)
|
||||||
|
# bash scripts/setup-frp-remote.sh wangjia@192.168.3.200 # 在家走局域网
|
||||||
|
# 前提:Mac 已连 Tailscale(pangolin 先断开);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'
|
||||||
Reference in New Issue
Block a user