Files
pangolin/deploy/single-node/README.md
T
wangjia 912a495e91 feat(deploy): 单节点全套部署脚本 deploy/single-node(档 3 真机准备)
一台全新 Ubuntu/Debian VPS 上一条命令起整套 pangolin:MySQL + Redis(docker)
+ 控制面(HTTP + gRPC agent 服务)+ 节点 agent + sing-box 数据面,客户端连该
VPS 公网 IP 真实出网。

- deploy.sh:幂等编排——构建二进制 → docker MySQL/Redis → 生成 JWT/webhook/
  derive 密钥 + Node CA + CA 签发的 gRPC server 证书(SAN localhost,匹配 agent
  enroll 后的 mTLS 校验)+ REALITY 密钥对(持久化跨重部署)→ migrate + seed 节点
  → 起控制面(走完整 gRPC 分支)→ nodectl bootstrap-token → 起 agent + sing-box。
- systemd/{pangolin-server,pangolin-agent,sing-box}.service、docker-compose.yml
  (MySQL/Redis 仅 loopback)、seed-node.sql.tmpl。
- README.md:架构图 + 部署/验证步骤 + 客户端接线(API URL / PANGOLIN_SINGBOX_BIN
  / sudoers / 真节点 uuid,客户端代码无改动)+ 排查指引。

本机验证:shellcheck + bash -n 通过;openssl CA ↔ mtls.NewCA + gRPC 证书链互通
已单测实测通过。VPS 端到端(出口 IP == VPS IP)待真机到位后执行。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 00:32:47 +08:00

119 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 单节点全套部署(档 3 真实连接)
一台全新 Ubuntu/Debian VPS 上跑 **整套** pangolinMySQL + Redis + 控制面(HTTP
API + gRPC agent 服务)+ 节点 agent + sing-box 数据面。客户端连这台 VPS 的公网
IP,真实出网(出口 IP = VPS IP)。
> 不碰 EC2 生产、不用本机出网。这套是独立的端到端验证环境。
## 架构(一台机器内)
```
客户端 (mac/手机)
│ ① POST /v1/auth/code → 注册/登录 (JWT)
│ ② GET /v1/nodes → 看到本节点 (status=up)
│ ③ POST /v1/nodes/{uuid}/connect → 控制面下发 sing-box CLIENT 配置 (REALITY)
┌───────────────────────────── VPS ──────────────────────────────┐
│ pangolin-server HTTP :8080 + gRPC :9443 │
│ │ connect → Hub.Push(Upsert dp_uuid) │
│ ▼ (gRPC Subscribe 命令流, mTLS) │
│ pangolin-agent ── 渲染 sing-box SERVER 配置 → systemctl restart │
│ ▼ │
│ sing-box VLESS+REALITY :11443 (+ Hysteria2 :443) → 直连出网 │
│ MySQL :3306 (loopback) Redis :6379 (loopback) [docker] │
└─────────────────────────────────────────────────────────────────┘
```
凭证天然自洽:connect 给客户端的 REALITY `uuid = dp_uuid`、Hy2 `password =
DeriveHy2Password(dp_uuid, NODE_DERIVE_KEY)`agent 渲染 server 配置时用 **同一**
`NODE_DERIVE_KEY` 和 dp_uuid,两端一致。
## 部署(VPS 上一条命令)
前置:root、`docker`(含 compose v2)、`openssl``go``sing-box`(脚本会尝试自动装)。
```bash
# 1. 把仓库 checkout 到 VPSgit clone 或 scp
# 2. 一键起全套(VPS_IP 不传则自动探测公网 IP)
sudo VPS_IP=<你的公网IP> bash deploy/single-node/deploy.sh
```
幂等:重跑只刷新二进制 + 配置,**不**轮换 REALITY 密钥 / 数据库(已发的客户端配置不失效)。
### 真发邮件(可选)
不传 SMTP_* 时,注册验证码打到 `journalctl -u pangolin-server`。要真发:
```bash
sudo VPS_IP=<IP> \
SMTP_HOST=smtp.resend.com SMTP_PORT=587 SMTP_USERNAME=resend \
SMTP_PASSWORD=<resend-api-key> SMTP_FROM='穿山甲 <noreply@mail.51yanmei.com>' \
bash deploy/single-node/deploy.sh
```
## 验证(VPS 上)
```bash
journalctl -u pangolin-agent -n 50 --no-pager # enrolled successfully + Register
systemctl status sing-box --no-pager # active (running)
grep -A3 reality-in /etc/sing-box/config.json # listen_port 11443 + 你的 short_id
ss -tlnp | grep 11443 # sing-box 在监听
```
agent 第一次 enroll 后会渲染 `/etc/sing-box/config.json``systemctl restart
sing-box`。在 connect 之前 server 配置里 reality-in 的 users 可能为空(没有已连用户),
客户端 connect 后 Hub 推 Upsertagent 会把 `dp_uuid` 加进 users 并重启 sing-box。
## 客户端接线(本机,无需改代码)
客户端代码已就绪(connect 透传、桌面 sing-box 子进程 + sudo TUN)。只需指向本 VPS
1. **API 地址**
```bash
flutter run -d macos --dart-define=PANGOLIN_API_URL=http://<VPS_IP>:8080
```
2. **本机 sing-box 二进制**:装 `brew install sing-box`,并让 app 能找到它
(环境变量 `PANGOLIN_SINGBOX_BIN=/opt/homebrew/bin/sing-box``--dart-define`
传不进进程 env,用真实环境变量或放到 kernel_process 查找路径)。
3. **macOS TUN 提权**:给 sing-box 免密 sudoPoC 用,正式走 SMJobBless
```
# /etc/sudoers.d/pangolin-singbox
<你的用户名> ALL=(root) NOPASSWD: /opt/homebrew/bin/sing-box
```
4. **真节点 uuid**seed 的节点 uuid 非空,`connection_provider` 会自动走真实分支
`ConnectApi.fetchConfig` → `bridge.start`),无需改代码。
## 验证出口(端到端)
客户端连上后,在本机:
```bash
curl https://api.ipify.org # 出口 IP 应等于 <VPS_IP>
```
出口 IP == VPS IP 即真实远端代理打通。
## 文件 / 路径
| 路径 | 内容 |
|------|------|
| `/etc/pangolin/server.env` | 控制面环境变量(DB/JWT/CA/gRPC/derive key |
| `/etc/pangolin/{ca,grpc}.{key,crt}` | Node CA + gRPC server 证书(CA 签发,SAN localhost |
| `/etc/pangolin/reality.env` | REALITY 密钥对 + short_id(跨重部署稳定) |
| `/etc/pangolin/db.env` | MySQL root 密码 |
| `/etc/pangolin-agent/agent.env` | agent 环境变量(含一次性 bootstrap token |
| `/etc/pangolin-agent/{node.key,node.crt,ca.crt}` | agent enroll 后持久化的 mTLS 身份 |
| `/etc/sing-box/config.json` | agent 渲染的 sing-box SERVER 配置 |
| `/usr/local/bin/{pangolin-server,pangolin-agent,nodectl,pangolin-migrate}` | 二进制 |
## 排查
- **agent 起不来 / enroll 失败**`journalctl -u pangolin-agent`。检查
`pangolin-server` 是否已起 gRPC(日志应有 gRPC,不是 "gRPC not configured")——
需要 server.env 里 `GRPC_ADDR` + `CA_*` + `GRPC_*` 全配(deploy.sh 已配好)。
- **sing-box 反复重启**:首次 enroll 前 `/etc/sing-box/config.json` 不存在,属正常;
enroll 后应自愈。仍失败看 `journalctl -u sing-box`,多半是 REALITY 配置/端口占用。
- **客户端 connect 返回 4xx**:确认 JWT 有效(先登录)、节点 `status='up'`。
- **出口 IP 不是 VPS**:检查客户端是否真的起了 TUNsudo 提权)、route final=auto。