VPS 代理节点部署手册
从零部署一个三协议代理节点(VLESS Reality + Hysteria2 + Trojan)的完整 SOP。
最新实践:2026-06-05 部署 vps(xTom 圣何塞,CN2 GIA)。蓝本:zgo-la / bytevirt-la-cn2。
架构总览
客户端
│
├─ TCP :443 ──→ nginx stream ──→ Xray VLESS Reality (127.0.0.1:10443)
│ 伪装 www.microsoft.com,无需自有证书
│
├─ TCP :8444 ─→ Xray Trojan (TLS, Let's Encrypt 证书)
│
└─ UDP :8443 ─→ Hysteria2 (QUIC, Let's Encrypt 证书)
+ iptables 端口跳跃 65000-65442 → 8443
端口分工:
| 端口 | 协议 | 监听 | 说明 | | 443/tcp | nginx stream | 0.0.0.0 | 转发到 Reality |
| 10443/tcp | VLESS Reality | 127.0.0.1 | Xray,伪装 microsoft |
| 8444/tcp | Trojan | 0.0.0.0 | Xray inbound,TLS |
| 8443/udp | Hysteria2 | 0.0.0.0 | QUIC |
| 65000-65442/udp | 端口跳跃 | — | iptables REDIRECT → 8443 |
| 80/tcp | nginx http | 0.0.0.0 | certbot HTTP-01 验证 + 跳转 |
| 10085/tcp | Xray stats API | 127.0.0.1 | 流量统计 |
协议选择理由:
- VLESS Reality — 抗检测最强,借用大厂域名 SNI(microsoft),无需自有证书
- Hysteria2 — QUIC 协议,低延迟(比 TCP 少 1 个 RTT),抗丢包,日常首选;UDP 被封时靠端口跳跃续命
- Trojan — TCP+TLS 保底,HY2 全挂时的回落方案;Surge 唯一能用的自建协议(Surge 不支持 VLESS)
前置条件
- 域名解析:
proxy-<别名>.seesaw.icu → VPS IP,Cloudflare DNS Only(不开橙色云,certbot HTTP-01 需要直连)
- SSH 配置:
~/.ssh/config 加好别名,公钥已上机
- 系统:Ubuntu 22.04 / 24.04
部署步骤
1. 装基础软件包
export DEBIAN_FRONTEND=noninteractive
apt-get update
# iptables-persistent 会弹交互框,先预设答案
echo 'iptables-persistent iptables-persistent/autosave_v4 boolean true' | debconf-set-selections
echo 'iptables-persistent iptables-persistent/autosave_v6 boolean true' | debconf-set-selections
apt-get install -y nginx libnginx-mod-stream ufw fail2ban \
certbot python3-certbot-nginx curl mtr bc iptables-persistent
坑 1:Ubuntu 的 nginx 不自带 stream 模块,必须装 libnginx-mod-stream,否则 nginx -t 报 unknown directive "stream"。
坑 2:iptables-persistent 不预设 debconf 会卡交互框导致整批 apt 失败。
坑 3:若机器之前残留 ufw 的 rc 状态(dpkg -l 显示 rc),需 apt-get install --reinstall ufw。
2. 开启 BBR
cat >> /etc/sysctl.conf << 'EOF'
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF
sysctl -p
sysctl net.ipv4.tcp_congestion_control # 确认输出 bbr
BBR 是跨境黄金组合的一半。不开 BBR,晚高峰高 RTT+丢包下 CUBIC 会让 Hy2 崩溃。
3. 装 Xray + Hysteria2
# Xray 官方脚本
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install
# Hysteria2 官方脚本
bash <(curl -fsSL https://get.hy2.sh/)
# 验证
/usr/local/bin/xray version
/usr/local/bin/hysteria version
4. 签 TLS 证书(HY2 + Trojan 需要)
# 临时 nginx 占 80 端口给 certbot 验证
cat > /etc/nginx/sites-available/default << 'EOF'
server {
listen 80 default_server;
server_name proxy-<别名>.seesaw.icu;
location /.well-known/acme-challenge/ { root /var/www/html; }
location / { return 200 'ok'; }
}
EOF
mkdir -p /var/www/html
nginx -t && systemctl restart nginx && systemctl enable nginx
certbot certonly --webroot -w /var/www/html \
-d proxy-<别名>.seesaw.icu \
--non-interactive --agree-tos -m admin@seesaw.icu
坑 4(关键):Xray 以 nobody、Hysteria 以 hysteria 用户运行,默认读不了 /etc/letsencrypt/live/(权限 700)。必须放权限,否则服务启动报 permission denied:
chmod -R 755 /etc/letsencrypt/live /etc/letsencrypt/archive
5. 生成 Reality 密钥 + UUID
/usr/local/bin/xray x25519 # 得到 PrivateKey / Password(PublicKey)
/usr/local/bin/xray uuid # 得到 UUID
记下:PrivateKey(服务端用)、PublicKey(客户端用)、UUID。
6. 写 Xray 配置
/usr/local/etc/xray/config.json(替换 <别名>):
{
"log": { "loglevel": "warning", "access": "/var/log/xray/access.log", "error": "/var/log/xray/error.log" },
"stats": {},
"api": { "tag": "api", "services": ["StatsService"] },
"policy": { "system": { "statsInboundUplink": true, "statsInboundDownlink": true } },
"inbounds": [
{
"tag": "proxy-in",
"listen": "127.0.0.1", "port": 10443,
"protocol": "vless",
"settings": { "clients": [ { "id": "", "flow": "xtls-rprx-vision" } ], "decryption": "none" },
"streamSettings": {
"network": "tcp", "security": "reality",
"realitySettings": { "dest": "www.microsoft.com:443", "serverNames": ["www.microsoft.com"], "privateKey": "", "shortIds": ["abcd1234"] }
},
"sniffing": { "enabled": true, "destOverride": ["http","tls","quic"] }
},
{ "listen": "127.0.0.1", "port": 10085, "protocol": "dokodemo-door", "settings": { "address": "127.0.0.1" }, "tag": "api" },
{
"tag": "trojan-in",
"listen": "0.0.0.0", "port": 8444, "protocol": "trojan",
"settings": { "clients": [ { "password": "seesaw-trojan-2026" } ] },
"streamSettings": {
"network": "tcp", "security": "tls",
"tlsSettings": { "certificates": [ { "certificateFile": "/etc/letsencrypt/live/proxy-<别名>.seesaw.icu/fullchain.pem", "keyFile": "/etc/letsencrypt/live/proxy-<别名>.seesaw.icu/privkey.pem" } ] }
}
}
],
"outbounds": [ { "protocol": "freedom", "tag": "direct" } ],
"routing": { "rules": [ { "inboundTag": ["api"], "outboundTag": "api", "type": "field" } ] }
}
# 测试
/usr/local/bin/xray -test -config /usr/local/etc/xray/config.json
Reality 监听非 443 端口(10443)会有 warning,无害——因为前面有 nginx 在 443 分流。
7. 写 Hysteria2 配置
/etc/hysteria/config.yaml:
listen: :8443
tls:
cert: /etc/letsencrypt/live/proxy-<别名>.seesaw.icu/fullchain.pem
key: /etc/letsencrypt/live/proxy-<别名>.seesaw.icu/privkey.pem
auth:
type: password
password: seesaw-hy2-2026
masquerade:
type: proxy
proxy:
url: https://www.bing.com
rewriteHost: true
8. 配置 nginx stream(443 转发到 Reality)
在 /etc/nginx/nginx.conf 末尾追加(与 http {} 同级):
stream {
server {
listen 443 reuseport;
proxy_pass 127.0.0.1:10443;
}
}
说明:纯代理节点不托管订阅,无需 SNI 分流,443 直接全转发给 Reality 即可。
若该机还要在 443 上托管订阅(订阅服务器场景),才需要 map $ssl_preread_server_name 按 SNI 分流到 nginx http(10444) 或 xray(10443)。纯节点不需要 10444。
/etc/nginx/sites-available/default 只保留 80(给 certbot 续签):
server {
listen 80 default_server;
server_name proxy-<别名>.seesaw.icu;
location /.well-known/acme-challenge/ { root /var/www/html; }
location / { return 301 https://$host$request_uri; }
}
9. 启动所有服务
nginx -t
systemctl restart nginx && systemctl enable nginx
systemctl restart xray && systemctl enable xray
systemctl restart hysteria-server && systemctl enable hysteria-server
# 确认全 active
for s in nginx xray hysteria-server; do systemctl is-active $s; done
10. HY2 端口跳跃(抗 UDP 封锁)
iptables -t nat -A PREROUTING -p udp --dport 65000:65442 -j REDIRECT --to-ports 8443
iptables-save > /etc/iptables/rules.v4
晚高峰部分宽带运营商封 UDP,端口跳跃让运营商难以精准封杀。客户端 HY2 端口字段写 65000-65442。
11. 安全加固
# UFW
ufw --force reset
ufw default deny incoming
ufw default allow outgoing
for r in 22/tcp 80/tcp 443/tcp 8444/tcp 8443/udp 65000:65442/udp; do ufw allow $r; done
ufw --force enable
# fail2ban
cat > /etc/fail2ban/jail.local << 'EOF'
[sshd]
enabled = true
maxretry = 5
bantime = 3600
findtime = 600
EOF
systemctl restart fail2ban && systemctl enable fail2ban
12. 证书续签自动化(关键)
证书续签后会重置 live/ 目录权限,导致 Xray/HY2 读不了。装 deploy hook 自动修复 + 重启:
mkdir -p /etc/letsencrypt/renewal-hooks/deploy
cat > /etc/letsencrypt/renewal-hooks/deploy/restart-proxy.sh << 'EOF'
#!/bin/bash
chmod -R 755 /etc/letsencrypt/live /etc/letsencrypt/archive
systemctl restart xray hysteria-server nginx
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/restart-proxy.sh
13. SSH 端口加固(可选)
把 SSH 从 22 改到非标端口,挡掉绝大多数自动化扫描/爆破。保留密码登录作兜底(失联时可用初始密码从机房 VNC 救回)。
坑 5(Ubuntu 22.04+ socket 激活):Ubuntu 新版用 ssh.socket 管端口(socket activation),直接改 sshd_config 的 Port 不生效。而且有个 systemd generator(/run/systemd/generator/ssh.socket.d/addresses.conf)会读 sshd_config 的 Port 自动生成 socket 监听——所以改端口要让 generator 按 sshd_config 重新生成,而不是手动改 socket。
安全步骤(全程不切断现有连接,先双端口验证再关旧口):
NEWPORT=<非标端口> # 自己选,别写进公网文档
# 1. UFW 先放行新端口(旧 22 暂留)
ufw allow ${NEWPORT}/tcp
# 2. sshd drop-in 配置(只写新端口,不要保留 Port 22,否则 generator 会把 22 加回来)
cat > /etc/ssh/sshd_config.d/99-custom-port.conf << EOF
Port ${NEWPORT}
PasswordAuthentication yes
PubkeyAuthentication yes
EOF
sshd -t # 测试语法
# 3. 重启 socket(generator 会按新 Port 重新生成监听)
systemctl daemon-reload
systemctl restart ssh.socket
ss -tlnp | grep ssh # 确认监听新端口
# 4. 【关键】另开一个终端用新端口登录测试,确认能进
# ssh -p <新端口> root@ ← 必须成功再继续!
# 5. 验证通过后撤掉 22 的 UFW 放行
ufw delete allow 22/tcp
铁律:关旧端口前,务必保持一个活着的 SSH 连接 + 用新端口实测登录成功。万一改崩,活连接是救命通道。本地 ~/.ssh/config 给该 Host 加 Port <新端口>,之后 ssh <别名> 直连。
14. 哪吒探针(监控)
agent 上报到哪吒面板(本项目面板在 tc-sv,域名 monitor.seesaw.icu)。哪吒 v1 用 client_secret 自动注册:
curl -L https://raw.githubusercontent.com/nezhahq/scripts/main/agent/install.sh -o /tmp/nezha-install.sh
NZ_SERVER='<面板IP>:8008' \
NZ_CLIENT_SECRET='<面板密钥>' \
NZ_UUID="$(cat /proc/sys/kernel/random/uuid)" \
NZ_TLS=false \
NZ_DISABLE_COMMAND_EXECUTE=true \
bash /tmp/nezha-install.sh
NZ_DISABLE_COMMAND_EXECUTE=true 关掉面板对 agent 的远程命令执行,更安全。client_secret 从面板现有 agent 的 /opt/nezha/agent/config.yml 取,或面板后台「设置」里看。
加入订阅
订阅文件托管在 tc-sv(主)+ zgo-la(备),路径 /var/www/sub//。
clash.yaml 加 3 个节点(全协议),surge.conf 只加 HY2 + Trojan(Surge 不支持 VLESS)。
clash.yaml 节点格式
- name: <别名>-VLESS
type: vless
server:
port: 443
uuid:
network: tcp
udp: true
tls: true
flow: xtls-rprx-vision
client-fingerprint: chrome
reality-opts:
public-key:
short-id: abcd1234
servername: www.microsoft.com
- name: <别名>-HY2
type: hysteria2
server:
port: 8443
password: seesaw-hy2-2026
sni: proxy-<别名>.seesaw.icu
skip-cert-verify: false
alpn: [h3]
up: 200 mbps
down: 200 mbps
- name: <别名>-Trojan
type: trojan
server:
port: 8444
password: seesaw-trojan-2026
sni: proxy-<别名>.seesaw.icu
skip-cert-verify: false
udp: true
记得把节点名加进 proxy-groups 的 Proxy 选择组。
surge.conf 节点格式
<别名>-HY2 = hysteria2, , 8443, password=seesaw-hy2-2026, sni=proxy-<别名>.seesaw.icu
<别名>-Trojan = trojan, , 8444, password=seesaw-trojan-2026, sni=proxy-<别名>.seesaw.icu
部署到双订阅服务器
# clash.yaml 改动建议在本地用 python yaml 处理(保留全局配置/sniffer/规则),再 scp
for host in tc-sv zgo-la; do
scp clash_new.yaml $host:/tmp/clash.yaml
scp surge_new.conf $host:/tmp/surge.conf
ssh $host "sudo cp /tmp/clash.yaml /var/www/sub//clash.yaml && \
sudo cp /tmp/surge.conf /var/www/sub//surge.conf && \
sudo chown www-data:www-data /var/www/sub//{clash.yaml,surge.conf}"
done
zgo-la 的 nginx 把订阅服务放在 conf.d/sub.conf,但其 nginx.conf 默认不 include conf.d,需手动在 http{} 块加 include /etc/nginx/conf.d/*.conf;。
验证
# 1. 订阅含新节点
curl -s "https://sub.seesaw.icu//clash.yaml" | grep -c "<别名>"
# 2. Reality 443 TCP 连通
timeout 5 bash -c "echo > /dev/tcp//443" && echo "443 OPEN"
# 3. Trojan 8444 TLS 握手(应 Verify return code: 0)
echo | openssl s_client -connect :8444 -servername proxy-<别名>.seesaw.icu 2>/dev/null | grep "Verify return"
客户端刷新订阅后,挨个测三个节点连通 + 访问 ip.sb 确认出口 IP。
客户端注意事项
- Clash Verge Rev (mihomo):支持全部三协议
- Surge:仅 HY2 + Trojan(无 VLESS)
- TUN 模式 sniffer:
override-destination: true 会把节点 server IP 覆盖成 SNI 域名解析的 IP——若用 CF 优选 IP 做 server,需在 sniffer 加 skip-domain。纯 Reality/HY2/Trojan 节点用真实 IP 不受影响。
- 日常默认挂 HY2,不要挂 VLESS:跨境高 RTT+丢包环境下,HY2(QUIC + Brutal 拥塞控制,无视丢包硬怼带宽)实测带宽是 VLESS(TCP,拥塞控制保守)的 4-5 倍(实测同台节点 265M vs 56M,见 个人服务器资产清单 实测带宽对比)。VLESS-Reality 的价值在抗检测/抗封锁(伪装大厂 SNI),留作 HY2 被封(UDP 被运营商掐)时的兜底,不是日常首选。
常见坑速查
| 现象 | 原因 | 解决 | nginx -t 报 unknown directive "stream" | 缺 stream 模块 | apt install libnginx-mod-stream |
| xray/hysteria 启动 permission denied | 证书目录 700 | chmod -R 755 /etc/letsencrypt/{live,archive} |
| apt 安装卡住/broken | iptables-persistent 交互 | debconf-set-selections 预设 |
| ufw: command not found | rc 残留状态 | apt install --reinstall ufw |
| 续签后节点全挂 | 续签重置权限 | deploy hook 自动 chmod+重启 |
| 晚高峰 HY2 超时 | UDP 被封 / CUBIC 崩 | 端口跳跃 + 开 BBR |
关联