Files
wangjia 5d90610649
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
feat(single-node): 改 SQLite + 非特权 pangolin 用户 + 免 Docker(512MB 可跑)
为小内存机收尾单机栈,已在 racknerd(107.172.55.251)实跑验证通过:
- 数据库:MySQL(docker)→ SQLite 文件(DB_DRIVER=sqlite);Redis 改 apt 原生
  (免 Docker);依赖 redis-server/sqlite3/polkitd/sing-box 由 deploy.sh 自动装
- 运行身份:三 systemd 单元 User=root → User=pangolin;sing-box 经
  AmbientCapabilities 绑 443;新增 polkit 规则让 pangolin 仅可重启 sing-box
- 二进制:支持预置(机上无 go 时用交叉编译产物;SQLite 纯 Go 免 CGO)
- 端口:REALITY 默认 443/tcp(对齐 bootstrap 防火墙);deploy.sh 放行 8080/tcp
- 修 sing-box.service:ExecStart 按实际安装路径渲染(官方 .deb 在 /usr/bin)
- Hy2 默认关闭:控制面尚未下发 Hy2 TLS 证书(handler_grpc.go 仅发 ListenPort)
  → 开启会 missing certificate;待补全证书后传 HY2_PORT=443 开启
- 删 docker-compose.yml;seed TRUE→1;README 同步

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 07:46:54 +08:00

134 lines
6.7 KiB
Markdown
Raw Permalink 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.
# 单节点全套部署(真实连接,SQLite + 非特权用户)
一台全新 Debian 12 VPS 上跑 **整套** pangolin**SQLite(文件)+ Redisapt** + 控制面
HTTP API + gRPC agent 服务)+ 节点 agent + sing-box 数据面,**全部以非特权 `pangolin`
用户运行,免 Docker**。客户端连这台 VPS 的公网 IP,真实出网(出口 IP = VPS IP)。
> 为小内存机(512MB)调优:SQLite 取代 MySQL、apt 原生 Redis 取代 docker 容器。
> 配合 `deploy/bootstrap/`(系统加固/防火墙/监控)使用。
## 架构(一台机器内)
```
客户端 (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 │
│ ▼ (polkit 规则允许 pangolin 仅重启 sing-box) │
│ sing-box VLESS+REALITY :443/tcp → 直连出网 │
│ SQLite /var/lib/pangolin/pangolin.db Redis :6379 (apt, loopback) │
└─────────────────────────────────────────────────────────────────┘
(三服务均 User=pangolin;sing-box 经 AmbientCapabilities 绑 443
```
> ⚠️ **Hy2 默认关闭**:控制面尚未接线 Hy2 的 TLS 证书下发(`handler_grpc.go` 只发
> ListenPort,不发 CertPath/KeyPath),开启会导致 sing-box `missing certificate`。
> 待补全证书下发后,传 `HY2_PORT=443` 开启。当前数据面=REALITY(443/tcp)。
凭证天然自洽:connect 给客户端的 REALITY `uuid = dp_uuid`、Hy2 `password =
DeriveHy2Password(dp_uuid, NODE_DERIVE_KEY)`agent 渲染 server 配置时用 **同一**
`NODE_DERIVE_KEY` 和 dp_uuid,两端一致。
## 部署
前置:root、`openssl``redis-server`/`sqlite3`/`polkitd`/`sing-box` 由 deploy.sh 自动装。
二进制:机上有 `go` 则就地构建;**512MB 机器推荐别处交叉编译后预置**(无需机上装 Go):
```bash
# 在开发机交叉编译(SQLite 纯 Go,免 CGO),scp 到 VPS 的 /usr/local/bin/
cd server && for c in server agent nodectl migrate; do
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /tmp/p-$c ./cmd/$c
done
scp /tmp/p-server <vps>:/usr/local/bin/pangolin-server
scp /tmp/p-agent <vps>:/usr/local/bin/pangolin-agent
scp /tmp/p-migrate <vps>:/usr/local/bin/pangolin-migrate
scp /tmp/p-nodectl <vps>:/usr/local/bin/nodectl
# scp 本目录到 VPS 的 <repo>/deploy/single-node/,再一键起(VPS_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。