先说结论
三步走:DD 脚本重装最新 Debian 13,装 AimiliVPN 与 WARP 两条出口隧道,最后上 Hysteria2 三个实例并封装成一份可直接分发的 Clash 订阅。
前言
一台甲骨文东京区小鸡(1G 内存),目标三个出口:本机直连、VPNGate 志愿节点中转、Cloudflare WARP,最后封装成一条 Clash 订阅地址。全文三步:
- DD 重装脚本换最新系统
- 装两条出口隧道(AimiliVPN + WARP)
- 装 Hysteria2,生成订阅
准备
- 一台能 SSH 登录的 VPS,root 权限
- 确认 TUN 可用:
ls /dev/net/tun存在即可 - 心理准备:跨境 SSH 偶发「TCP 通但 banner 读不到」,是链路抖动不是机器挂了,脚本加重试或等一分钟再试
第一步:DD 脚本重装最新系统
用 bin456789/reinstall,走官方安装器,对甲骨文这类机器会自动选 cloud 内核变体、自动配网络:
curl -O https://raw.githubusercontent.com/bin456789/reinstall/main/reinstall.sh |
重启后机器进入无人值守安装,SSH 断开,等 10~30 分钟再连。注意重装会清空整块系统盘,重要配置先备份到本机。
到这里算成功:
cat /etc/os-release # 显示 13 (trixie) |
第二步:装两条出口隧道
AimiliVPN(VPNGate 出口)
AimiliVPN 管理 VPNGate 公共节点,装好后在本地起一个代理,流量从 OpenVPN 隧道出站,出口 IP 就是当前志愿节点:
AIMILIVPN_NONINTERACTIVE=1 bash <(curl -Ls https://raw.githubusercontent.com/baoweise-bot/aimili-vpngate/main/install.sh) |
装完终端会输出三样东西:Web 面板地址(8787 端口 + 随机路径)、面板账号密码、本地代理端口 127.0.0.1:7928。
验证出口已切换:
curl -s -x http://127.0.0.1:7928 https://api.ipify.org # 应返回志愿节点 IP,不是本机 IP |
日常管理用 ml 命令:ml status(状态)、ml logs(日志)、ml password(改面板密码,装完建议改)。它的切换策略是故障切换——当前节点失效自动换下一个,不追逐「更快」的节点,平时不用管。
WARP(Cloudflare 出口)
用 fscarmen/warp-sh,选 c 方案(Socks5 代理模式)——只在本地开代理、不动系统路由,这样才不会和 AimiliVPN 的策略路由打架:
curl -fsSL https://gitlab.com/fscarmen/warp/-/raw/main/menu.sh -o /root/warp.sh |
新版客户端需要手工激活三连(缺一不可,非交互环境必须带 --accept-tos):
systemctl enable --now warp-svc |
验证:
warp-cli --accept-tos status # Connected |
日常开关:warp r(断开/重连)、warp i jp(刷指定地区的出口 IP)、warp u(卸载)。
到这里算成功: 7928 和 40000 两个本地代理分别返回两个不同的出口 IP。
第三步:装 Hysteria2,生成订阅
三个出口都用 Hysteria2 承载,架构:
客户端 ──Hysteria2──> VPS |
3.1 安装与证书
curl -fsSL https://get.hy2.sh/ | bash |
3.2 直连实例
/etc/hysteria/config.yaml(完整模板见附录):
listen: :443 |
官方 systemd 单元以 hysteria 用户运行,配置默认 root 600 会读不了,必须改属主再启动:
chown hysteria:hysteria /etc/hysteria/config.yaml /etc/hysteria/server.key /etc/hysteria/server.crt |
3.3 两个链式实例
中转实例 /etc/hysteria/config-vpngate.yaml(WARP 实例同理,只是出站地址和端口换掉):
listen: :8443 |
两个高频坑都在这:
- Hysteria2 v2.12 起 ACL 必须包在
inline:下,规则改成函数式出站名(all)。网上老教程的acl: [all,vpngate]逗号写法会直接 FATAL; - 每个实例的配置都要
chown hysteria:hysteria,否则服务起不来。
第二个实例的 systemd 单元(hysteria-server-warp.service 同理,记得 Wants=warp-svc.service):
[Service] |
验证三个出口:本地各起一个 hysteria client 指向 443/8443/9443,socks5 出口 curl api.ipify.org,看到三个不同的 IP 就成了。
3.4 生成订阅
三个出口封装成一条订阅。一个几十行的 Python 小服务挂在 8788 端口:随机路径防扫描,按路径返回 Clash YAML 和 base64 通用格式两份文件,systemd 守护(完整代码见附录)。
订阅内容就是一份完整 Clash 配置:proxies 写三个节点(443 直连 udp: true;8443/9443 两个链式节点因为上游 SOCKS5 只支持 TCP,标 udp: false),proxy-groups 放节点选择/自动选择(url-test)/故障转移(fallback) 加几个服务组,rules 用 MetaCubeX 规则集 做国内外分流。
一个值得做的细节:订阅名是从响应头读的,加上两行,用户导入时自动显示你想要的名字:
self.send_header("Content-Disposition", "attachment; filename=<订阅名>") |
最后提醒一个客户端侧的坑:Clash Party / Verge 类客户端的**「覆写」功能会把你订阅里的分组整个重写**(冒出一堆「香港节点/台湾节点」占位组)。导入后分组数量对不上,就去客户端覆写页面停用脚本再刷新订阅。
踩坑汇总
| 现象 | 原因 | 处理 |
|---|---|---|
| SSH 偶发 banner 超时 | 跨境链路抖动 | 加重试,等一分钟再试 |
| hysteria 起不来,日志 permission denied | 官方单元以 hysteria 用户跑,配置是 root 600 | 配置/证书 chown 给 hysteria |
| FATAL: ‘acl’ expected a map or struct | v2.12 ACL 必须包在 inline 下 | acl: inline: + 列表 |
| FATAL: invalid syntax: all,vpngate | 旧逗号语法废弃 | 函数式 - 出站名(all) |
| warp-cli 报 Registration Missing | 新版要先注册设备 | warp-cli --accept-tos registration new |
| 导入订阅冒出几十个分组 | 客户端「覆写」脚本改写订阅 | 停用覆写,刷新订阅 |
| 重装后服务全没了 | 重装本来就是全盘清空 | 重要配置先备份到本机 |
总结
三步走完:一台 1G 小鸡上跑起三个出口加一条可分发的订阅,内存占用不到 300MB。两条经验:链式出站的本质是「本地代理串联」,任何能开本地 socks5 的工具都能变成 Hysteria2 的一个出站;订阅名、分组这类小体验决定了分发成本,两个响应头就能让用户导入即用,值得做。
附录
直连实例完整配置见 3.2,链式实例见 3.3,密码与混淆口令全部换成自己的随机串(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 20)。
订阅服务 server.py 完整代码:
#!/usr/bin/env python3 |
配套 systemd 单元:
[Unit] |
参考
- bin456789/reinstall —— DD 重装脚本
- baoweise-bot/aimili-vpngate —— VPNGate 节点管理
- fscarmen/warp-sh —— WARP 安装脚本
- apernet/hysteria —— Hysteria2(ACL 语法以官方 v2 文档为准)
- MetaCubeX/meta-rules-dat —— Clash 规则数据