commit cd0639d55c903fd431a978ccbb2104014879ad80 Author: dekun Date: Tue Jun 16 08:21:49 2026 +0800 Initial commit: sing-box Reality + Hysteria2 deploy for 66.hyf2.cc Ubuntu deployment at /opt/jiedian with pre-filled env for 47.76.87.111. Co-authored-by: Cursor diff --git a/.env b/.env new file mode 100644 index 0000000..2d51f9c --- /dev/null +++ b/.env @@ -0,0 +1,13 @@ +# VPS 环境配置(66.hyf2.cc @ 47.76.87.111) +# 部署路径:/opt/jiedian + +VPS_IP=47.76.87.111 +DOMAIN=66.hyf2.cc +ACME_EMAIL=admin@hyf2.cc +REALITY_SERVER_NAME=www.microsoft.com + +UUID=42f5b04d-292d-4f13-b892-b70553a714d5 +REALITY_PRIVATE_KEY=IPKtaw1aVb4fS0TPcimu8zwaVGml-JJ5H1rj-_TFQHM +REALITY_PUBLIC_KEY=51H_ikqYdDRgCpjq3pvMYNbqrX8S3zuow1UEjqTN-nI +REALITY_SHORT_ID=e126b4ef9d36adfc +HY2_PASSWORD=npDFaGfRzAPLS3Hh7iM6TEOk diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..023044e --- /dev/null +++ b/.env.example @@ -0,0 +1,21 @@ +# 复制为 .env 后填写,部署脚本会读取这些变量 +# cp .env.example .env + +# VPS 公网 IP +VPS_IP=47.76.87.111 + +# 域名(Hysteria2 证书用) +DOMAIN=66.hyf2.cc + +# Let's Encrypt 申请证书邮箱 +ACME_EMAIL=admin@hyf2.cc + +# Reality 伪装目标(真实大站,不要用你自己的域名) +REALITY_SERVER_NAME=www.microsoft.com + +# 以下由 scripts/generate-keys.sh 自动生成,也可手动填写 +# UUID= +# REALITY_PRIVATE_KEY= +# REALITY_PUBLIC_KEY= +# REALITY_SHORT_ID= +# HY2_PASSWORD= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..34e8d52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# 部署生成的客户端配置 +client/generated/ +# 临时文件 +*.log +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..e68cb3a --- /dev/null +++ b/README.md @@ -0,0 +1,108 @@ +# jiedian — VPS 自建节点 + +个人/家庭自用的 **VLESS + Reality(主力)** + **Hysteria2(备用)** 双栈方案,基于 [sing-box](https://github.com/SagerNet/sing-box)。 + +**仓库**:https://git.bz121.com/dekun/jiedian.git +**部署路径**:`/opt/jiedian`(Ubuntu) + +| 项目 | 值 | +|------|-----| +| VPS IP | `47.76.87.111` | +| 域名 | `66.hyf2.cc` | + +> 完整部署步骤见 **[docs/DEPLOY.md](docs/DEPLOY.md)** + +--- + +## 快速部署(Ubuntu VPS) + +```bash +ssh root@47.76.87.111 + +apt update && apt install -y git +git clone https://git.bz121.com/dekun/jiedian.git /opt/jiedian +cd /opt/jiedian +bash scripts/install.sh +``` + +安装完成后: + +```bash +cat /opt/jiedian/client/generated/share-links.txt +``` + +客户端导入见 [docs/client-import.md](docs/client-import.md)。 + +--- + +## 架构 + +``` +客户端 (Win/iOS/Android) + │ + ├─ TCP 443 ──► sing-box VLESS+Reality ──► 直连出站 + │ + └─ UDP 8443 ─► sing-box Hysteria2 ─────► 直连出站 + +Nginx 127.0.0.1:8080 ← 伪装静态页(fallback) +``` + +详细选型见 [docs/STACK.md](docs/STACK.md)。 + +--- + +## 目录结构 + +``` +/opt/jiedian/ +├── .env # 环境变量(IP/域名/密钥,已预填) +├── scripts/ +│ ├── generate-keys.sh # 生成 UUID / Reality 密钥 / Hy2 密码 +│ ├── install.sh # 一键部署 +│ └── render-client.sh # 本地渲染客户端配置 +├── server/ +│ ├── sing-box.json.template +│ └── nginx/ # fallback 伪装站 +├── client/ +│ └── sing-box-client.json.template +└── docs/ + ├── DEPLOY.md # Ubuntu 部署指南(主文档) + ├── client-import.md # 客户端导入 + └── troubleshooting.md # 故障排查 +``` + +--- + +## 端口与防火墙 + +| 端口 | 协议 | 用途 | +|------|------|------| +| 22 | TCP | SSH | +| 443 | TCP | VLESS + Reality | +| 8443 | UDP | Hysteria2 | + +--- + +## 常用运维 + +```bash +systemctl status sing-box +journalctl -u sing-box -f +sing-box check -c /etc/sing-box/config.json && systemctl restart sing-box +/root/.acme.sh/acme.sh --renew -d 66.hyf2.cc --force +``` + +--- + +## 防墙要点 + +1. 不要公开分享节点链接 +2. Reality SNI 使用 `www.microsoft.com`,不要用 `66.hyf2.cc` +3. 客户端开启 uTLS / chrome 指纹 +4. 被封后:换 serverName → 换 IP → 换 VPS 地区 + +--- + +## 免责声明 + +本项目仅供学习网络技术使用。请遵守当地法律法规。 diff --git a/client/sing-box-client.json.template b/client/sing-box-client.json.template new file mode 100644 index 0000000..a1bd7d7 --- /dev/null +++ b/client/sing-box-client.json.template @@ -0,0 +1,106 @@ +{ + "log": { + "level": "info" + }, + "dns": { + "servers": [ + { + "tag": "remote", + "address": "tls://8.8.8.8", + "detour": "proxy" + }, + { + "tag": "local", + "address": "223.5.5.5", + "detour": "direct" + } + ], + "rules": [ + { + "outbound": "any", + "server": "local" + } + ], + "final": "remote" + }, + "inbounds": [ + { + "type": "mixed", + "tag": "mixed-in", + "listen": "127.0.0.1", + "listen_port": 7890 + }, + { + "type": "tun", + "tag": "tun-in", + "interface_name": "sing-tun", + "address": ["172.19.0.1/30"], + "mtu": 9000, + "auto_route": true, + "strict_route": true, + "stack": "mixed" + } + ], + "outbounds": [ + { + "type": "selector", + "tag": "proxy", + "outbounds": ["reality", "hysteria2", "direct"] + }, + { + "type": "vless", + "tag": "reality", + "server": "${VPS_IP}", + "server_port": 443, + "uuid": "${UUID}", + "flow": "xtls-rprx-vision", + "tls": { + "enabled": true, + "server_name": "${REALITY_SERVER_NAME}", + "utls": { + "enabled": true, + "fingerprint": "chrome" + }, + "reality": { + "enabled": true, + "public_key": "${REALITY_PUBLIC_KEY}", + "short_id": "${REALITY_SHORT_ID}" + } + } + }, + { + "type": "hysteria2", + "tag": "hysteria2", + "server": "${DOMAIN}", + "server_port": 8443, + "password": "${HY2_PASSWORD}", + "tls": { + "enabled": true, + "server_name": "${DOMAIN}", + "alpn": ["h3"] + } + }, + { + "type": "direct", + "tag": "direct" + }, + { + "type": "dns", + "tag": "dns-out" + } + ], + "route": { + "rules": [ + { + "protocol": "dns", + "outbound": "dns-out" + }, + { + "ip_is_private": true, + "outbound": "direct" + } + ], + "final": "proxy", + "auto_detect_interface": true + } +} diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md new file mode 100644 index 0000000..41cb357 --- /dev/null +++ b/docs/DEPLOY.md @@ -0,0 +1,165 @@ +# Ubuntu 部署指南 + +仓库:[https://git.bz121.com/dekun/jiedian.git](https://git.bz121.com/dekun/jiedian.git) + +| 项目 | 值 | +|------|-----| +| VPS IP | `47.76.87.111` | +| 域名 | `66.hyf2.cc` | +| 部署目录 | `/opt/jiedian` | +| 系统 | Ubuntu 22.04 / 24.04 | + +--- + +## 部署前准备 + +### 1. DNS 解析 + +在域名控制台添加 **A 记录**: + +``` +66.hyf2.cc → 47.76.87.111 +``` + +验证(本地或 VPS 上执行): + +```bash +dig +short A 66.hyf2.cc +# 应返回 47.76.87.111 +``` + +### 2. SSH 登录 VPS + +```bash +ssh root@47.76.87.111 +``` + +--- + +## 一键部署(推荐) + +在 VPS 上以 **root** 执行: + +```bash +# 安装 git +apt update && apt install -y git + +# 克隆到 /opt/jiedian +git clone https://git.bz121.com/dekun/jiedian.git /opt/jiedian +cd /opt/jiedian + +# .env 已预填 IP/域名/密钥,直接安装 +bash scripts/install.sh +``` + +安装完成后查看节点链接: + +```bash +cat /opt/jiedian/client/generated/share-links.txt +``` + +--- + +## 分步部署(如需手动控制) + +```bash +apt update && apt install -y git +git clone https://git.bz121.com/dekun/jiedian.git /opt/jiedian +cd /opt/jiedian + +# 检查 .env(已预配置,一般无需修改) +cat .env + +# 若需重新生成密钥 +bash scripts/generate-keys.sh + +# 执行安装 +bash scripts/install.sh +``` + +--- + +## 安装脚本做了什么 + +1. 安装 sing-box、nginx、ufw +2. 防火墙放行:`22/tcp`、`443/tcp`、`8443/udp` +3. Nginx 伪装站监听 `127.0.0.1:8080` +4. acme.sh 为 `66.hyf2.cc` 申请 Let's Encrypt 证书 +5. 生成 `/etc/sing-box/config.json` 并启动 systemd 服务 +6. 输出客户端分享链接到 `client/generated/share-links.txt` + +--- + +## 节点信息 + +| 节点 | 协议 | 地址 | 端口 | +|------|------|------|------| +| 主力 | VLESS + Reality | `47.76.87.111` | 443/TCP | +| 备用 | Hysteria2 | `66.hyf2.cc` | 8443/UDP | + +Reality 伪装 SNI:`www.microsoft.com`(不是你的域名) + +--- + +## 部署后验证 + +```bash +# sing-box 运行状态 +systemctl status sing-box + +# 端口监听 +ss -tlnp | grep 443 +ss -ulnp | grep 8443 + +# 配置语法检查 +sing-box check -c /etc/sing-box/config.json + +# 查看日志 +journalctl -u sing-box -f +``` + +客户端导入见 [client-import.md](client-import.md)。 + +--- + +## 常用运维 + +```bash +cd /opt/jiedian + +# 拉取最新配置(若仓库有更新) +git pull + +# 重新安装/更新 +bash scripts/install.sh + +# 证书手动续期 +/root/.acme.sh/acme.sh --renew -d 66.hyf2.cc --force +systemctl restart sing-box +``` + +--- + +## 故障排查 + +| 问题 | 处理 | +|------|------| +| `dig` 未返回正确 IP | 等待 DNS 生效或检查解析记录 | +| acme 证书失败 | 确认 80 端口可访问:`curl -I http://66.hyf2.cc` | +| sing-box 启动失败 | `journalctl -u sing-box -n 50` 查看报错 | +| 客户端连不上 | 核对 `share-links.txt` 与 `.env` 中密钥一致 | + +更多见 [troubleshooting.md](troubleshooting.md)。 + +--- + +## 更新仓库(本地开发机) + +```bash +cd 节点 +git add . +git commit -m "update config" +git push origin main +``` + +VPS 上 `git pull` 后重新运行 `bash scripts/install.sh` 即可同步。 diff --git a/docs/STACK.md b/docs/STACK.md new file mode 100644 index 0000000..d47585b --- /dev/null +++ b/docs/STACK.md @@ -0,0 +1,20 @@ +# 方案决策记录 + +## 已确认选型 + +| 项目 | 选择 | 理由 | +|------|------|------| +| 协议栈 | **VLESS + Reality + Hysteria2 双栈** | Reality 抗主动探测;Hysteria2 作 UDP 备用与流媒体 | +| 服务端 | **sing-box** | 单进程同时跑 Reality 与 Hysteria2,配置统一 | +| 系统 | **Ubuntu 22.04/24.04 或 Debian 12** | 脚本基于 apt,其他发行版需手动适配 | +| 面板 | **无** | 个人 1–5 人,手改 `.env` + 模板即可 | + +## 端口规划 + +- `443/TCP` — VLESS + Reality(主力) +- `8443/UDP` — Hysteria2(备用) +- `127.0.0.1:8080` — Nginx 伪装静态页(Reality fallback 场景) + +## 单协议简化 + +若只想维护一种协议,删除 `server/sing-box.json.template` 中的 `hysteria2-in` inbound,并跳过证书申请步骤即可,仅保留 Reality。 diff --git a/docs/client-import.md b/docs/client-import.md new file mode 100644 index 0000000..9dd29d4 --- /dev/null +++ b/docs/client-import.md @@ -0,0 +1,152 @@ +# 客户端导入与测试指南 + +部署完成后,在 VPS 上查看 `client/generated/share-links.txt`,或本地运行 `bash scripts/render-client.sh` 生成链接。 + +--- + +## 一、连通性测试(推荐顺序) + +### 1. Reality(TCP 443) + +在**国内网络**下测试(不要在 VPS 本机 curl 自己): + +```bash +# Windows PowerShell — 仅测端口是否可达 +Test-NetConnection -ComputerName YOUR_VPS_IP -Port 443 +``` + +客户端连上后访问 https://www.google.com 或 https://ip.sb 确认出口 IP 为 VPS。 + +### 2. Hysteria2(UDP 8443) + +UDP 无法用普通 TCP 工具测。直接导入客户端测试;若 Reality 可用但 Hysteria2 不通,可能是运营商 QoS/封锁 UDP,可继续只用 Reality。 + +### 3. 故障判断 + +| 现象 | 可能原因 | +|------|----------| +| ping 通 IP,客户端 timeout | 协议层问题,检查 UUID/密钥/SNI | +| ping 不通 | IP 可能被封,考虑换 IP | +| 连接成功但无网 | 检查 VPS 防火墙、sing-box 日志 `journalctl -u sing-box -f` | +| Hysteria2 不通,Reality 正常 | UDP 被干扰,备用节点可忽略 | + +--- + +## 二、Windows + +### 方案 A:v2rayN(推荐,上手快) + +1. 下载 [v2rayN](https://github.com/2dust/v2rayN/releases)(选 `v2rayN-With-Core.zip`) +2. 解压运行,托盘图标 → **服务器** → **从剪贴板导入批量 URL** +3. 复制 `share-links.txt` 中的 `vless://...` 行到剪贴板,导入 +4. 再导入 `hy2://...` 行作为备用 +5. 右键节点 → **设为活动服务器**,路由选 **绕过大陆** +6. 系统代理 → **自动配置系统代理** + +**手动核对 Reality 参数:** + +| 字段 | 值 | +|------|-----| +| 地址 | VPS IP | +| 端口 | 443 | +| 用户 ID | `.env` 中的 UUID | +| 流控 | xtls-rprx-vision | +| 传输 | tcp | +| 安全 | reality | +| SNI | www.microsoft.com(或你设的 REALITY_SERVER_NAME) | +| Fingerprint | chrome | +| Public Key | REALITY_PUBLIC_KEY | +| Short ID | REALITY_SHORT_ID | + +### 方案 B:sing-box 客户端 + +1. 下载 [sing-box for Windows](https://github.com/SagerNet/sing-box/releases) +2. 将 `client/generated/sing-box-client.json` 放入配置目录 +3. 以管理员运行(TUN 模式需要),启动后选择 `reality` 出站 + +### 方案 C:Nekoray + +1. 下载 [Nekoray](https://github.com/MatsuriDayo/nekoray/releases) +2. **Program → Add profile from clipboard**,粘贴 `vless://` 链接 +3. 右键 → **Start** + +--- + +## 三、Android + +### v2rayNG(推荐) + +1. [Google Play](https://play.google.com/store/apps/details?id=com.v2ray.ang) 或 [GitHub Releases](https://github.com/2dust/v2rayNG/releases) 安装 +2. 右上角 **+** → **从剪贴板导入**(先复制 `vless://` 链接) +3. 再导入 `hy2://` 备用节点 +4. 点击右下角 **V** 连接 +5. 设置 → **路由设置** → **绕过局域网及大陆地址** + +### sing-box for Android + +1. [GitHub Releases](https://github.com/SagerNet/sing-box/releases) 安装 APK +2. **Profiles** → **+** → 导入 `sing-box-client.json` +3. 开启 **Service** + +--- + +## 四、iOS + +需要美区 Apple ID 或已有购买记录。 + +### Shadowrocket(小火箭) + +1. App Store 购买安装 Shadowrocket +2. 首页右上角 **+** → **类型选 NONE** → 右上角 **扫描或从剪贴板导入** +3. 粘贴 `vless://` 链接,自动填充 Reality 参数 +4. 再添加 `hy2://` 备用 +5. 连接后 **连通性测试** 应显示延迟 + +**手动添加 Reality:** + +- 类型:VLESS +- 地址:VPS IP +- 端口:443 +- UUID:你的 UUID +- 传输方式:none +- TLS:开启 → 类型 **REALITY** +- SNI:REALITY_SERVER_NAME +- Public Key / Short ID:从 `.env` 复制 +- uTLS:chrome +- Flow:xtls-rprx-vision + +### Streisand + +1. App Store 安装 Streisand +2. **+** → **Import from Clipboard**,粘贴链接 +3. 连接 + +--- + +## 五、macOS + +与 Windows 类似: + +- **V2rayU** / **Nekoray** / **sing-box for Apple platforms** +- 从剪贴板导入 `vless://` 和 `hy2://` 链接 + +--- + +## 六、日常使用建议 + +1. **默认节点**:Reality(`vless://`) +2. **备用节点**:Hysteria2(看视频卡顿时可切换) +3. **不要分享**节点链接;每设备保存一份即可 +4. **每月检查**:`systemctl status sing-box`、证书是否续期(acme.sh 自动) + +--- + +## 七、分享链接格式参考 + +``` +vless://UUID@IP:443?encryption=none&flow=xtls-rprx-vision&security=reality&sni=SNI&fp=chrome&pbk=PUBLIC_KEY&sid=SHORT_ID&type=tcp#名称 + +hy2://PASSWORD@DOMAIN:8443?sni=DOMAIN#名称 +``` + +完整链接见部署后生成的 `client/generated/share-links.txt`。 diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..cbce5b6 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,75 @@ +# 运维与故障排查 + +## 服务检查 + +```bash +# sing-box 是否运行 +systemctl is-active sing-box + +# 配置语法 +sing-box check -c /etc/sing-box/config.json + +# 端口监听 +ss -tlnp | grep 443 # Reality TCP +ss -ulnp | grep 8443 # Hysteria2 UDP + +# Nginx fallback +curl -s http://127.0.0.1:8080 +``` + +## 常见问题 + +### acme.sh 证书申请失败 + +```bash +# 确认 DNS 已生效 +dig +short A your.domain.com + +# 确认 80 端口未被占用(nginx 需先启动) +ss -tlnp | grep :80 + +# 手动重试 +/root/.acme.sh/acme.sh --issue -d your.domain.com --nginx --force +``` + +### sing-box 无法启动 + +```bash +journalctl -u sing-box -n 50 --no-pager +``` + +常见原因:证书路径错误、JSON 语法错误、443 被占用。 + +### 客户端能连但速度慢 + +- 换 Hysteria2 节点(UDP/QUIC 抗丢包) +- 检查 VPS 带宽:`wget -O /dev/null http://speedtest.tele2.net/100MB.zip` +- 避免高峰时段长时间 4K 流媒体 + +### IP 被封 + +1. 向 VPS 商申请更换 IP +2. 修改 `.env` 中 `REALITY_SERVER_NAME` 为其他大站(如 `www.apple.com`) +3. 重新运行 `install.sh` 或手动更新 `/etc/sing-box/config.json` 并 restart + +### 改用 Xray 替代 sing-box(可选) + +若更熟悉 Xray,可使用 `server/xray-server.json.template`: + +```bash +# 安装 Xray +bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install + +# 渲染配置 +sed -e "s|\${UUID}|...|g" ... server/xray-server.json.template > /usr/local/etc/xray/config.json + +systemctl restart xray +``` + +Hysteria2 仍需单独部署(或使用 sing-box 仅跑 Hy2 inbound)。 + +## 安全建议 + +- SSH 改用密钥登录,禁用密码:`PermitRootLogin prohibit-password` +- 可选修改 SSH 端口,ufw 放行新端口后再删 22 +- 不要将 `.env` 或 `share-links.txt` 上传到公开仓库 diff --git a/scripts/generate-keys.sh b/scripts/generate-keys.sh new file mode 100644 index 0000000..6e218e8 --- /dev/null +++ b/scripts/generate-keys.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# 生成 Reality 与 Hysteria2 所需密钥,输出到 stdout 并写入 .env +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(dirname "$SCRIPT_DIR")" +ENV_FILE="${ROOT_DIR}/.env" + +# 依赖 sing-box 生成 reality 密钥对 +if ! command -v sing-box &>/dev/null; then + echo "sing-box 未安装,使用临时下载..." >&2 + TMP="$(mktemp -d)" + ARCH="$(uname -m)" + case "$ARCH" in + x86_64) SB_ARCH="amd64" ;; + aarch64) SB_ARCH="arm64" ;; + *) echo "不支持的架构: $ARCH" >&2; exit 1 ;; + esac + curl -fsSL "https://github.com/SagerNet/sing-box/releases/latest/download/sing-box-1.11.0-linux-${SB_ARCH}.tar.gz" \ + | tar -xz -C "$TMP" --strip-components=1 + SB="$TMP/sing-box" +else + SB="sing-box" +fi + +UUID="$("$SB" generate uuid)" +KEYPAIR="$("$SB" generate reality-keypair)" +PRIVATE_KEY="$(echo "$KEYPAIR" | grep 'PrivateKey:' | awk '{print $2}')" +PUBLIC_KEY="$(echo "$KEYPAIR" | grep 'PublicKey:' | awk '{print $2}')" +SHORT_ID="$("$SB" generate rand --hex 8)" +HY2_PASSWORD="$("$SB" generate rand --base64 32 | tr -d '/+=' | head -c 24)" + +echo "========== 生成的密钥 ==========" +echo "UUID: $UUID" +echo "REALITY_PRIVATE_KEY: $PRIVATE_KEY" +echo "REALITY_PUBLIC_KEY: $PUBLIC_KEY" +echo "REALITY_SHORT_ID: $SHORT_ID" +echo "HY2_PASSWORD: $HY2_PASSWORD" +echo "================================" + +if [[ -f "$ENV_FILE" ]]; then + # 更新或追加 .env 中的密钥字段 + for var in UUID REALITY_PRIVATE_KEY REALITY_PUBLIC_KEY REALITY_SHORT_ID HY2_PASSWORD; do + val="${!var}" + if grep -q "^${var}=" "$ENV_FILE" 2>/dev/null; then + sed -i "s|^${var}=.*|${var}=${val}|" "$ENV_FILE" + else + echo "${var}=${val}" >> "$ENV_FILE" + fi + done + echo "已写入 $ENV_FILE" +else + echo "提示: 先复制 .env.example 为 .env 并填写 VPS_IP、DOMAIN 等,再重新运行本脚本" >&2 +fi diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100644 index 0000000..96076c7 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,171 @@ +#!/usr/bin/env bash +# VPS 一键部署:sing-box (Reality + Hysteria2) + Nginx fallback +# 适用:Ubuntu 22.04/24.04、Debian 12 +# 用法:sudo bash scripts/install.sh +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(dirname "$SCRIPT_DIR")" +ENV_FILE="${ROOT_DIR}/.env" + +RED='\033[0;31m' +GREEN='\033[0;32m' +NC='\033[0m' + +log() { echo -e "${GREEN}[+]${NC} $*"; } +err() { echo -e "${RED}[!]${NC} $*" >&2; exit 1; } + +[[ $EUID -eq 0 ]] || err "请使用 root 运行: sudo bash scripts/install.sh" +[[ -f "$ENV_FILE" ]] || err "缺少 .env 文件,请先: cp .env.example .env 并填写" + +# shellcheck disable=SC1090 +source "$ENV_FILE" + +: "${VPS_IP:?请在 .env 中设置 VPS_IP}" +: "${DOMAIN:?请在 .env 中设置 DOMAIN}" +: "${ACME_EMAIL:?请在 .env 中设置 ACME_EMAIL}" +: "${REALITY_SERVER_NAME:=www.microsoft.com}" + +if [[ -z "${UUID:-}" || -z "${REALITY_PRIVATE_KEY:-}" ]]; then + log "未检测到密钥,运行 generate-keys.sh ..." + bash "$SCRIPT_DIR/generate-keys.sh" + source "$ENV_FILE" +fi + +: "${UUID:?}" +: "${REALITY_PRIVATE_KEY:?}" +: "${REALITY_SHORT_ID:?}" +: "${HY2_PASSWORD:?}" + +ARCH="$(uname -m)" +case "$ARCH" in + x86_64) SB_ARCH="amd64" ;; + aarch64) SB_ARCH="arm64" ;; + *) err "不支持的架构: $ARCH" ;; +esac + +SB_VERSION="1.11.0" +SB_URL="https://github.com/SagerNet/sing-box/releases/download/v${SB_VERSION}/sing-box-${SB_VERSION}-linux-${SB_ARCH}.tar.gz" + +log "更新系统包 ..." +export DEBIAN_FRONTEND=noninteractive +apt-get update -qq +apt-get install -y -qq curl wget nginx ufw ca-certificates + +log "安装 sing-box ${SB_VERSION} ..." +TMP="$(mktemp -d)" +curl -fsSL "$SB_URL" | tar -xz -C "$TMP" --strip-components=1 +install -m 755 "$TMP/sing-box" /usr/local/bin/sing-box +rm -rf "$TMP" + +log "配置防火墙 ..." +ufw --force reset +ufw default deny incoming +ufw default allow outgoing +ufw allow 22/tcp comment 'SSH' +ufw allow 443/tcp comment 'Reality' +ufw allow 8443/udp comment 'Hysteria2' +ufw --force enable + +log "部署 Nginx fallback 站点 ..." +mkdir -p /var/www/fallback +cp "$ROOT_DIR/server/nginx/index.html" /var/www/fallback/ +cp "$ROOT_DIR/server/nginx/fallback.conf" /etc/nginx/sites-available/fallback +ln -sf /etc/nginx/sites-available/fallback /etc/nginx/sites-enabled/fallback +rm -f /etc/nginx/sites-enabled/default +nginx -t && systemctl enable nginx && systemctl restart nginx + +log "申请 TLS 证书 (Let's Encrypt) ..." +mkdir -p /etc/sing-box/certs +if [[ ! -f /root/.acme.sh/acme.sh ]]; then + curl -fsSL https://get.acme.sh | sh -s email="$ACME_EMAIL" +fi +# shellcheck disable=SC1091 +source /root/.acme.sh/acme.sh.env || true + +# 确保域名已解析到本机 +CURRENT_IP="$(curl -4 -fsSL ifconfig.me 2>/dev/null || curl -4 -fsSL ip.sb)" +if [[ "$CURRENT_IP" != "$VPS_IP" ]]; then + err "域名 $DOMAIN 需先解析到 VPS IP ($VPS_IP),当前 VPS 出口 IP 为 $CURRENT_IP" +fi + +/root/.acme.sh/acme.sh --set-default-ca --server letsencrypt +/root/.acme.sh/acme.sh --issue -d "$DOMAIN" --nginx --force +/root/.acme.sh/acme.sh --install-cert -d "$DOMAIN" \ + --key-file /etc/sing-box/certs/privkey.pem \ + --fullchain-file /etc/sing-box/certs/fullchain.pem \ + --reloadcmd "systemctl restart sing-box" + +log "生成 sing-box 服务端配置 ..." +mkdir -p /etc/sing-box +sed -e "s|\${UUID}|${UUID}|g" \ + -e "s|\${REALITY_SERVER_NAME}|${REALITY_SERVER_NAME}|g" \ + -e "s|\${REALITY_PRIVATE_KEY}|${REALITY_PRIVATE_KEY}|g" \ + -e "s|\${REALITY_SHORT_ID}|${REALITY_SHORT_ID}|g" \ + -e "s|\${HY2_PASSWORD}|${HY2_PASSWORD}|g" \ + -e "s|\${DOMAIN}|${DOMAIN}|g" \ + "$ROOT_DIR/server/sing-box.json.template" > /etc/sing-box/config.json + +sing-box check -c /etc/sing-box/config.json + +log "创建 systemd 服务 ..." +cat > /etc/systemd/system/sing-box.service <<'UNIT' +[Unit] +Description=sing-box service +After=network-online.target nginx.service +Wants=network-online.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/sing-box run -c /etc/sing-box/config.json +Restart=on-failure +RestartSec=5 +LimitNOFILE=1048576 + +[Install] +WantedBy=multi-user.target +UNIT + +systemctl daemon-reload +systemctl enable sing-box +systemctl restart sing-box + +log "生成客户端配置 ..." +CLIENT_DIR="${ROOT_DIR}/client/generated" +mkdir -p "$CLIENT_DIR" +: "${REALITY_PUBLIC_KEY:?请在 .env 中设置 REALITY_PUBLIC_KEY(运行 generate-keys.sh 可自动生成)}" + +sed -e "s|\${VPS_IP}|${VPS_IP}|g" \ + -e "s|\${DOMAIN}|${DOMAIN}|g" \ + -e "s|\${UUID}|${UUID}|g" \ + -e "s|\${REALITY_SERVER_NAME}|${REALITY_SERVER_NAME}|g" \ + -e "s|\${REALITY_PUBLIC_KEY}|${REALITY_PUBLIC_KEY}|g" \ + -e "s|\${REALITY_SHORT_ID}|${REALITY_SHORT_ID}|g" \ + -e "s|\${HY2_PASSWORD}|${HY2_PASSWORD}|g" \ + "$ROOT_DIR/client/sing-box-client.json.template" > "$CLIENT_DIR/sing-box-client.json" + +# 生成分享链接 +cat > "$CLIENT_DIR/share-links.txt" < "$OUT_DIR/sing-box-client.json" + +cat > "$OUT_DIR/share-links.txt" < + + + + + Welcome + + + +

Welcome

+

This site is under construction.

+ + diff --git a/server/sing-box.json.template b/server/sing-box.json.template new file mode 100644 index 0000000..e1d9a85 --- /dev/null +++ b/server/sing-box.json.template @@ -0,0 +1,69 @@ +{ + "log": { + "level": "warn", + "timestamp": true + }, + "inbounds": [ + { + "type": "vless", + "tag": "vless-reality-in", + "listen": "::", + "listen_port": 443, + "users": [ + { + "uuid": "${UUID}", + "flow": "xtls-rprx-vision" + } + ], + "tls": { + "enabled": true, + "server_name": "${REALITY_SERVER_NAME}", + "reality": { + "enabled": true, + "handshake": { + "server": "${REALITY_SERVER_NAME}", + "server_port": 443 + }, + "private_key": "${REALITY_PRIVATE_KEY}", + "short_id": ["${REALITY_SHORT_ID}"] + } + } + }, + { + "type": "hysteria2", + "tag": "hysteria2-in", + "listen": "::", + "listen_port": 8443, + "users": [ + { + "password": "${HY2_PASSWORD}" + } + ], + "tls": { + "enabled": true, + "server_name": "${DOMAIN}", + "certificate_path": "/etc/sing-box/certs/fullchain.pem", + "key_path": "/etc/sing-box/certs/privkey.pem" + } + } + ], + "outbounds": [ + { + "type": "direct", + "tag": "direct" + }, + { + "type": "block", + "tag": "block" + } + ], + "route": { + "rules": [ + { + "ip_is_private": true, + "outbound": "block" + } + ], + "final": "direct" + } +} diff --git a/server/xray-server.json.template b/server/xray-server.json.template new file mode 100644 index 0000000..597c027 --- /dev/null +++ b/server/xray-server.json.template @@ -0,0 +1,56 @@ +{ + "log": { + "loglevel": "warning" + }, + "inbounds": [ + { + "listen": "0.0.0.0", + "port": 443, + "protocol": "vless", + "settings": { + "clients": [ + { + "id": "${UUID}", + "flow": "xtls-rprx-vision" + } + ], + "decryption": "none" + }, + "streamSettings": { + "network": "tcp", + "security": "reality", + "realitySettings": { + "show": false, + "dest": "${REALITY_SERVER_NAME}:443", + "xver": 0, + "serverNames": ["${REALITY_SERVER_NAME}"], + "privateKey": "${REALITY_PRIVATE_KEY}", + "shortIds": ["${REALITY_SHORT_ID}"] + } + }, + "sniffing": { + "enabled": true, + "destOverride": ["http", "tls", "quic"] + } + } + ], + "outbounds": [ + { + "protocol": "freedom", + "tag": "direct" + }, + { + "protocol": "blackhole", + "tag": "block" + } + ], + "routing": { + "rules": [ + { + "type": "field", + "ip": ["geoip:private"], + "outboundTag": "block" + } + ] + } +}