feat: add xui-deploy skill with lessons learned
- SKILL.md v1.1: full deployment workflow for 3x-ui on VPS via SSH - Covers Docker/native install, Nginx+TLS, Xray inbound config - references/xray-inbound-config.md: VLESS+WS+TLS and Reality configs - references/lessons-learned.md: lessons from first real deployment - /app/x-ui binary vs shell wrapper in Docker - correct API path: panel/api/inbounds/add - subPath-only DB write (subURI causes blank settings page) - --network host port exposure workaround - Agent prompt and eval configs included
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
# Recommended Xray Inbound Configuration
|
||||
|
||||
This reference covers the best protocol/transport combinations for security and performance,
|
||||
and the API payloads to create them via the x-ui REST API.
|
||||
|
||||
## Protocol Recommendation (ranked)
|
||||
|
||||
| Rank | Protocol | Transport | TLS | Why |
|
||||
|------|----------|-----------|-----|-----|
|
||||
| ✅ Best | VLESS | WebSocket | TLS (via Nginx) | CDN-friendly, low overhead, widely supported |
|
||||
| ✅ Best | VLESS | gRPC | TLS (via Nginx) | Multiplexed, low latency, CDN-friendly |
|
||||
| Good | VLESS | TCP | XTLS/Reality | No CDN needed, excellent performance, anti-detection |
|
||||
| Good | VMess | WebSocket | TLS (via Nginx) | Broad client support |
|
||||
| Avoid | VMess | TCP | none | Detectable, no forward secrecy |
|
||||
| Avoid | Shadowsocks | — | — | Blocked in many regions |
|
||||
|
||||
**Default recommendation**: VLESS + WebSocket + TLS, proxied through Nginx.
|
||||
- Panel port stays on `127.0.0.1` (localhost-only)
|
||||
- Nginx terminates TLS on 443 and forwards to the inbound port on `127.0.0.1`
|
||||
- Inbound port also stays localhost-only (e.g. `127.0.0.1:10000`)
|
||||
|
||||
---
|
||||
|
||||
## Step-by-Step: Create VLESS + WS + TLS Inbound
|
||||
|
||||
### 1. Collect inbound parameters
|
||||
|
||||
Ask the user (or use defaults):
|
||||
|
||||
| Field | Default |
|
||||
|-------|---------|
|
||||
| Inbound port | `10000` |
|
||||
| WS path | `/ws/` (use a random string for security) |
|
||||
| Remark | `vless-ws` |
|
||||
|
||||
### 2. Create inbound via x-ui API
|
||||
|
||||
The x-ui panel exposes a REST API at `http://127.0.0.1:<panel_port><base_path>`.
|
||||
Authenticate with a session cookie first, then POST the inbound.
|
||||
|
||||
```bash
|
||||
# 1. Login and save session cookie
|
||||
PANEL_USER=$(bash /repo/common-skills/tools/kp-get.sh "<entry_title>" UserName)
|
||||
PANEL_PASS=$(bash /repo/common-skills/tools/kp-get.sh "<entry_title>" Password)
|
||||
|
||||
ssh -i <key_path> -p <ssh_port> <user>@<host> "
|
||||
curl -sc /tmp/xui-cookie.txt \
|
||||
-X POST http://127.0.0.1:<panel_port><base_path>login \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{\"username\":\"'\"$PANEL_USER\"'\",\"password\":\"'\"$PANEL_PASS\"'\"}'
|
||||
"
|
||||
|
||||
# 2. Generate a UUID for the client
|
||||
UUID=$(ssh -i <key_path> -p <ssh_port> <user>@<host> "cat /proc/sys/kernel/random/uuid")
|
||||
|
||||
# 3. Create VLESS + WebSocket inbound
|
||||
ssh -i <key_path> -p <ssh_port> <user>@<host> "
|
||||
curl -sb /tmp/xui-cookie.txt \
|
||||
-X POST http://127.0.0.1:<panel_port><base_path>xui/API/inbounds/add \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
\"remark\": \"vless-ws\",
|
||||
\"enable\": true,
|
||||
\"protocol\": \"vless\",
|
||||
\"listen\": \"127.0.0.1\",
|
||||
\"port\": 10000,
|
||||
\"settings\": \"{\\\"clients\\\":[{\\\"id\\\":\\\"'\"$UUID\"'\\\",\\\"flow\\\":\\\"\\\"}],\\\"decryption\\\":\\\"none\\\"}\",
|
||||
\"streamSettings\": \"{\\\"network\\\":\\\"ws\\\",\\\"wsSettings\\\":{\\\"path\\\":\\\"/ws/\\\"}}\",
|
||||
\"sniffing\": \"{\\\"enabled\\\":true,\\\"destOverride\\\":[\\\"http\\\",\\\"tls\\\"]}\"
|
||||
}'
|
||||
"
|
||||
```
|
||||
|
||||
### 3. Add Nginx location for the WS path
|
||||
|
||||
Append to the existing Nginx config (`/etc/nginx/conf.d/x-ui.conf`):
|
||||
|
||||
```nginx
|
||||
location /ws/ {
|
||||
proxy_pass http://127.0.0.1:10000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_read_timeout 86400s;
|
||||
}
|
||||
```
|
||||
|
||||
Reload Nginx: `systemctl reload nginx`
|
||||
|
||||
### 4. Report client config
|
||||
|
||||
Output the connection info for the user to import into their client (v2rayN, Clash, etc.):
|
||||
|
||||
```
|
||||
Protocol : VLESS
|
||||
Address : <domain>
|
||||
Port : 443
|
||||
UUID : <uuid>
|
||||
Transport: WebSocket
|
||||
Path : /ws/
|
||||
TLS : TLS
|
||||
SNI : <domain>
|
||||
```
|
||||
|
||||
Or generate a VLESS share link:
|
||||
```
|
||||
vless://<uuid>@<domain>:443?encryption=none&security=tls&type=ws&path=%2Fws%2F&sni=<domain>#vless-ws
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Alternative: VLESS + Reality (no CDN, no domain needed)
|
||||
|
||||
Reality is the best option when no domain is available. It mimics a real TLS handshake against a target site.
|
||||
|
||||
```bash
|
||||
# Generate Reality key pair inside the container/host
|
||||
ssh -i <key_path> -p <ssh_port> <user>@<host> \
|
||||
"docker exec x-ui xray x25519" # Docker
|
||||
# or: xray x25519 # native
|
||||
```
|
||||
|
||||
Save the `Private key` and `Public key` output.
|
||||
|
||||
Create inbound via API (replace keys and port):
|
||||
|
||||
```json
|
||||
{
|
||||
"remark": "vless-reality",
|
||||
"enable": true,
|
||||
"protocol": "vless",
|
||||
"listen": "",
|
||||
"port": 443,
|
||||
"settings": "{\"clients\":[{\"id\":\"<uuid>\",\"flow\":\"xtls-rprx-vision\"}],\"decryption\":\"none\"}",
|
||||
"streamSettings": "{\"network\":\"tcp\",\"security\":\"reality\",\"realitySettings\":{\"show\":false,\"dest\":\"www.microsoft.com:443\",\"serverNames\":[\"www.microsoft.com\"],\"privateKey\":\"<private_key>\",\"shortIds\":[\"\"]}}"
|
||||
}
|
||||
```
|
||||
|
||||
Client connection info:
|
||||
```
|
||||
Protocol : VLESS
|
||||
Address : <vps_ip>
|
||||
Port : 443
|
||||
UUID : <uuid>
|
||||
Flow : xtls-rprx-vision
|
||||
Transport : TCP
|
||||
Security : Reality
|
||||
PublicKey : <public_key>
|
||||
SNI : www.microsoft.com
|
||||
```
|
||||
|
||||
> Note: Reality listens on `0.0.0.0:443` (must be public). This is intentional — it's the proxy traffic port, not the panel.
|
||||
|
||||
---
|
||||
|
||||
## Security Hardening for Inbounds
|
||||
|
||||
- Always set `"listen": "127.0.0.1"` for WS/gRPC inbounds (Nginx handles public exposure).
|
||||
- Use a random UUID per client; rotate periodically.
|
||||
- Use a non-obvious WS path (e.g. `/a3f9k2/` not `/ws/`).
|
||||
- Enable sniffing (`destOverride: ["http","tls"]`) to block DNS leaks.
|
||||
- For Reality, use a high-traffic legitimate domain as `dest` (e.g. `www.microsoft.com`, `www.apple.com`).
|
||||
Reference in New Issue
Block a user