#!/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 < /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 容器" # DSM 的 docker 在 /usr/local/bin(Container Manager 符号链接),不在 sudo 的 secure_path 里,必须绝对路径 DOCKER=/usr/local/bin/docker COMPOSE=/usr/local/bin/docker-compose cd "$FRP_DIR" if sudo "$COMPOSE" version >/dev/null 2>&1; then sudo "$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 /usr/local/bin/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 失败:对应本地端口没起。"