Files
proxy-station/.claude/skills/edge-node-multi-protocol/references/workflow.md
YANG JIANKUAN 5326c572a2 feat: 合并边缘节点运维并完善 Surge 配置安全
迁入通用节点部署工具、离线交付页生成器和共用技能,隔离真实配置与交付产物。统一 Codex 与 Claude Code 开发约定,补充节点迁移与运维文档。

完善 FINAL、策略组引用、输入校验、Surge 字段编码与 AI DNS 例外保护,增加隔离测试和订阅验证工具。

验证:全量类型检查、44 项单测、前端构建、脚本语法、技能入口与凭据隔离检查均通过。
2026-10-03 23:19:59 +08:00

176 lines
12 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.

# aaPanel deployment workflow
This is the migrated aaPanel workflow. Run shell commands from the LOCAL private node workspace: Bash scripts invoke SSH and their Python companions run on the VPS. Standalone nginx, QUIC and upgrade repairs are documented in `standalone-and-quic.md`. Keep live config out of the skill source directory.
Turn a bare Debian/Ubuntu VPS into a fully managed edge node running **three proxy protocols side by side, each with a direct-egress and a Cloudflare-WARP clean-exit inbound — six inbounds in total.** This is the standard target of this skill, not an optional extra: a single protocol is a fragile bet (if it gets fingerprinted or a client stops supporting it, the whole node goes dark), and three protocol families sharing the same domain/cert/firewall costs almost nothing extra to run once the panel infrastructure is up.
**What it builds**
| Layer | Component | Result |
|---|---|---|
| Panel | aaPanel International (7.x) + nginx | `<SUB_PANEL>.<root>` reverse-proxied, secret-path entrance, hardened |
| Proxy | 3x-ui (Xray) | `<SUB_XUI>.<root>` panel; **6 inbounds** behind nginx: VMess/Trojan/Shadowsocks × direct/WARP |
| Clean exit | cloudflare-warp (proxy mode) | every protocol's WARP inbound egresses through Cloudflare WARP |
| TLS | acme.sh DNS-01 (any provider) | wildcard `*.<root>` cert, auto-renew + reload, shared by all 6 inbounds |
| DNS | Cloudflare API (or your provider, by hand) | `<root>` + `*.<root>` A/AAAA (DNS-only) |
| Cover | nginx vhosts | landing page + node cover (6 WS paths) + default-server `444` |
| Watchdog | systemd timer | WARP proxy self-heal every 60s |
| Monitoring | Nezha + Komari agents (optional) | node appears on your panels |
| Access | SSH key + optional shortcut | passwordless login |
> **This is a template, not a turnkey binary.** You supply your own domain, Cloudflare token, server IP, and passwords. No secrets, domains, or routing rules are baked in.
## Why three protocols instead of one
VMess, Trojan, and Shadowsocks(+WS/TLS) use TCP and are implemented by 3x-ui/Xray-core. Client support depends on the protocol/transport combination: Surge does not support Shadowsocks over WebSocket. Proxy Station filters unsupported combinations using `shared/src/protocols.ts`. Running all three from day one means: if a client only supports a subset (e.g. Surge doesn't do VLESS), you're already covered; if one protocol family gets fingerprinted, you have two independent fallbacks without touching the box again. See `protocol-choice.md` for the fuller reasoning (performance/detection tradeoffs, and how to add a fourth protocol like VLESS+REALITY later).
## Inputs to collect first
Fill a working copy of `scripts/config.env.example` → `config.env` (git-ignored by you). Collect these before touching the box:
- `SERVER_IP` / `SERVER_IPV6` — the new VPS public addresses (amd64 or arm64)
- `SSH_USER` / `SSH_KEY` — `root` or a sudo user (scripts escalate via `sudo`), and the private key
- `TZ` — server timezone (default `UTC`)
- `DOMAIN_ROOT` — the sub-zone this node owns, e.g. `nodeA.example.com` (you get `*.nodeA.example.com`)
- `ACME_DNS_PLUGIN` + creds — DNS provider for the wildcard cert. Default `dns_cf` (Cloudflare). Any acme.sh-supported provider works (DNSPod/Aliyun/Route53/GoDaddy/…) — set the plugin + its env vars (`dns-records.md`).
- `CF_API_TOKEN` / `CF_ZONE_ID` — Cloudflare only: token with **Zone:DNS:Edit** (enables the turnkey A/AAAA record script). Non-CF users create the A/AAAA records themselves.
- `SUB_PANEL` / `SUB_XUI` / `SUB_COVER` — the subdomain labels for the three planes. **If your zone is public, choose non-obvious labels** — don't reuse a guessable convention or people can enumerate your control planes from DNS.
- `AAPANEL_USER` / `AAPANEL_ENTRANCE` — desired panel username + secret entrance path (e.g. `/somesecret`)
- `XUI_USER` / `XUI_PATH` — 3x-ui username + webBasePath (e.g. `/somesecret/`)
- **Six WS cover paths + two ports each** (`PORT_<PROTO>_{DIRECT,WARP}` / `WS_PATH_<PROTO>_{DIRECT,WARP}` for `VMESS`/`TROJAN`/`SS`) — pick your own, non-obvious, don't reuse a naming convention across them
- `XUI_UUID` + `NODE_PASSWORD` (+ `XUI_SUBID`, `SS_METHOD` default `chacha20-poly1305`) — the credentials for the **single `node-main` client** that owns all six inbounds: `XUI_UUID` authenticates the two VMess inbounds, `NODE_PASSWORD` authenticates the two Trojan **and** two Shadowsocks inbounds (they share it — it's one client). Generate strong values at runtime; **store them in your password manager**, never in git.
- monitoring (optional) — Nezha `SERVER:PORT` + shared key; Komari endpoint + auto-discovery key
## Workflow
For a fresh aaPanel node, follow the selected steps in order from the local private workspace. Shell scripts read `scripts/config.env` and invoke SSH; their Python companions run on the VPS. For an existing node, read its handover notes and run only the steps required by the task.
### 0. Verify access & prep
```bash
scripts/00-preflight.sh # confirms SSH, records specs, sets timezone, installs base pkgs (curl/gnupg/ufw)
```
Also drop **your** public key into the box's `~/.ssh/authorized_keys` for passwordless login.
### 1. Wildcard DNS records
```bash
python3 scripts/02-cf-dns.py # Cloudflare: upserts <root> + *.<root> A/AAAA -> SERVER_IP/IPV6 (proxied=false)
```
Non-Cloudflare users: create the same records by hand, see `dns-records.md`.
### 2. Wildcard TLS cert (acme.sh DNS-01)
```bash
scripts/03-sign-wildcard-cert.sh # on-box acme.sh --dns dns_cf, issues *.<root> (ECDSA P-256, Let's Encrypt)
```
One cert, shared by all 6 inbounds via the configured `<SUB_COVER>.<root>` cover domain. Fleet cert signing: `centralized-certs.md`.
### 3. aaPanel International + nginx
```bash
scripts/04-install-aapanel.sh # official install_7.0_en.sh (headless); waits for the log's success banner
scripts/05-harden-aapanel.sh # sets username/password/entrance, binds panel domain, CDN_PROXY=True
scripts/04b-install-nginx.sh # install_soft.sh 0 install nginx 1.24 (aaPanel-managed)
```
`pitfalls.md` has three aaPanel-7.x-specific fixes baked into these scripts already (install-wait logic, `class_v2` import path, anti-scan UA strictness) — read it if you're editing these rather than just running them.
### 4. Cloudflare WARP (clean exit)
```bash
scripts/06-install-warp.sh # cloudflare-warp, proxy mode SOCKS5 127.0.0.1:40000, verify warp=on
scripts/06b-warp-watchdog.sh # installs warp-healthcheck.sh + systemd timer (self-heal every 60s)
```
### 5. 3x-ui (Xray) + six inbounds under one client
```bash
scripts/07-install-3xui.sh # installs 3x-ui, sets panel 127.0.0.1:14533 + XUI_PATH + creds
scripts/08-xui-bootstrap.sh # adds ALL SIX inbounds under ONE node-main client, in one pass
```
`08-xui-bootstrap.py` creates a **single `node-main` client** (carrying both the VMess UUID and the shared
Trojan/Shadowsocks password) linked to all six inbounds — so the panel's Clients page shows one row whose
Info dialog lists all six share links + QR codes and one subscription. It bakes in four version-compat
fixes that are easy to miss and each cost real debugging on a live v3.5.0 box: `user_id=1` (else inbounds
are invisible in the panel), an `externalProxy` link hint (else generated links point at the useless
internal `127.0.0.1:<port>`), a top-level SS `password` (else the panel's copy-links endpoint panics and
returns empty → "No response data"), and read-modify-write of the WARP routing rule (else re-runs wipe it).
It writes directly into 3x-ui's sqlite DB rather than using the panel API (which 403s scripted requests on
this build) — see `3x-ui-schema.md` and `pitfalls.md` for
the table layout if you need to add a 4th protocol or debug a client not authenticating.
### 6. aaPanel sites (panel-native, UI-manageable)
```bash
scripts/09-aapanel-sites.sh # AddSite + SetSSL + CreateProxy (x6 for cover) + HttpToHttps, via panel classes
scripts/09b-default-444.sh # unmatched server_name -> return 444; drop landing page
```
### 7. Firewall + monitoring (optional)
```bash
scripts/10-ufw.sh # allow 22/80/443 + panel port + aaPanel 888/39000-40000
scripts/11-monitoring.sh # optional: install Nezha + Komari agents (placeholders in config.env)
```
No extra firewall ports needed for the 6 inbounds — they're all `127.0.0.1`-only, reverse-proxied through nginx on 443.
### 8. Smoke test — all six inbounds
```bash
scripts/12-smoke-test.sh
```
Checks: WS upgrade returns 101 on all 6 cover paths; landing/3x-ui/aaPanel panels return 200 (aaPanel test
uses a full realistic UA string, see pitfalls); an unmatched hostname gets dropped (444); and — via a
temporary on-box xray client covering all 6 outbounds — every `*-direct` egress IP equals `SERVER_IP` with
`warp=off`, and every `*-warp` egress IP is a Cloudflare address with `warp=on`.
### 9. Generate client links + QR codes
```bash
python3 scripts/14-generate-links-and-qr.py # run LOCALLY, not on the box
```
Writes `vmess://`, `trojan://` and Xray-style `ss://` share links + QR codes via local `qrencode`, plus sing-box JSON snippets for Shadowsocks. Proxy Station parses these SS links; other clients may require transport-specific import. Nothing is sent to a third-party service. See
`templates/02-node-info-template.md` for how to hand these to a client.
### 10. Documentation
Fill in the three templates in `templates/` (handover doc, node info, ops guide) with this node's real
values — see "Documentation templates" below.
## Domains this creates
With `DOMAIN_ROOT=nodeA.example.com` and example labels `SUB_COVER=edge`, `SUB_XUI=gateway`, `SUB_PANEL=console` (pick your own):
| Domain | Role | Backend |
|---|---|---|
| `nodeA.example.com` | landing page | static |
| `<SUB_COVER>.nodeA.example.com` | node cover: 6 WS paths, one per inbound | 127.0.0.1:57707-57712 |
| `<SUB_XUI>.nodeA.example.com` | 3x-ui panel | 127.0.0.1:14533 |
| `<SUB_PANEL>.nodeA.example.com` | aaPanel | 127.0.0.1:\<panel port\> |
| `*.nodeA.example.com` (default) | unmatched → `return 444` | — |
> The labels above are just examples. **Never publish the real labels/paths you pick** — on a public zone they are the map to your control planes and tunnels.
## Adding a protocol to an already-provisioned node
You don't need to redo steps 0-4. Add the new protocol's env vars to `config.env`, extend the `PROTOCOLS`
list in `scripts/08-xui-bootstrap.py` and the `PROXIES` loop in `scripts/09-aapanel-sites.py` (both scripts
are idempotent — re-running them skips ports/sites that already exist), re-run `08-xui-bootstrap.sh` then
`09-aapanel-sites.sh`, then re-run the smoke test with the new outbound added. Full walkthrough with the
concrete steps used to add Trojan+Shadowsocks to a VMess-only node: `3x-ui-schema.md`.
## Documentation templates
`templates/01-handover-template.md`, `templates/02-node-info-template.md`, `templates/03-ops-guide-template.md`
are meant to be copied out and filled in with a real node's values after deployment (they're written with
`{{PLACEHOLDER}}` tokens, not a real node's domain/keys). See `../templates/README.md` for the full list of
tokens each one needs.
## References
- `pitfalls.md` — **read first.** aaPanel 7.x version-compatibility fixes (install-wait logic, `class_v2`, anti-scan UA strictness), 3x-ui multi-protocol schema gotchas, WARP/nginx/monitoring/SSH pitfalls. Skipping it costs hours.
- `3x-ui-schema.md` — the sqlite table layout 3x-ui uses, and how to add a 4th protocol.
- `protocol-choice.md` — why VMess+Trojan+Shadowsocks, performance/detection tradeoffs vs VLESS+REALITY/AnyTLS/Hysteria2/TUIC, and when you'd want a 4th protocol.
- `dns-records.md` — the exact records to create, and how to use a non-Cloudflare DNS provider.
- `centralized-certs.md` — sign wildcards on one host and push to a fleet.
## Project entry points
Use the project root `AGENTS.md` and the shared `edge-node-multi-protocol` skill. The former standalone agent guides are migration snapshots, not active instructions.
## Privacy / safety notes
- Never commit `config.env`, cert keys, panel/xui passwords, or any UUID/password/token. Add them to your password manager.
- The scripts contain **no** real domains, IPs, subscription rules, or routing policy — only placeholders. Keep it that way when you adapt them.
- WARP mode here is for **your own** clean egress; it is not a bypass for anyone else's controls.
- The link/QR-code generator (step 9) never sends anything to a third-party service — QR codes are generated fully offline with local `qrencode`.