feat: 合并边缘节点运维并完善 Surge 配置安全
迁入通用节点部署工具、离线交付页生成器和共用技能,隔离真实配置与交付产物。统一 Codex 与 Claude Code 开发约定,补充节点迁移与运维文档。 完善 FINAL、策略组引用、输入校验、Surge 字段编码与 AI DNS 例外保护,增加隔离测试和订阅验证工具。 验证:全量类型检查、44 项单测、前端构建、脚本语法、技能入口与凭据隔离检查均通过。
This commit is contained in:
175
.claude/skills/edge-node-multi-protocol/references/workflow.md
Normal file
175
.claude/skills/edge-node-multi-protocol/references/workflow.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# 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`.
|
||||
Reference in New Issue
Block a user