HBB BLOG
首页
登录
🌙
搭建节点从这个开始
📅 2026-04-01
📝 6659 字
⏱️ 23 分钟
👁️ 1
本脚本集成了目前对抗 GFW 最强、最稳的三种节点协议,专为追求**高成功率、极致防封、低延迟不卡顿**的用户打造。 **支持协议:** 1. `VLESS + Reality + XHTTP` (最新伪装,防封王者) 2. `VLESS + Reality + Vision` (极致性能,低延迟首选) 3. `NaiveProxy + TLS` (完美伪装 Chrome 浏览器流量) --- ## 🛠️ 准备条件 * **VPS 服务器**:一台国外 VPS(推荐 Ubuntu 20.04+ / Debian 11+ / CentOS 8+),**必须是全新或干净的 IP**,已被墙的 IP 无法直连。 * **权限要求**:必须拥有 `root` 权限。 * **域名(可选/必选)**: * 安装 **VLESS+Reality**:**不需要**域名(使用真实 IP 直连)。 * 安装 **NaiveProxy**:**必须**有一个域名,且已将 DNS A 记录解析到该 VPS 的 IP。 --- ## 📥 使用方法 1. 使用 SSH 工具(如 Xshell, Termius, PuTTY)连接到你的 VPS。 2. 在服务器新建一个文件(例如 `hbb_proxy.sh`),将脚本代码复制进去并保存。 3. 赋予执行权限并运行: ```bash chmod +x hbb_proxy.sh ./hbb_proxy.sh ``` *(或者直接跟随终端菜单的中文提示,按数字键选择操作即可。)* --- ## ⚠️ 注意事项与进阶避坑指南 (必读!) 该脚本已经尽最大可能避免了系统级冲突,并**内置了 BBR 与内核网络高并发优化(防卡顿)**。但在作为生产力工具使用时,请务必关注以下细节: ### 1. 🛡️ 云服务商的外层防火墙 (安全组) 虽然脚本已经内置了 `iptables/firewalld/UFW` 的自动放行及规则持久化,但如果你使用的是**阿里云、腾讯云、AWS、Google Cloud、Oracle** 等云服务商的机器: * **必须**登录云服务商的网页控制台。 * 找到“安全组”或“防火墙”面板,**手动放行**你刚才在脚本中设置的 TCP 端口(例如 `443` 或 `8443`)。否则外部无法连接。 ### 2. 🚫 关于 CDN (Cloudflare) 的使用限制 这两种协议目前都属于**强特征对抗型协议**,仅限**真实 IP 直连**使用: * **VLESS + Reality**:依赖直接的 TLS 握手特征伪装(“偷”目标网站的证书)。如果套上 Cloudflare,客户端握手的是 CF 的证书,Reality 直接失效。**绝对不能开 CDN**。 * **NaiveProxy**:依赖 HTTP/2 的特定头部和填充字节。若开启 Cloudflare 代理(点亮小黄云),CF 会清洗掉这些特征,导致客户端无法建立连接。 * **结论**:均不支持免费版 Cloudflare CDN。如果 IP 被墙,只能考虑更换 IP 或套接中转机。 ### 3. 🔄 NaiveProxy 专属:TLS 证书的热重载陷阱 在使用“选项 2 (NaiveProxy)”时,你需要提供硬盘上的证书路径。 * **原理**:Caddy (Naive 的后端) 通常会自动监控证书文件变化并无感重载。 * **避坑**:如果你使用第三方的 `crontab` 定时脚本(如 `acme.sh`)强制覆盖证书文件,某些情况下 Caddy 可能无法捕获文件 `inode` 的改变,导致内存中仍使用旧证书。 * **解决方案**:建议在证书续期脚本的“续期成功”钩子(Hook)中,加上一行强制重启命令: ```bash systemctl restart naiveproxy ``` *踹它一脚让它重启,确保 90 天后节点不会因为证书过期而断连。* --- ## 🛠️ 安装配置指南 ### 方案 A:安装 VLESS + Reality(推荐,成功率最高,免域名) 1. **选择选项 `1`** 2. **节点别名**:随便填(如 `日本-东京-Reality`),仅用于客户端显示。 3. **端口**:推荐 `443`(如果被占用,可换 `8443` 等高位端口,**记得去云控制台放行**)。 4. **UUID**:直接回车留空,脚本会自动生成安全的 UUID。 5. **传输模式**(核心选择): * `1) XHTTP`:**伪装性最强**,将流量伪装成普通网页请求,GFW 最难识别,适合重度封锁区。 * `2) Vision`:**性能更高**,延迟更低,适合打游戏或追求极致流畅度的用户。 6. **Reality 伪装域名**:直接回车,默认使用 `www.microsoft.com:443`(也可自定义为 `www.apple.com` 等大厂域名)。 7. **ServerNames**:直接回车,默认 `www.microsoft.com`。 8. **ShortIds**:直接回车,自动生成。 ### 方案 B:安装 NaiveProxy + TLS(极致伪装,需域名和证书) 1. **选择选项 `2`** 2. **节点别名**:随便填。 3. **解析到本机的域名**:**必须填**,且确保 DNS 已经正确解析到本 VPS 的 IP。 4. **HTTPS 端口**:推荐 `443`。 5. **用户名/密码**:留空使用默认值,或自定义。 6. **证书路径配置**: * 你需要提前在服务器上申请好 SSL 证书(推荐使用 `acme.sh` 或 `certbot`)。 * **证书公钥路径**:填绝对路径,如 `/root/cert/fullchain.pem` * **证书私钥路径**:填绝对路径,如 `/root/cert/private.key` > **💡 补充:如何快速申请 Let's Encrypt 证书?** > 如果你还没有证书,可以在运行本脚本前,执行以下命令快速申请(需先安装 `socat`): > ```bash > curl https://get.acme.sh | sh -s email=my@example.com > ~/.acme.sh/acme.sh --issue -d 你的域名.com --standalone > # 申请成功后,证书路径通常在 /root/.acme.sh/你的域名.com_ecc/ 目录下 > ``` --- ## 📱 客户端推荐 (搭配使用效果更佳) 搭建完成后,请使用支持 **VLESS-Reality** 和 **NaiveProxy** 的现代客户端导入生成的分享链接: * **Windows**: `v2rayN` (最新版), `Hiddify`, `Clash.Meta (mihomo)` * **Android**: `v2rayNG`, `NekoBox`, `Hiddify` * **iOS / macOS**: `Shadowrocket` (小火箭), `Stash`, `Hiddify` * **路由器 (OpenWrt)**: `PassWall2`, `SSR+`, `OpenClash` (需 Meta 核心) --- ## ❓ 常见问题 (FAQ) **Q: 脚本提示“内核版本过低,不支持 BBR”怎么办?** A: 脚本内置了 BBR 和 `fq` 队列优化以防止高并发卡顿。如果你的 VPS 系统太老(内核低于 4.9),脚本会跳过此步骤。建议更换为 Ubuntu 22.04 或 Debian 12 等现代系统以获得最佳网络性能。 **Q: 如何查看已经安装的节点信息?** A: 运行脚本,选择主菜单的 **`5. 查看所有节点配置`**,脚本会读取本地记录并重新输出所有节点的分享链接。 **Q: 如何彻底卸载?** A: 运行脚本,选择主菜单的 **`3. 卸载全部`**,脚本会停止服务、删除核心文件、清空防火墙规则及节点记录,做到真正的“无痕卸载”。 ```bash #!/bin/bash # Xray (VLESS + Reality/XHTTP/Vision) / NaiveProxy + TLS # Author: HBB | Crafted with soul & code # 优化版: 修复注入漏洞、兼容ECC证书、支持纯IPv6、加入BBR与内核高并发优化 set -euo pipefail RED="\033[31m" GREEN="\033[32m" YELLOW="\033[33m" BLUE="\033[36m" PLAIN='\033[0m' # 白名单命令列表(防止任意命令执行) ALLOWED_COMMANDS=("menu" "uninstall") XRAY_CONFIG="/etc/xray/config.json" XRAY_SERVICE="/etc/systemd/system/xray.service" NAIVE_SERVICE="/etc/systemd/system/naiveproxy.service" V6_PROXY="" SWAP_ADDED=0 PMT="" CMD_INSTALL="" # 系统检查与包管理器设定 checkSystem() { if [[ $EUID -ne 0 ]]; then colorEcho $RED "需要 root 权限" exit 1 fi if [[ -f /etc/debian_version ]]; then PMT="apt" CMD_INSTALL="apt update && apt install -y " elif [[ -f /etc/redhat-release ]]; then PMT="yum" CMD_INSTALL="yum install -y " else colorEcho $RED "不支持的系统,仅支持 Debian/Ubuntu 或 CentOS/RHEL" exit 1 fi } # 安装基础依赖 (加入了 bc 用于内核版本计算) installBaseDeps() { colorEcho $BLUE "正在检查并安装基础环境依赖..." if [[ "$PMT" == "apt" ]]; then $CMD_INSTALL wget curl openssl jq net-tools iproute2 unzip tar git dnsutils bc >/dev/null 2>&1 else $CMD_INSTALL wget curl openssl jq net-tools iproute unzip tar git bind-utils bc >/dev/null 2>&1 fi } colorEcho() { echo -e "${1}${@:2}${PLAIN}" } # 安全解析配置文件(彻底摒弃 eval,使用 printf -v 杜绝命令注入) safe_read_conf() { local file="$1" [[ -f "$file" ]] || return 1 while IFS='=' read -r key value; do value=$(echo "$value" | sed "s/^['\"]//;s/['\"]$//;s/^[[:space:]]*//;s/[[:space:]]*$//") case "$key" in NODE_ALIAS|PORT|UUID|FLOW|REALITY_MODE|XHTTP_PATH|REALITY_PUBLIC_KEY|REALITY_PRIVATE_KEY|REALITY_SERVER_NAMES|REALITY_SHORT_IDS|REALITY_DEST|IP|NODE_TYPE|NODE_INDEX|DOMAIN|NAIVE_USER|NAIVE_PASS|CERT_FILE|KEY_FILE) printf -v "$key" '%s' "$value" ;; esac done < "$file" return 0 } # 获取IP getIP() { for svc in ipinfo.io api.ipify.org ifconfig.me; do local ip=$(curl -sL --connect-timeout 5 -4 https://${svc}/ip 2>/dev/null) if [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then IP="$ip" return 0 fi done # 如果 IPv4 失败,尝试 IPv6 IP=$(curl -sL --connect-timeout 5 -6 https://api64.ipify.org 2>/dev/null || echo "") if [[ -n "$IP" ]]; then V6_PROXY="https://ghp.ci/" return 0 fi return 1 } # URL编码 (使用 jq 完美支持中文及所有 UTF-8 字符) url_encode() { jq -nr --arg v "$1" '$v|@uri' } # CPU架构获取 getArch() { case "$(uname -m)" in x86_64|amd64) echo "64" ;; *armv8*|aarch64) echo "arm64-v8a" ;; *) colorEcho $RED "不支持的 CPU 架构: $(uname -m)"; exit 1 ;; esac } getGoArch() { case "$(uname -m)" in x86_64|amd64) echo "amd64" ;; *armv8*|aarch64) echo "arm64" ;; *) colorEcho $RED "不支持的 CPU 架构用于编译: $(uname -m)"; exit 1 ;; esac } # 高精度端口冲突拦截 checkPort() { local p=$1 local target_svc=$2 local pid_info="" if command -v ss >/dev/null 2>&1; then pid_info=$(ss -tlnp 2>/dev/null | awk -v port=":$p " 'index($0, port) > 0') elif command -v netstat >/dev/null 2>&1; then pid_info=$(netstat -tlnp 2>/dev/null | awk -v port=":$p " 'index($0, port) > 0') fi if [[ -n "$pid_info" ]]; then if [[ "$target_svc" == "vless" ]] && echo "$pid_info" | grep -qi "xray"; then colorEcho $YELLOW " 提示: 端口 $p 正被旧版 Xray 占用,安装后将覆盖接管。" return 0 elif [[ "$target_svc" == "naive" ]] && echo "$pid_info" | grep -qi "caddy"; then colorEcho $YELLOW " 提示: 端口 $p 正被旧版 Naive(Caddy) 占用,安装后将覆盖接管。" return 0 else local proc_info proc_info=$(echo "$pid_info" | head -n 1 | sed -n 's/.*users:\(\([^]]*\)\).*/\1/p' || echo "未知进程") colorEcho $RED " [拦截] 错误: 端口 $p 已被其他进程占用!" colorEcho $YELLOW " 占用详情: $proc_info" return 1 fi fi return 0 } # 开启 BBR 与内核网络高并发优化 (防卡顿核心) enableBBR() { colorEcho $BLUE "正在检查并优化底层网络参数 (BBR/队列)..." local bbr_status=$(sysctl net.ipv4.tcp_congestion_control 2>/dev/null | awk '{print $3}') if [[ "$bbr_status" == "bbr" ]]; then colorEcho $GREEN " BBR 已开启,跳过" return fi local kernel_ver=$(uname -r | grep -oE '^[0-9]+\.[0-9]+') local is_supported=0 if command -v bc &>/dev/null; then is_supported=$(echo "$kernel_ver >= 4.9" | bc -l 2>/dev/null || echo 0) else [[ "${kernel_ver%%.*}" -ge 5 ]] && is_supported=1 [[ "${kernel_ver%%.*}" -eq 4 ]] && [[ "${kernel_ver#*.}" -ge 9 ]] && is_supported=1 fi if [[ "$is_supported" -eq 1 ]]; then # 清除旧的冲突配置 sed -i '/net.core.default_qdisc/d' /etc/sysctl.conf sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.conf cat >> /etc/sysctl.conf <<EOF # Xray/Naive 高并发防卡顿优化 (by HBB Script) net.core.default_qdisc=fq net.ipv4.tcp_congestion_control=bbr net.core.rmem_max=16777216 net.core.wmem_max=16777216 net.ipv4.tcp_rmem=4096 87380 16777216 net.ipv4.tcp_wmem=4096 16384 16777216 EOF sysctl -p >/dev/null 2>&1 colorEcho $GREEN " BBR 与内核网络参数优化成功" else colorEcho $YELLOW " 内核版本 ($kernel_ver) 可能不支持 BBR,跳过优化" fi } # VLESS配置输入 getDataVLESS() { echo "" colorEcho $BLUE "[VLESS + Reality 配置]" read -p " 节点别名[默认: VLESS-Reality]: " input_alias NODE_ALIAS=$(echo "$input_alias" | tr -cd 'a-zA-Z0-9_\-\u4e00-\u9fff .') NODE_ALIAS="${NODE_ALIAS:-VLESS-Reality}" while true; do read -p " 监听端口 [默认: 443]: " input_port PORT="${input_port:-443}" if ! [[ "$PORT" =~ ^[0-9]+$ ]] || [[ "$PORT" -lt 1 ]] || [[ "$PORT" -gt 65535 ]]; then colorEcho $RED " 端口无效,请输入 1-65535 之间的数字" continue fi if checkPort "$PORT" "vless"; then break fi done read -p " UUID [留空自动随机]: " input_uuid if [[ "$input_uuid" =~ ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ ]]; then UUID="$input_uuid" else UUID=$(cat /proc/sys/kernel/random/uuid) fi echo "" colorEcho $BLUE " 传输模式:" echo -e " 1) XHTTP ${BLUE}(最新伪装推荐)${PLAIN}" echo -e " 2) Vision ${BLUE}(高性能推荐)${PLAIN}" read -p " 选择[1-2 默认1]: " mode_choice REALITY_MODE=$([[ "$mode_choice" == "2" ]] && echo "vision" || echo "xhttp") if [[ "$REALITY_MODE" == "vision" ]]; then FLOW="xtls-rprx-vision" XHTTP_PATH="" else FLOW="" read -p " XHTTP路径[默认: /xhttp]: " input_path XHTTP_PATH="${input_path:-/xhttp}" if [[ "${XHTTP_PATH:0:1}" != "/" ]]; then XHTTP_PATH="/${XHTTP_PATH}" fi fi echo "" colorEcho $BLUE " Reality 伪装配置" read -p " 伪装目标域名[默认: www.microsoft.com:443]: " input_dest if [[ -z "$input_dest" ]]; then REALITY_DEST="www.microsoft.com:443" elif [[ "$input_dest" =~ ^[a-zA-Z0-9.-]+:[0-9]+$ ]]; then REALITY_DEST="$input_dest" elif [[ "$input_dest" =~ ^[a-zA-Z0-9.-]+$ ]]; then REALITY_DEST="${input_dest}:443" else colorEcho $YELLOW " 格式无效,使用默认值" REALITY_DEST="www.microsoft.com:443" fi read -p " ServerNames (逗号分隔) [默认: www.microsoft.com]: " input_sn REALITY_SERVER_NAMES=$(echo "$input_sn" | tr -cd 'a-zA-Z0-9.,-') if [[ -z "$REALITY_SERVER_NAMES" ]]; then REALITY_SERVER_NAMES="www.microsoft.com" fi read -p " ShortIds[留空自动随机]: " input_sid if [[ "$input_sid" =~ ^[0-9a-fA-F]+$ ]]; then REALITY_SHORT_IDS="$input_sid" else REALITY_SHORT_IDS=$(openssl rand -hex 8) fi } # NaiveProxy配置输入 getDataNaive() { echo "" colorEcho $BLUE "[NaiveProxy + TLS 配置]" read -p " 节点别名[默认: NaiveProxy]: " input_alias NODE_ALIAS=$(echo "$input_alias" | tr -cd 'a-zA-Z0-9_\-\u4e00-\u9fff .') NODE_ALIAS="${NODE_ALIAS:-NaiveProxy}" read -p " 解析到本机的域名: " input_domain if [[ -z "$input_domain" ]]; then colorEcho $RED " 域名不能为空" exit 1 fi DOMAIN=$(echo "$input_domain" | tr '[:upper:]' '[:lower:]') while true; do read -p " HTTPS端口[默认: 443]: " input_port PORT="${input_port:-443}" if ! [[ "$PORT" =~ ^[0-9]+$ ]] || [[ "$PORT" -lt 1 ]] || [[ "$PORT" -gt 65535 ]]; then colorEcho $RED " 端口无效,请输入 1-65535" continue fi if checkPort "$PORT" "naive"; then break fi done read -p " 用户名[默认: user]: " input_user NAIVE_USER="${input_user:-user}" read -p " 密码 [留空自动随机生成]: " input_pass if [[ -z "$input_pass" ]]; then NAIVE_PASS=$(openssl rand -base64 12 | tr -d '\n' | sed 's/[+/=]/x/g') else NAIVE_PASS="$input_pass" fi # 手动指定证书路径 echo "" colorEcho $BLUE " 请指定 SSL/TLS 证书路径" colorEcho $YELLOW " 提示: SSL 证书需要两个配套文件" colorEcho $YELLOW " ① 证书文件: 包含域名公钥 (.pem/.crt/.cer)" colorEcho $YELLOW " ② 私钥文件: 与证书配对的私钥 (.key)" echo "" while true; do colorEcho $BLUE " [1/2] 请输入证书文件路径:" read -p " 路径: " input_cert CERT_FILE=$(echo "$input_cert" | sed "s/^['\"]//;s/['\"]$//" | awk '{print $1}') if [[ -z "$CERT_FILE" ]]; then colorEcho $RED " [错误] 路径不能为空" continue fi if echo "$CERT_FILE" | grep -Pq '[^\x00-\x7F]'; then colorEcho $YELLOW " [提示] 检测到路径包含中文,建议移动到英文路径目录以防兼容性问题" read -p " 是否继续?[y/n]: " confirm if [[ "${confirm,,}" != "y" ]]; then continue fi fi if [[ -f "$CERT_FILE" ]]; then local perms=$(stat -c %a "$CERT_FILE" 2>/dev/null || stat -f %A "$CERT_FILE" 2>/dev/null) if [[ "$perms" != "600" && "$perms" != "400" ]]; then colorEcho $YELLOW " [警告] 证书文件权限为 $perms,建议设置为 600 或 400" read -p " 是否继续?[y/n 输入r返回主菜单]: " confirm if [[ "${confirm,,}" == "r" ]]; then return 1 elif [[ "${confirm,,}" != "y" ]]; then continue fi fi colorEcho $GREEN " [√] 证书文件已确认: $CERT_FILE" break else colorEcho $RED " [错误] 文件不存在: $CERT_FILE" read -p " 输入 r 返回主菜单,或按回车重新输入: " retry_action if [[ "${retry_action,,}" == "r" ]]; then return 1 fi fi done echo "" while true; do colorEcho $BLUE " [2/2] 请输入对应的私钥文件路径:" read -p " 路径: " input_key KEY_FILE=$(echo "$input_key" | sed "s/^['\"]//;s/['\"]$//" | awk '{print $1}') if [[ -z "$KEY_FILE" ]]; then colorEcho $RED " [错误] 路径不能为空" continue fi if [[ -f "$KEY_FILE" ]]; then local perms=$(stat -c %a "$KEY_FILE" 2>/dev/null || stat -f %A "$KEY_FILE" 2>/dev/null) if [[ "$perms" != "600" && "$perms" != "400" ]]; then colorEcho $RED " [错误] 私钥文件权限为 $perms,必须为 600 或 400" colorEcho $BLUE " 请执行: chmod 600 $KEY_FILE" read -p " 输入 r 返回主菜单,或按回车重新输入: " retry_action if [[ "${retry_action,,}" == "r" ]]; then return 1 fi continue fi colorEcho $GREEN " [√] 私钥文件已确认: $KEY_FILE" break else colorEcho $RED " [错误] 文件不存在: $KEY_FILE" read -p " 输入 r 返回主菜单,或按回车重新输入: " retry_action if [[ "${retry_action,,}" == "r" ]]; then return 1 fi fi done } # 防火墙设置 setFirewall() { colorEcho $BLUE "正在配置防火墙放行端口 ${PORT}..." if command -v ufw &>/dev/null; then local ufw_status ufw_status=$(ufw status 2>/dev/null || echo "") if echo "$ufw_status" | grep -q "Status: active"; then ufw allow ${PORT}/tcp 2>/dev/null || true return fi fi if command -v firewall-cmd &>/dev/null; then if systemctl is-active firewalld &>/dev/null; then firewall-cmd --permanent --add-port=${PORT}/tcp 2>/dev/null || true firewall-cmd --reload 2>/dev/null || true return fi fi if command -v iptables &>/dev/null; then iptables -C INPUT -p tcp --dport ${PORT} -j ACCEPT 2>/dev/null || \ iptables -A INPUT -p tcp --dport ${PORT} -j ACCEPT 2>/dev/null || true if command -v netfilter-persistent &>/dev/null; then netfilter-persistent save &>/dev/null || true elif command -v iptables-save &>/dev/null; then mkdir -p /etc/iptables iptables-save > /etc/iptables/rules.v4 2>/dev/null || true fi fi } # 安装Xray installXray() { local arch=$(getArch) colorEcho $BLUE " 获取 Xray 最新稳定版本..." local latest_ver=$(curl -sL -o /dev/null -w '%{url_effective}' "https://github.com/XTLS/Xray-core/releases/latest" | rev | cut -d'/' -f1 | rev) if [[ -z "$latest_ver" || "$latest_ver" == "releases" ]]; then latest_ver="v24.11.11" colorEcho $YELLOW " 获取最新版本号失败,采用兜底稳定版: ${latest_ver}" fi if [[ -f /usr/bin/xray ]]; then local current_ver current_ver=$(/usr/bin/xray version 2>/dev/null | head -n1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1 || echo "") if [[ -n "$current_ver" ]]; then current_ver="v${current_ver}" if [[ "$current_ver" == "$latest_ver" ]]; then colorEcho $GREEN " Xray ${latest_ver} 已是最新版本,跳过下载" return 0 else colorEcho $YELLOW " 检测到版本 ${current_ver} -> ${latest_ver},将升级" fi fi fi local url="${V6_PROXY}https://github.com/XTLS/Xray-core/releases/download/${latest_ver}/Xray-linux-${arch}.zip" colorEcho $BLUE " 下载 Xray ${latest_ver}..." local tmpdir=$(mktemp -d) if ! curl -L --connect-timeout 15 -o "${tmpdir}/xray.zip" "$url" 2>&1; then colorEcho $RED " [错误] 下载 Xray 核心失败,请检查网络" rm -rf "$tmpdir"; exit 1 fi unzip -q "${tmpdir}/xray.zip" -d "${tmpdir}" 2>/dev/null systemctl stop xray.service 2>/dev/null || true rm -f /usr/bin/xray mkdir -p /etc/xray /var/log/xray cp "${tmpdir}/xray" /usr/bin/xray cp "${tmpdir}/geo"* /usr/bin/ 2>/dev/null || true chmod +x /usr/bin/xray # 下载最新的 GeoIP/GeoSite 路由规则,防止分流报错 colorEcho $BLUE " 下载 GeoIP/GeoSite 路由规则文件..." curl -sL -o /usr/bin/geoip.dat "${V6_PROXY}https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" curl -sL -o /usr/bin/geosite.dat "${V6_PROXY}https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" chmod 644 /usr/bin/geoip.dat /usr/bin/geosite.dat if ! /usr/bin/xray version >/dev/null 2>&1; then colorEcho $RED " [错误] Xray 安装异常,核心文件无法运行" rm -rf "$tmpdir"; exit 1 fi colorEcho $GREEN " Xray ${latest_ver} 安装成功" cat > "$XRAY_SERVICE" <<EOF [Unit] Description=Xray Service Documentation=https://xtls.github.io/ After=network.target nss-lookup.target [Service] User=root ExecStart=/usr/bin/xray run -config /etc/xray/config.json Restart=on-failure RestartSec=3 LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable xray.service rm -rf "$tmpdir" } # 挂载/卸载 Swap manageSwap() { local action="$1" if [[ "$action" == "add" ]]; then local mem_total=$(free -m | awk '/Mem:/ {print $2}') if [[ "$mem_total" -lt 1500 ]]; then colorEcho $YELLOW " 检测到可用内存不足 1.5G,尝试添加临时 Swap..." fallocate -l 2G /tmp/swapfile 2>/dev/null || dd if=/dev/zero of=/tmp/swapfile bs=1M count=2048 2>/dev/null || true chmod 600 /tmp/swapfile 2>/dev/null || true mkswap /tmp/swapfile >/dev/null 2>&1 || true swapon /tmp/swapfile >/dev/null 2>&1 || true SWAP_ADDED=1 fi elif [[ "$action" == "remove" && "$SWAP_ADDED" == "1" ]]; then swapoff /tmp/swapfile >/dev/null 2>&1 || true rm -f /tmp/swapfile colorEcho $GREEN " 临时 Swap 卸载完毕" fi } # 安装带 forward_proxy 插件的 Caddy installCaddy() { if [[ -f /usr/bin/caddy ]]; then local caddy_ver=$(/usr/bin/caddy version 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "") if [[ "$caddy_ver" == "v2.8.4" ]]; then colorEcho $GREEN " Caddy v2.8.4 已存在,跳过编译" return 0 else colorEcho $YELLOW " 检测到 Caddy ${caddy_ver:-未知},将重新编译至 v2.8.4" fi fi colorEcho $BLUE " 部署稳定 Go 编译环境..." local go_installed=false if [[ -f /usr/local/go/bin/go ]]; then local go_ver=$(/usr/local/go/bin/go version 2>/dev/null | grep -oE 'go[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1 || echo "") if [[ "$go_ver" == "go1.21.13" ]]; then colorEcho $GREEN " Go 1.21.13 已存在,跳过下载" go_installed=true fi fi if [[ "$go_installed" == false ]]; then rm -rf /usr/local/go local go_arch=$(getGoArch) local go_latest="go1.21.13" # 使用 V6_PROXY 防止纯 IPv6 机器下载失败 wget -qO- "${V6_PROXY}https://go.dev/dl/${go_latest}.linux-${go_arch}.tar.gz" | tar -C /usr/local -xz colorEcho $GREEN " Go 1.21.13 安装完成" fi export PATH=$PATH:/usr/local/go/bin export GOPATH=/root/go export PATH=$PATH:$GOPATH/bin export GOPROXY=https://goproxy.cn,https://goproxy.io,direct # 解决国内拉取 Go 模块超时 local xcaddy_bin="$GOPATH/bin/xcaddy" if [[ ! -f "$xcaddy_bin" ]]; then colorEcho $BLUE " 安装 xcaddy 工具..." go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest if [[ ! -f "$xcaddy_bin" ]]; then colorEcho $RED " [错误] xcaddy 依赖安装失败,请检查网络或更换 VPS" exit 1 fi colorEcho $GREEN " xcaddy 安装完成" else colorEcho $GREEN " xcaddy 已存在,跳过安装" fi manageSwap "add" colorEcho $BLUE " 编译带 forward_proxy 插件的 Caddy (预计需要 1~3 分钟)..." if ! "$xcaddy_bin" build v2.8.4 \ --with github.com/caddyserver/forwardproxy=github.com/klzgrad/forwardproxy@naive \ --output /usr/local/bin/caddy_custom; then manageSwap "remove" colorEcho $RED " [错误] Caddy 核心编译失败" exit 1 fi manageSwap "remove" systemctl stop naiveproxy.service 2>/dev/null || true rm -f /usr/bin/caddy mv /usr/local/bin/caddy_custom /usr/bin/caddy chmod +x /usr/bin/caddy colorEcho $GREEN " Caddy (集成 NaiveProxy) 编译成功" } # 生成Xray配置 genXrayConf() { mkdir -p /etc/xray colorEcho $BLUE " 生成 Reality 密钥对..." local keypair_output keypair_output=$(/usr/bin/xray x25519 2>&1) || { colorEcho $RED " x25519 命令执行失败" exit 1 } REALITY_PRIVATE_KEY=$(echo "$keypair_output" | grep -i "Private" | head -n1 | sed 's/.*:[[:space:]]*//' || echo "") REALITY_PUBLIC_KEY=$(echo "$keypair_output" | grep -i "Password\|Public" | head -n1 | sed 's/.*:[[:space:]]*//' || echo "") if [[ -z "$REALITY_PRIVATE_KEY" || -z "$REALITY_PUBLIC_KEY" ]]; then colorEcho $RED " 密钥解析失败,Xray 版本可能不兼容" echo "$keypair_output" exit 1 fi echo "$REALITY_PUBLIC_KEY" > /etc/xray/public_key.txt chmod 644 /etc/xray/public_key.txt local network="tcp" local xhttp_settings="{}" if [[ "$REALITY_MODE" == "xhttp" ]]; then network="xhttp" xhttp_settings="{\"path\": \"$XHTTP_PATH\", \"mode\": \"auto\"}" fi local sn_array=$(jq -Rc 'split(",") | map(gsub("^[ \t]+|[ \t]+$";"")) | map(select(length>0))' <<< "$REALITY_SERVER_NAMES") local sid_array=$(jq -Rc 'split(",") | map(gsub("^[ \t]+|[ \t]+$";"")) | map(select(length>0))' <<< "$REALITY_SHORT_IDS") local client_obj="{\"id\": \"$UUID\", \"level\": 0" if [[ -n "$FLOW" ]]; then client_obj="${client_obj}, \"flow\": \"$FLOW\"" fi client_obj="${client_obj}}" local new_inbound=$(jq -n \ --arg port "$PORT" \ --arg protocol "vless" \ --arg network "$network" \ --arg dest "$REALITY_DEST" \ --arg privateKey "$REALITY_PRIVATE_KEY" \ --argjson serverNames "$sn_array" \ --argjson shortIds "$sid_array" \ --argjson client "$client_obj" \ --argjson xhttpSettings "$xhttp_settings" \ '{ port: ($port|tonumber), protocol: $protocol, settings: { clients: [$client], decryption: "none" }, streamSettings: { network: $network, security: "reality", realitySettings: { show: false, dest: $dest, serverNames: $serverNames, privateKey: $privateKey, shortIds: $shortIds } } }') if [[ "$REALITY_MODE" == "xhttp" ]]; then new_inbound=$(echo "$new_inbound" | jq --argjson xs "$xhttp_settings" '.streamSettings.xhttpSettings = $xs') fi local existing="[]" if [[ -f "$XRAY_CONFIG" ]]; then existing=$(jq -c '.inbounds //[]' "$XRAY_CONFIG" 2>/dev/null) || existing="[]" fi local merged_inbounds=$(echo "$existing" | jq -c --argjson new "$new_inbound" --arg port "$PORT" 'map(select(.port != ($port|tonumber))) +[$new]') # 将日志级别降为 error,减少高并发下的磁盘 I/O 卡顿 jq -n --argjson inbounds "$merged_inbounds" '{ log: {loglevel: "error"}, inbounds: $inbounds, outbounds:[{protocol: "freedom", settings: {}}] }' > "$XRAY_CONFIG" } # 生成 NaiveProxy Caddyfile 配置 genNaiveConf() { mkdir -p /etc/naiveproxy # 提取公钥进行 MD5 对比,完美兼容 ECC 与 RSA 证书 colorEcho $BLUE " 正在验证证书与私钥是否匹配..." local cert_mod key_mod cert_mod=$(openssl x509 -in "$CERT_FILE" -noout -pubkey 2>/dev/null | openssl md5 | awk '{print $NF}' || echo "error") key_mod=$(openssl pkey -in "$KEY_FILE" -pubout 2>/dev/null | openssl md5 | awk '{print $NF}' || echo "error") if [[ "$cert_mod" == "error" || "$key_mod" == "error" ]]; then colorEcho $YELLOW " [警告] 无法验证密钥对(非标准格式或 openssl 缺失),将尝试继续..." elif [[ "$cert_mod" != "$key_mod" ]]; then colorEcho $RED " [致命错误] 证书公钥与私钥不匹配!" colorEcho $YELLOW " 请使用配套的 .pem/.crt 和 .key 文件" exit 1 else colorEcho $GREEN " [√] 证书与私钥匹配验证通过" fi local safe_domain=$(echo "$DOMAIN" | tr -cd 'a-zA-Z0-9.-') local safe_user=$(echo "$NAIVE_USER" | tr -cd 'a-zA-Z0-9_=@.-') local safe_pass=$(echo "$NAIVE_PASS" | tr -cd 'a-zA-Z0-9_=@.!*~-') if [[ -z "$safe_domain" || -z "$safe_user" || -z "$safe_pass" ]]; then colorEcho $RED " 参数验证失败"; exit 1 fi cat > /etc/naiveproxy/Caddyfile <<CEOF { admin off order forward_proxy before file_server } https://${safe_domain}:${PORT} { tls "${CERT_FILE}" "${KEY_FILE}" forward_proxy { basic_auth ${safe_user} ${safe_pass} hide_ip hide_via probe_resistance secret.localhost } } CEOF cat > "$NAIVE_SERVICE" <<'SEOF' [Unit] Description=NaiveProxy Service After=network.target [Service] Type=simple User=root ExecStart=/usr/bin/caddy run --config /etc/naiveproxy/Caddyfile --adapter caddyfile Restart=on-failure RestartSec=3 LimitNOFILE=65536 [Install] WantedBy=multi-user.target SEOF systemctl daemon-reload systemctl enable naiveproxy.service } # 启动服务 startSvc() { colorEcho $BLUE " 正在启动并验证服务状态..." if [[ "$PROTOCOL_TYPE" == "vless" ]]; then systemctl restart xray.service local retries=5 for ((i=1; i<=retries; i++)); do sleep 2 if systemctl is-active --quiet xray.service; then colorEcho $GREEN " Xray 启动成功并在后台运行中" return 0 fi colorEcho $BLUE " 等待启动中... ($i/$retries)" done colorEcho $RED " [致命错误] Xray 启动超时,请运行 journalctl -u xray.service -n 50 查看日志" return 1 else systemctl restart naiveproxy.service local retries=5 for ((i=1; i<=retries; i++)); do sleep 2 if systemctl is-active --quiet naiveproxy.service; then colorEcho $GREEN " NaiveProxy 启动成功并在后台运行中" return 0 fi colorEcho $BLUE " 等待启动中... ($i/$retries)" done colorEcho $RED " [致命错误] NaiveProxy 启动超时,请运行 journalctl -u naiveproxy.service -n 50 查看日志" return 1 fi } # 辅助记录节点 getNextNodeIndex() { local node_type="$1" local config_dir="/etc/xray/nodes" local max_index=0 if [[ ! -d "$config_dir" ]]; then echo 1 return fi for f in "$config_dir"/${node_type}_node*.conf; do [[ -f "$f" ]] || continue local idx idx=$(echo "$f" | grep -o 'node[0-9]*' | grep -o '[0-9]*' || echo "0") if [[ "$idx" =~ ^[0-9]+$ ]] && [[ "$idx" -gt "$max_index" ]]; then max_index=$idx fi done echo $((max_index + 1)) } saveNodeConfig() { local node_type="$1" local config_dir="/etc/xray/nodes" mkdir -p "$config_dir" && chmod 700 "$config_dir" local node_index=$(getNextNodeIndex "$node_type") if [[ "$node_type" == "vless" ]]; then cat > "$config_dir/vless_node${node_index}.conf" <<EOF NODE_ALIAS="${NODE_ALIAS}" PORT="${PORT}" UUID="${UUID}" FLOW="${FLOW}" REALITY_MODE="${REALITY_MODE}" XHTTP_PATH="${XHTTP_PATH}" REALITY_PUBLIC_KEY="${REALITY_PUBLIC_KEY}" REALITY_PRIVATE_KEY="${REALITY_PRIVATE_KEY}" REALITY_SERVER_NAMES="${REALITY_SERVER_NAMES}" REALITY_SHORT_IDS="${REALITY_SHORT_IDS}" REALITY_DEST="${REALITY_DEST}" IP="${IP}" NODE_TYPE=vless NODE_INDEX=${node_index} EOF chmod 600 "$config_dir/vless_node${node_index}.conf" elif [[ "$node_type" == "naive" ]]; then cat > "$config_dir/naive_node${node_index}.conf" <<EOF NODE_ALIAS="${NODE_ALIAS}" DOMAIN="${DOMAIN}" PORT="${PORT}" NAIVE_USER="${NAIVE_USER}" NAIVE_PASS="${NAIVE_PASS}" CERT_FILE="${CERT_FILE}" KEY_FILE="${KEY_FILE}" NODE_TYPE=naive NODE_INDEX=${node_index} EOF chmod 600 "$config_dir/naive_node${node_index}.conf" fi } showVLESSInstall() { echo " =======================================" colorEcho $GREEN " VLESS + Reality 配置 - ${NODE_ALIAS}" echo " =======================================" echo " 地址: $IP | 端口: $PORT" echo " UUID: $UUID" if [[ "$REALITY_MODE" == "xhttp" ]]; then echo " 模式: XHTTP | 路径: $XHTTP_PATH" else echo " 模式: Vision | 流控: ${FLOW}" fi echo " 公钥: $REALITY_PUBLIC_KEY" echo " SNI: $REALITY_SERVER_NAMES" echo "" local link="vless://$UUID@$IP:$PORT?encryption=none&security=reality" if [[ "$REALITY_MODE" == "xhttp" ]]; then link="${link}&type=xhttp&path=$(url_encode "$XHTTP_PATH")" else link="${link}&type=tcp&flow=$(url_encode "${FLOW}")" fi link="${link}&sni=$(echo $REALITY_SERVER_NAMES | cut -d',' -f1)&pbk=$REALITY_PUBLIC_KEY&sid=$(echo $REALITY_SHORT_IDS | cut -d',' -f1)#$(url_encode "$NODE_ALIAS")" colorEcho $BLUE " 客户端分享链接 (复制导入):" echo "$link" echo "" } showNaiveInstall() { echo " =======================================" colorEcho $GREEN " NaiveProxy + TLS 配置 - ${NODE_ALIAS}" echo " =======================================" echo " 域名: $DOMAIN | 端口: $PORT" echo " 用户: $NAIVE_USER | 密码: $NAIVE_PASS" echo "" colorEcho $BLUE " 客户端分享链接 (复制导入):" echo "naive+https://$(url_encode "$NAIVE_USER"):$(url_encode "$NAIVE_PASS")@${DOMAIN}:${PORT}#$(url_encode "$NODE_ALIAS")" echo "" } showAllConfigs() { colorEcho $BLUE "正在获取本地节点记录..." local found=false for conf in /etc/xray/nodes/vless_node*.conf; do [[ -f "$conf" ]] || continue found=true safe_read_conf "$conf" showVLESSInstall done for conf in /etc/xray/nodes/naive_node*.conf; do [[ -f "$conf" ]] || continue found=true safe_read_conf "$conf" showNaiveInstall done if [[ "$found" == false ]]; then colorEcho $YELLOW " 未找到任何节点配置" fi } install() { if [[ "$PROTOCOL_TYPE" == "vless" ]]; then getDataVLESS setFirewall installXray genXrayConf if startSvc; then showVLESSInstall saveNodeConfig "vless" fi else getDataNaive || { colorEcho $YELLOW " 操作已取消"; return; } setFirewall installCaddy genNaiveConf if startSvc; then showNaiveInstall saveNodeConfig "naive" fi fi } uninstall() { read -p " 确认卸载所有组件(包括节点配置)?[y/n]: " answer if [[ "${answer,,}" != "y" ]]; then return fi systemctl disable --now xray.service naiveproxy.service 2>/dev/null || true rm -f /usr/bin/xray /usr/bin/caddy /usr/bin/geoip.dat /usr/bin/geosite.dat rm -f /etc/systemd/system/xray.service /etc/systemd/system/naiveproxy.service rm -f "$XRAY_CONFIG" rm -rf /etc/naiveproxy /var/log/xray /etc/xray/nodes rm -f /etc/xray/public_key.txt systemctl daemon-reload colorEcho $GREEN " 卸载清理完成" colorEcho $BLUE " 所有组件和配置已彻底清除" } menu() { while true; do clear echo "##################################################################" echo -e "# ${GREEN}程序员歌${PLAIN} #" echo -e "# ${GREEN}酒醒只在屏前坐,酒醉还是屏下眠。${PLAIN} #" echo -e "# ${GREEN}梦里只见她依旧,回首自是单身狗。${PLAIN} #" echo -e "# ${GREEN}相思只能出粘液,相见才能起泡沫。${PLAIN} #" echo -e "# ${GREEN}但见满街漂亮妹,哪个归得程序员。${PLAIN} #" echo "##################################################################" echo "" echo " =======================================" echo -e " ${GREEN}最稳三剑客${PLAIN}" echo -e " ${GREEN}VLESS + Reality + Vision${PLAIN}" echo -e " ${GREEN}VLESS + Reality + XHTTP${PLAIN}" echo -e " ${GREEN}NaiveProxy + TLS${PLAIN}" echo " =======================================" echo -e " ${GREEN}1.${PLAIN} 安装 VLESS + Reality (XHTTP/Vision)" echo -e " ${GREEN}2.${PLAIN} 安装 NaiveProxy + TLS" echo " ---------------------------------------" echo -e " ${GREEN}3.${PLAIN} 卸载全部" echo -e " ${GREEN}4.${PLAIN} 重启服务" echo -e " ${GREEN}5.${PLAIN} 查看所有节点配置" echo " ---------------------------------------" echo -e " ${GREEN}00.${PLAIN} 退出脚本" echo "" read -p " 请选择你的操作 [1-5, 00退出]: " choice case "$choice" in 1) PROTOCOL_TYPE="vless"; install read -p " 操作完成,是否返回主菜单?[y/n 默认y]: " _back if [[ "${_back,,}" != "n" ]]; then continue; else exit 0; fi ;; 2) PROTOCOL_TYPE="naive"; install read -n1 -s -p " 按任意键返回主菜单..." ;; 3) uninstall read -p " 操作完成,是否返回主菜单?[y/n 默认y]: " _back if [[ "${_back,,}" != "n" ]]; then continue; else exit 0; fi ;; 4) systemctl restart xray.service naiveproxy.service 2>/dev/null || colorEcho $YELLOW " 部分服务不存在" colorEcho $GREEN " 指令已下发,所有在线服务已重启" ;; 5) showAllConfigs ;; 00) exit 0 ;; *) colorEcho $RED " 无效选择,请重新输入" ;; esac echo "" read -n1 -s -p " 按任意键继续..." done } # --- 脚本入口 --- checkSystem if ! getIP; then colorEcho $RED "[致命错误] 无法获取服务器 IP,请检查网络连接" exit 1 fi installBaseDeps enableBBR menu ```
# 节点
← 上一篇
Windows免工具纯命令行激活全指南
下一篇 →
IDM下载神器
💬 评论
0
/1000
发表评论
加载中...
本脚本集成了目前对抗 GFW 最强、最稳的三种节点协议,专为追求**高成功率、极致防封、低延迟不卡顿**的用户打造。 **支持协议:** 1. `VLESS + Reality + XHTTP` (最新伪装,防封王者) 2. `VLESS + Reality + Vision` (极致性能,低延迟首选) 3. `NaiveProxy + TLS` (完美伪装 Chrome 浏览器流量) --- ## 🛠️ 准备条件 * **VPS 服务器**:一台国外 VPS(推荐 Ubuntu 20.04+ / Debian 11+ / CentOS 8+),**必须是全新或干净的 IP**,已被墙的 IP 无法直连。 * **权限要求**:必须拥有 `root` 权限。 * **域名(可选/必选)**: * 安装 **VLESS+Reality**:**不需要**域名(使用真实 IP 直连)。 * 安装 **NaiveProxy**:**必须**有一个域名,且已将 DNS A 记录解析到该 VPS 的 IP。 --- ## 📥 使用方法 1. 使用 SSH 工具(如 Xshell, Termius, PuTTY)连接到你的 VPS。 2. 在服务器新建一个文件(例如 `hbb_proxy.sh`),将脚本代码复制进去并保存。 3. 赋予执行权限并运行: ```bash chmod +x hbb_proxy.sh ./hbb_proxy.sh ``` *(或者直接跟随终端菜单的中文提示,按数字键选择操作即可。)* --- ## ⚠️ 注意事项与进阶避坑指南 (必读!) 该脚本已经尽最大可能避免了系统级冲突,并**内置了 BBR 与内核网络高并发优化(防卡顿)**。但在作为生产力工具使用时,请务必关注以下细节: ### 1. 🛡️ 云服务商的外层防火墙 (安全组) 虽然脚本已经内置了 `iptables/firewalld/UFW` 的自动放行及规则持久化,但如果你使用的是**阿里云、腾讯云、AWS、Google Cloud、Oracle** 等云服务商的机器: * **必须**登录云服务商的网页控制台。 * 找到“安全组”或“防火墙”面板,**手动放行**你刚才在脚本中设置的 TCP 端口(例如 `443` 或 `8443`)。否则外部无法连接。 ### 2. 🚫 关于 CDN (Cloudflare) 的使用限制 这两种协议目前都属于**强特征对抗型协议**,仅限**真实 IP 直连**使用: * **VLESS + Reality**:依赖直接的 TLS 握手特征伪装(“偷”目标网站的证书)。如果套上 Cloudflare,客户端握手的是 CF 的证书,Reality 直接失效。**绝对不能开 CDN**。 * **NaiveProxy**:依赖 HTTP/2 的特定头部和填充字节。若开启 Cloudflare 代理(点亮小黄云),CF 会清洗掉这些特征,导致客户端无法建立连接。 * **结论**:均不支持免费版 Cloudflare CDN。如果 IP 被墙,只能考虑更换 IP 或套接中转机。 ### 3. 🔄 NaiveProxy 专属:TLS 证书的热重载陷阱 在使用“选项 2 (NaiveProxy)”时,你需要提供硬盘上的证书路径。 * **原理**:Caddy (Naive 的后端) 通常会自动监控证书文件变化并无感重载。 * **避坑**:如果你使用第三方的 `crontab` 定时脚本(如 `acme.sh`)强制覆盖证书文件,某些情况下 Caddy 可能无法捕获文件 `inode` 的改变,导致内存中仍使用旧证书。 * **解决方案**:建议在证书续期脚本的“续期成功”钩子(Hook)中,加上一行强制重启命令: ```bash systemctl restart naiveproxy ``` *踹它一脚让它重启,确保 90 天后节点不会因为证书过期而断连。* --- ## 🛠️ 安装配置指南 ### 方案 A:安装 VLESS + Reality(推荐,成功率最高,免域名) 1. **选择选项 `1`** 2. **节点别名**:随便填(如 `日本-东京-Reality`),仅用于客户端显示。 3. **端口**:推荐 `443`(如果被占用,可换 `8443` 等高位端口,**记得去云控制台放行**)。 4. **UUID**:直接回车留空,脚本会自动生成安全的 UUID。 5. **传输模式**(核心选择): * `1) XHTTP`:**伪装性最强**,将流量伪装成普通网页请求,GFW 最难识别,适合重度封锁区。 * `2) Vision`:**性能更高**,延迟更低,适合打游戏或追求极致流畅度的用户。 6. **Reality 伪装域名**:直接回车,默认使用 `www.microsoft.com:443`(也可自定义为 `www.apple.com` 等大厂域名)。 7. **ServerNames**:直接回车,默认 `www.microsoft.com`。 8. **ShortIds**:直接回车,自动生成。 ### 方案 B:安装 NaiveProxy + TLS(极致伪装,需域名和证书) 1. **选择选项 `2`** 2. **节点别名**:随便填。 3. **解析到本机的域名**:**必须填**,且确保 DNS 已经正确解析到本 VPS 的 IP。 4. **HTTPS 端口**:推荐 `443`。 5. **用户名/密码**:留空使用默认值,或自定义。 6. **证书路径配置**: * 你需要提前在服务器上申请好 SSL 证书(推荐使用 `acme.sh` 或 `certbot`)。 * **证书公钥路径**:填绝对路径,如 `/root/cert/fullchain.pem` * **证书私钥路径**:填绝对路径,如 `/root/cert/private.key` > **💡 补充:如何快速申请 Let's Encrypt 证书?** > 如果你还没有证书,可以在运行本脚本前,执行以下命令快速申请(需先安装 `socat`): > ```bash > curl https://get.acme.sh | sh -s email=my@example.com > ~/.acme.sh/acme.sh --issue -d 你的域名.com --standalone > # 申请成功后,证书路径通常在 /root/.acme.sh/你的域名.com_ecc/ 目录下 > ``` --- ## 📱 客户端推荐 (搭配使用效果更佳) 搭建完成后,请使用支持 **VLESS-Reality** 和 **NaiveProxy** 的现代客户端导入生成的分享链接: * **Windows**: `v2rayN` (最新版), `Hiddify`, `Clash.Meta (mihomo)` * **Android**: `v2rayNG`, `NekoBox`, `Hiddify` * **iOS / macOS**: `Shadowrocket` (小火箭), `Stash`, `Hiddify` * **路由器 (OpenWrt)**: `PassWall2`, `SSR+`, `OpenClash` (需 Meta 核心) --- ## ❓ 常见问题 (FAQ) **Q: 脚本提示“内核版本过低,不支持 BBR”怎么办?** A: 脚本内置了 BBR 和 `fq` 队列优化以防止高并发卡顿。如果你的 VPS 系统太老(内核低于 4.9),脚本会跳过此步骤。建议更换为 Ubuntu 22.04 或 Debian 12 等现代系统以获得最佳网络性能。 **Q: 如何查看已经安装的节点信息?** A: 运行脚本,选择主菜单的 **`5. 查看所有节点配置`**,脚本会读取本地记录并重新输出所有节点的分享链接。 **Q: 如何彻底卸载?** A: 运行脚本,选择主菜单的 **`3. 卸载全部`**,脚本会停止服务、删除核心文件、清空防火墙规则及节点记录,做到真正的“无痕卸载”。 ```bash #!/bin/bash # Xray (VLESS + Reality/XHTTP/Vision) / NaiveProxy + TLS # Author: HBB | Crafted with soul & code # 优化版: 修复注入漏洞、兼容ECC证书、支持纯IPv6、加入BBR与内核高并发优化 set -euo pipefail RED="\033[31m" GREEN="\033[32m" YELLOW="\033[33m" BLUE="\033[36m" PLAIN='\033[0m' # 白名单命令列表(防止任意命令执行) ALLOWED_COMMANDS=("menu" "uninstall") XRAY_CONFIG="/etc/xray/config.json" XRAY_SERVICE="/etc/systemd/system/xray.service" NAIVE_SERVICE="/etc/systemd/system/naiveproxy.service" V6_PROXY="" SWAP_ADDED=0 PMT="" CMD_INSTALL="" # 系统检查与包管理器设定 checkSystem() { if [[ $EUID -ne 0 ]]; then colorEcho $RED "需要 root 权限" exit 1 fi if [[ -f /etc/debian_version ]]; then PMT="apt" CMD_INSTALL="apt update && apt install -y " elif [[ -f /etc/redhat-release ]]; then PMT="yum" CMD_INSTALL="yum install -y " else colorEcho $RED "不支持的系统,仅支持 Debian/Ubuntu 或 CentOS/RHEL" exit 1 fi } # 安装基础依赖 (加入了 bc 用于内核版本计算) installBaseDeps() { colorEcho $BLUE "正在检查并安装基础环境依赖..." if [[ "$PMT" == "apt" ]]; then $CMD_INSTALL wget curl openssl jq net-tools iproute2 unzip tar git dnsutils bc >/dev/null 2>&1 else $CMD_INSTALL wget curl openssl jq net-tools iproute unzip tar git bind-utils bc >/dev/null 2>&1 fi } colorEcho() { echo -e "${1}${@:2}${PLAIN}" } # 安全解析配置文件(彻底摒弃 eval,使用 printf -v 杜绝命令注入) safe_read_conf() { local file="$1" [[ -f "$file" ]] || return 1 while IFS='=' read -r key value; do value=$(echo "$value" | sed "s/^['\"]//;s/['\"]$//;s/^[[:space:]]*//;s/[[:space:]]*$//") case "$key" in NODE_ALIAS|PORT|UUID|FLOW|REALITY_MODE|XHTTP_PATH|REALITY_PUBLIC_KEY|REALITY_PRIVATE_KEY|REALITY_SERVER_NAMES|REALITY_SHORT_IDS|REALITY_DEST|IP|NODE_TYPE|NODE_INDEX|DOMAIN|NAIVE_USER|NAIVE_PASS|CERT_FILE|KEY_FILE) printf -v "$key" '%s' "$value" ;; esac done < "$file" return 0 } # 获取IP getIP() { for svc in ipinfo.io api.ipify.org ifconfig.me; do local ip=$(curl -sL --connect-timeout 5 -4 https://${svc}/ip 2>/dev/null) if [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then IP="$ip" return 0 fi done # 如果 IPv4 失败,尝试 IPv6 IP=$(curl -sL --connect-timeout 5 -6 https://api64.ipify.org 2>/dev/null || echo "") if [[ -n "$IP" ]]; then V6_PROXY="https://ghp.ci/" return 0 fi return 1 } # URL编码 (使用 jq 完美支持中文及所有 UTF-8 字符) url_encode() { jq -nr --arg v "$1" '$v|@uri' } # CPU架构获取 getArch() { case "$(uname -m)" in x86_64|amd64) echo "64" ;; *armv8*|aarch64) echo "arm64-v8a" ;; *) colorEcho $RED "不支持的 CPU 架构: $(uname -m)"; exit 1 ;; esac } getGoArch() { case "$(uname -m)" in x86_64|amd64) echo "amd64" ;; *armv8*|aarch64) echo "arm64" ;; *) colorEcho $RED "不支持的 CPU 架构用于编译: $(uname -m)"; exit 1 ;; esac } # 高精度端口冲突拦截 checkPort() { local p=$1 local target_svc=$2 local pid_info="" if command -v ss >/dev/null 2>&1; then pid_info=$(ss -tlnp 2>/dev/null | awk -v port=":$p " 'index($0, port) > 0') elif command -v netstat >/dev/null 2>&1; then pid_info=$(netstat -tlnp 2>/dev/null | awk -v port=":$p " 'index($0, port) > 0') fi if [[ -n "$pid_info" ]]; then if [[ "$target_svc" == "vless" ]] && echo "$pid_info" | grep -qi "xray"; then colorEcho $YELLOW " 提示: 端口 $p 正被旧版 Xray 占用,安装后将覆盖接管。" return 0 elif [[ "$target_svc" == "naive" ]] && echo "$pid_info" | grep -qi "caddy"; then colorEcho $YELLOW " 提示: 端口 $p 正被旧版 Naive(Caddy) 占用,安装后将覆盖接管。" return 0 else local proc_info proc_info=$(echo "$pid_info" | head -n 1 | sed -n 's/.*users:\(\([^]]*\)\).*/\1/p' || echo "未知进程") colorEcho $RED " [拦截] 错误: 端口 $p 已被其他进程占用!" colorEcho $YELLOW " 占用详情: $proc_info" return 1 fi fi return 0 } # 开启 BBR 与内核网络高并发优化 (防卡顿核心) enableBBR() { colorEcho $BLUE "正在检查并优化底层网络参数 (BBR/队列)..." local bbr_status=$(sysctl net.ipv4.tcp_congestion_control 2>/dev/null | awk '{print $3}') if [[ "$bbr_status" == "bbr" ]]; then colorEcho $GREEN " BBR 已开启,跳过" return fi local kernel_ver=$(uname -r | grep -oE '^[0-9]+\.[0-9]+') local is_supported=0 if command -v bc &>/dev/null; then is_supported=$(echo "$kernel_ver >= 4.9" | bc -l 2>/dev/null || echo 0) else [[ "${kernel_ver%%.*}" -ge 5 ]] && is_supported=1 [[ "${kernel_ver%%.*}" -eq 4 ]] && [[ "${kernel_ver#*.}" -ge 9 ]] && is_supported=1 fi if [[ "$is_supported" -eq 1 ]]; then # 清除旧的冲突配置 sed -i '/net.core.default_qdisc/d' /etc/sysctl.conf sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.conf cat >> /etc/sysctl.conf <<EOF # Xray/Naive 高并发防卡顿优化 (by HBB Script) net.core.default_qdisc=fq net.ipv4.tcp_congestion_control=bbr net.core.rmem_max=16777216 net.core.wmem_max=16777216 net.ipv4.tcp_rmem=4096 87380 16777216 net.ipv4.tcp_wmem=4096 16384 16777216 EOF sysctl -p >/dev/null 2>&1 colorEcho $GREEN " BBR 与内核网络参数优化成功" else colorEcho $YELLOW " 内核版本 ($kernel_ver) 可能不支持 BBR,跳过优化" fi } # VLESS配置输入 getDataVLESS() { echo "" colorEcho $BLUE "[VLESS + Reality 配置]" read -p " 节点别名[默认: VLESS-Reality]: " input_alias NODE_ALIAS=$(echo "$input_alias" | tr -cd 'a-zA-Z0-9_\-\u4e00-\u9fff .') NODE_ALIAS="${NODE_ALIAS:-VLESS-Reality}" while true; do read -p " 监听端口 [默认: 443]: " input_port PORT="${input_port:-443}" if ! [[ "$PORT" =~ ^[0-9]+$ ]] || [[ "$PORT" -lt 1 ]] || [[ "$PORT" -gt 65535 ]]; then colorEcho $RED " 端口无效,请输入 1-65535 之间的数字" continue fi if checkPort "$PORT" "vless"; then break fi done read -p " UUID [留空自动随机]: " input_uuid if [[ "$input_uuid" =~ ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ ]]; then UUID="$input_uuid" else UUID=$(cat /proc/sys/kernel/random/uuid) fi echo "" colorEcho $BLUE " 传输模式:" echo -e " 1) XHTTP ${BLUE}(最新伪装推荐)${PLAIN}" echo -e " 2) Vision ${BLUE}(高性能推荐)${PLAIN}" read -p " 选择[1-2 默认1]: " mode_choice REALITY_MODE=$([[ "$mode_choice" == "2" ]] && echo "vision" || echo "xhttp") if [[ "$REALITY_MODE" == "vision" ]]; then FLOW="xtls-rprx-vision" XHTTP_PATH="" else FLOW="" read -p " XHTTP路径[默认: /xhttp]: " input_path XHTTP_PATH="${input_path:-/xhttp}" if [[ "${XHTTP_PATH:0:1}" != "/" ]]; then XHTTP_PATH="/${XHTTP_PATH}" fi fi echo "" colorEcho $BLUE " Reality 伪装配置" read -p " 伪装目标域名[默认: www.microsoft.com:443]: " input_dest if [[ -z "$input_dest" ]]; then REALITY_DEST="www.microsoft.com:443" elif [[ "$input_dest" =~ ^[a-zA-Z0-9.-]+:[0-9]+$ ]]; then REALITY_DEST="$input_dest" elif [[ "$input_dest" =~ ^[a-zA-Z0-9.-]+$ ]]; then REALITY_DEST="${input_dest}:443" else colorEcho $YELLOW " 格式无效,使用默认值" REALITY_DEST="www.microsoft.com:443" fi read -p " ServerNames (逗号分隔) [默认: www.microsoft.com]: " input_sn REALITY_SERVER_NAMES=$(echo "$input_sn" | tr -cd 'a-zA-Z0-9.,-') if [[ -z "$REALITY_SERVER_NAMES" ]]; then REALITY_SERVER_NAMES="www.microsoft.com" fi read -p " ShortIds[留空自动随机]: " input_sid if [[ "$input_sid" =~ ^[0-9a-fA-F]+$ ]]; then REALITY_SHORT_IDS="$input_sid" else REALITY_SHORT_IDS=$(openssl rand -hex 8) fi } # NaiveProxy配置输入 getDataNaive() { echo "" colorEcho $BLUE "[NaiveProxy + TLS 配置]" read -p " 节点别名[默认: NaiveProxy]: " input_alias NODE_ALIAS=$(echo "$input_alias" | tr -cd 'a-zA-Z0-9_\-\u4e00-\u9fff .') NODE_ALIAS="${NODE_ALIAS:-NaiveProxy}" read -p " 解析到本机的域名: " input_domain if [[ -z "$input_domain" ]]; then colorEcho $RED " 域名不能为空" exit 1 fi DOMAIN=$(echo "$input_domain" | tr '[:upper:]' '[:lower:]') while true; do read -p " HTTPS端口[默认: 443]: " input_port PORT="${input_port:-443}" if ! [[ "$PORT" =~ ^[0-9]+$ ]] || [[ "$PORT" -lt 1 ]] || [[ "$PORT" -gt 65535 ]]; then colorEcho $RED " 端口无效,请输入 1-65535" continue fi if checkPort "$PORT" "naive"; then break fi done read -p " 用户名[默认: user]: " input_user NAIVE_USER="${input_user:-user}" read -p " 密码 [留空自动随机生成]: " input_pass if [[ -z "$input_pass" ]]; then NAIVE_PASS=$(openssl rand -base64 12 | tr -d '\n' | sed 's/[+/=]/x/g') else NAIVE_PASS="$input_pass" fi # 手动指定证书路径 echo "" colorEcho $BLUE " 请指定 SSL/TLS 证书路径" colorEcho $YELLOW " 提示: SSL 证书需要两个配套文件" colorEcho $YELLOW " ① 证书文件: 包含域名公钥 (.pem/.crt/.cer)" colorEcho $YELLOW " ② 私钥文件: 与证书配对的私钥 (.key)" echo "" while true; do colorEcho $BLUE " [1/2] 请输入证书文件路径:" read -p " 路径: " input_cert CERT_FILE=$(echo "$input_cert" | sed "s/^['\"]//;s/['\"]$//" | awk '{print $1}') if [[ -z "$CERT_FILE" ]]; then colorEcho $RED " [错误] 路径不能为空" continue fi if echo "$CERT_FILE" | grep -Pq '[^\x00-\x7F]'; then colorEcho $YELLOW " [提示] 检测到路径包含中文,建议移动到英文路径目录以防兼容性问题" read -p " 是否继续?[y/n]: " confirm if [[ "${confirm,,}" != "y" ]]; then continue fi fi if [[ -f "$CERT_FILE" ]]; then local perms=$(stat -c %a "$CERT_FILE" 2>/dev/null || stat -f %A "$CERT_FILE" 2>/dev/null) if [[ "$perms" != "600" && "$perms" != "400" ]]; then colorEcho $YELLOW " [警告] 证书文件权限为 $perms,建议设置为 600 或 400" read -p " 是否继续?[y/n 输入r返回主菜单]: " confirm if [[ "${confirm,,}" == "r" ]]; then return 1 elif [[ "${confirm,,}" != "y" ]]; then continue fi fi colorEcho $GREEN " [√] 证书文件已确认: $CERT_FILE" break else colorEcho $RED " [错误] 文件不存在: $CERT_FILE" read -p " 输入 r 返回主菜单,或按回车重新输入: " retry_action if [[ "${retry_action,,}" == "r" ]]; then return 1 fi fi done echo "" while true; do colorEcho $BLUE " [2/2] 请输入对应的私钥文件路径:" read -p " 路径: " input_key KEY_FILE=$(echo "$input_key" | sed "s/^['\"]//;s/['\"]$//" | awk '{print $1}') if [[ -z "$KEY_FILE" ]]; then colorEcho $RED " [错误] 路径不能为空" continue fi if [[ -f "$KEY_FILE" ]]; then local perms=$(stat -c %a "$KEY_FILE" 2>/dev/null || stat -f %A "$KEY_FILE" 2>/dev/null) if [[ "$perms" != "600" && "$perms" != "400" ]]; then colorEcho $RED " [错误] 私钥文件权限为 $perms,必须为 600 或 400" colorEcho $BLUE " 请执行: chmod 600 $KEY_FILE" read -p " 输入 r 返回主菜单,或按回车重新输入: " retry_action if [[ "${retry_action,,}" == "r" ]]; then return 1 fi continue fi colorEcho $GREEN " [√] 私钥文件已确认: $KEY_FILE" break else colorEcho $RED " [错误] 文件不存在: $KEY_FILE" read -p " 输入 r 返回主菜单,或按回车重新输入: " retry_action if [[ "${retry_action,,}" == "r" ]]; then return 1 fi fi done } # 防火墙设置 setFirewall() { colorEcho $BLUE "正在配置防火墙放行端口 ${PORT}..." if command -v ufw &>/dev/null; then local ufw_status ufw_status=$(ufw status 2>/dev/null || echo "") if echo "$ufw_status" | grep -q "Status: active"; then ufw allow ${PORT}/tcp 2>/dev/null || true return fi fi if command -v firewall-cmd &>/dev/null; then if systemctl is-active firewalld &>/dev/null; then firewall-cmd --permanent --add-port=${PORT}/tcp 2>/dev/null || true firewall-cmd --reload 2>/dev/null || true return fi fi if command -v iptables &>/dev/null; then iptables -C INPUT -p tcp --dport ${PORT} -j ACCEPT 2>/dev/null || \ iptables -A INPUT -p tcp --dport ${PORT} -j ACCEPT 2>/dev/null || true if command -v netfilter-persistent &>/dev/null; then netfilter-persistent save &>/dev/null || true elif command -v iptables-save &>/dev/null; then mkdir -p /etc/iptables iptables-save > /etc/iptables/rules.v4 2>/dev/null || true fi fi } # 安装Xray installXray() { local arch=$(getArch) colorEcho $BLUE " 获取 Xray 最新稳定版本..." local latest_ver=$(curl -sL -o /dev/null -w '%{url_effective}' "https://github.com/XTLS/Xray-core/releases/latest" | rev | cut -d'/' -f1 | rev) if [[ -z "$latest_ver" || "$latest_ver" == "releases" ]]; then latest_ver="v24.11.11" colorEcho $YELLOW " 获取最新版本号失败,采用兜底稳定版: ${latest_ver}" fi if [[ -f /usr/bin/xray ]]; then local current_ver current_ver=$(/usr/bin/xray version 2>/dev/null | head -n1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1 || echo "") if [[ -n "$current_ver" ]]; then current_ver="v${current_ver}" if [[ "$current_ver" == "$latest_ver" ]]; then colorEcho $GREEN " Xray ${latest_ver} 已是最新版本,跳过下载" return 0 else colorEcho $YELLOW " 检测到版本 ${current_ver} -> ${latest_ver},将升级" fi fi fi local url="${V6_PROXY}https://github.com/XTLS/Xray-core/releases/download/${latest_ver}/Xray-linux-${arch}.zip" colorEcho $BLUE " 下载 Xray ${latest_ver}..." local tmpdir=$(mktemp -d) if ! curl -L --connect-timeout 15 -o "${tmpdir}/xray.zip" "$url" 2>&1; then colorEcho $RED " [错误] 下载 Xray 核心失败,请检查网络" rm -rf "$tmpdir"; exit 1 fi unzip -q "${tmpdir}/xray.zip" -d "${tmpdir}" 2>/dev/null systemctl stop xray.service 2>/dev/null || true rm -f /usr/bin/xray mkdir -p /etc/xray /var/log/xray cp "${tmpdir}/xray" /usr/bin/xray cp "${tmpdir}/geo"* /usr/bin/ 2>/dev/null || true chmod +x /usr/bin/xray # 下载最新的 GeoIP/GeoSite 路由规则,防止分流报错 colorEcho $BLUE " 下载 GeoIP/GeoSite 路由规则文件..." curl -sL -o /usr/bin/geoip.dat "${V6_PROXY}https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" curl -sL -o /usr/bin/geosite.dat "${V6_PROXY}https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" chmod 644 /usr/bin/geoip.dat /usr/bin/geosite.dat if ! /usr/bin/xray version >/dev/null 2>&1; then colorEcho $RED " [错误] Xray 安装异常,核心文件无法运行" rm -rf "$tmpdir"; exit 1 fi colorEcho $GREEN " Xray ${latest_ver} 安装成功" cat > "$XRAY_SERVICE" <<EOF [Unit] Description=Xray Service Documentation=https://xtls.github.io/ After=network.target nss-lookup.target [Service] User=root ExecStart=/usr/bin/xray run -config /etc/xray/config.json Restart=on-failure RestartSec=3 LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable xray.service rm -rf "$tmpdir" } # 挂载/卸载 Swap manageSwap() { local action="$1" if [[ "$action" == "add" ]]; then local mem_total=$(free -m | awk '/Mem:/ {print $2}') if [[ "$mem_total" -lt 1500 ]]; then colorEcho $YELLOW " 检测到可用内存不足 1.5G,尝试添加临时 Swap..." fallocate -l 2G /tmp/swapfile 2>/dev/null || dd if=/dev/zero of=/tmp/swapfile bs=1M count=2048 2>/dev/null || true chmod 600 /tmp/swapfile 2>/dev/null || true mkswap /tmp/swapfile >/dev/null 2>&1 || true swapon /tmp/swapfile >/dev/null 2>&1 || true SWAP_ADDED=1 fi elif [[ "$action" == "remove" && "$SWAP_ADDED" == "1" ]]; then swapoff /tmp/swapfile >/dev/null 2>&1 || true rm -f /tmp/swapfile colorEcho $GREEN " 临时 Swap 卸载完毕" fi } # 安装带 forward_proxy 插件的 Caddy installCaddy() { if [[ -f /usr/bin/caddy ]]; then local caddy_ver=$(/usr/bin/caddy version 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "") if [[ "$caddy_ver" == "v2.8.4" ]]; then colorEcho $GREEN " Caddy v2.8.4 已存在,跳过编译" return 0 else colorEcho $YELLOW " 检测到 Caddy ${caddy_ver:-未知},将重新编译至 v2.8.4" fi fi colorEcho $BLUE " 部署稳定 Go 编译环境..." local go_installed=false if [[ -f /usr/local/go/bin/go ]]; then local go_ver=$(/usr/local/go/bin/go version 2>/dev/null | grep -oE 'go[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1 || echo "") if [[ "$go_ver" == "go1.21.13" ]]; then colorEcho $GREEN " Go 1.21.13 已存在,跳过下载" go_installed=true fi fi if [[ "$go_installed" == false ]]; then rm -rf /usr/local/go local go_arch=$(getGoArch) local go_latest="go1.21.13" # 使用 V6_PROXY 防止纯 IPv6 机器下载失败 wget -qO- "${V6_PROXY}https://go.dev/dl/${go_latest}.linux-${go_arch}.tar.gz" | tar -C /usr/local -xz colorEcho $GREEN " Go 1.21.13 安装完成" fi export PATH=$PATH:/usr/local/go/bin export GOPATH=/root/go export PATH=$PATH:$GOPATH/bin export GOPROXY=https://goproxy.cn,https://goproxy.io,direct # 解决国内拉取 Go 模块超时 local xcaddy_bin="$GOPATH/bin/xcaddy" if [[ ! -f "$xcaddy_bin" ]]; then colorEcho $BLUE " 安装 xcaddy 工具..." go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest if [[ ! -f "$xcaddy_bin" ]]; then colorEcho $RED " [错误] xcaddy 依赖安装失败,请检查网络或更换 VPS" exit 1 fi colorEcho $GREEN " xcaddy 安装完成" else colorEcho $GREEN " xcaddy 已存在,跳过安装" fi manageSwap "add" colorEcho $BLUE " 编译带 forward_proxy 插件的 Caddy (预计需要 1~3 分钟)..." if ! "$xcaddy_bin" build v2.8.4 \ --with github.com/caddyserver/forwardproxy=github.com/klzgrad/forwardproxy@naive \ --output /usr/local/bin/caddy_custom; then manageSwap "remove" colorEcho $RED " [错误] Caddy 核心编译失败" exit 1 fi manageSwap "remove" systemctl stop naiveproxy.service 2>/dev/null || true rm -f /usr/bin/caddy mv /usr/local/bin/caddy_custom /usr/bin/caddy chmod +x /usr/bin/caddy colorEcho $GREEN " Caddy (集成 NaiveProxy) 编译成功" } # 生成Xray配置 genXrayConf() { mkdir -p /etc/xray colorEcho $BLUE " 生成 Reality 密钥对..." local keypair_output keypair_output=$(/usr/bin/xray x25519 2>&1) || { colorEcho $RED " x25519 命令执行失败" exit 1 } REALITY_PRIVATE_KEY=$(echo "$keypair_output" | grep -i "Private" | head -n1 | sed 's/.*:[[:space:]]*//' || echo "") REALITY_PUBLIC_KEY=$(echo "$keypair_output" | grep -i "Password\|Public" | head -n1 | sed 's/.*:[[:space:]]*//' || echo "") if [[ -z "$REALITY_PRIVATE_KEY" || -z "$REALITY_PUBLIC_KEY" ]]; then colorEcho $RED " 密钥解析失败,Xray 版本可能不兼容" echo "$keypair_output" exit 1 fi echo "$REALITY_PUBLIC_KEY" > /etc/xray/public_key.txt chmod 644 /etc/xray/public_key.txt local network="tcp" local xhttp_settings="{}" if [[ "$REALITY_MODE" == "xhttp" ]]; then network="xhttp" xhttp_settings="{\"path\": \"$XHTTP_PATH\", \"mode\": \"auto\"}" fi local sn_array=$(jq -Rc 'split(",") | map(gsub("^[ \t]+|[ \t]+$";"")) | map(select(length>0))' <<< "$REALITY_SERVER_NAMES") local sid_array=$(jq -Rc 'split(",") | map(gsub("^[ \t]+|[ \t]+$";"")) | map(select(length>0))' <<< "$REALITY_SHORT_IDS") local client_obj="{\"id\": \"$UUID\", \"level\": 0" if [[ -n "$FLOW" ]]; then client_obj="${client_obj}, \"flow\": \"$FLOW\"" fi client_obj="${client_obj}}" local new_inbound=$(jq -n \ --arg port "$PORT" \ --arg protocol "vless" \ --arg network "$network" \ --arg dest "$REALITY_DEST" \ --arg privateKey "$REALITY_PRIVATE_KEY" \ --argjson serverNames "$sn_array" \ --argjson shortIds "$sid_array" \ --argjson client "$client_obj" \ --argjson xhttpSettings "$xhttp_settings" \ '{ port: ($port|tonumber), protocol: $protocol, settings: { clients: [$client], decryption: "none" }, streamSettings: { network: $network, security: "reality", realitySettings: { show: false, dest: $dest, serverNames: $serverNames, privateKey: $privateKey, shortIds: $shortIds } } }') if [[ "$REALITY_MODE" == "xhttp" ]]; then new_inbound=$(echo "$new_inbound" | jq --argjson xs "$xhttp_settings" '.streamSettings.xhttpSettings = $xs') fi local existing="[]" if [[ -f "$XRAY_CONFIG" ]]; then existing=$(jq -c '.inbounds //[]' "$XRAY_CONFIG" 2>/dev/null) || existing="[]" fi local merged_inbounds=$(echo "$existing" | jq -c --argjson new "$new_inbound" --arg port "$PORT" 'map(select(.port != ($port|tonumber))) +[$new]') # 将日志级别降为 error,减少高并发下的磁盘 I/O 卡顿 jq -n --argjson inbounds "$merged_inbounds" '{ log: {loglevel: "error"}, inbounds: $inbounds, outbounds:[{protocol: "freedom", settings: {}}] }' > "$XRAY_CONFIG" } # 生成 NaiveProxy Caddyfile 配置 genNaiveConf() { mkdir -p /etc/naiveproxy # 提取公钥进行 MD5 对比,完美兼容 ECC 与 RSA 证书 colorEcho $BLUE " 正在验证证书与私钥是否匹配..." local cert_mod key_mod cert_mod=$(openssl x509 -in "$CERT_FILE" -noout -pubkey 2>/dev/null | openssl md5 | awk '{print $NF}' || echo "error") key_mod=$(openssl pkey -in "$KEY_FILE" -pubout 2>/dev/null | openssl md5 | awk '{print $NF}' || echo "error") if [[ "$cert_mod" == "error" || "$key_mod" == "error" ]]; then colorEcho $YELLOW " [警告] 无法验证密钥对(非标准格式或 openssl 缺失),将尝试继续..." elif [[ "$cert_mod" != "$key_mod" ]]; then colorEcho $RED " [致命错误] 证书公钥与私钥不匹配!" colorEcho $YELLOW " 请使用配套的 .pem/.crt 和 .key 文件" exit 1 else colorEcho $GREEN " [√] 证书与私钥匹配验证通过" fi local safe_domain=$(echo "$DOMAIN" | tr -cd 'a-zA-Z0-9.-') local safe_user=$(echo "$NAIVE_USER" | tr -cd 'a-zA-Z0-9_=@.-') local safe_pass=$(echo "$NAIVE_PASS" | tr -cd 'a-zA-Z0-9_=@.!*~-') if [[ -z "$safe_domain" || -z "$safe_user" || -z "$safe_pass" ]]; then colorEcho $RED " 参数验证失败"; exit 1 fi cat > /etc/naiveproxy/Caddyfile <<CEOF { admin off order forward_proxy before file_server } https://${safe_domain}:${PORT} { tls "${CERT_FILE}" "${KEY_FILE}" forward_proxy { basic_auth ${safe_user} ${safe_pass} hide_ip hide_via probe_resistance secret.localhost } } CEOF cat > "$NAIVE_SERVICE" <<'SEOF' [Unit] Description=NaiveProxy Service After=network.target [Service] Type=simple User=root ExecStart=/usr/bin/caddy run --config /etc/naiveproxy/Caddyfile --adapter caddyfile Restart=on-failure RestartSec=3 LimitNOFILE=65536 [Install] WantedBy=multi-user.target SEOF systemctl daemon-reload systemctl enable naiveproxy.service } # 启动服务 startSvc() { colorEcho $BLUE " 正在启动并验证服务状态..." if [[ "$PROTOCOL_TYPE" == "vless" ]]; then systemctl restart xray.service local retries=5 for ((i=1; i<=retries; i++)); do sleep 2 if systemctl is-active --quiet xray.service; then colorEcho $GREEN " Xray 启动成功并在后台运行中" return 0 fi colorEcho $BLUE " 等待启动中... ($i/$retries)" done colorEcho $RED " [致命错误] Xray 启动超时,请运行 journalctl -u xray.service -n 50 查看日志" return 1 else systemctl restart naiveproxy.service local retries=5 for ((i=1; i<=retries; i++)); do sleep 2 if systemctl is-active --quiet naiveproxy.service; then colorEcho $GREEN " NaiveProxy 启动成功并在后台运行中" return 0 fi colorEcho $BLUE " 等待启动中... ($i/$retries)" done colorEcho $RED " [致命错误] NaiveProxy 启动超时,请运行 journalctl -u naiveproxy.service -n 50 查看日志" return 1 fi } # 辅助记录节点 getNextNodeIndex() { local node_type="$1" local config_dir="/etc/xray/nodes" local max_index=0 if [[ ! -d "$config_dir" ]]; then echo 1 return fi for f in "$config_dir"/${node_type}_node*.conf; do [[ -f "$f" ]] || continue local idx idx=$(echo "$f" | grep -o 'node[0-9]*' | grep -o '[0-9]*' || echo "0") if [[ "$idx" =~ ^[0-9]+$ ]] && [[ "$idx" -gt "$max_index" ]]; then max_index=$idx fi done echo $((max_index + 1)) } saveNodeConfig() { local node_type="$1" local config_dir="/etc/xray/nodes" mkdir -p "$config_dir" && chmod 700 "$config_dir" local node_index=$(getNextNodeIndex "$node_type") if [[ "$node_type" == "vless" ]]; then cat > "$config_dir/vless_node${node_index}.conf" <<EOF NODE_ALIAS="${NODE_ALIAS}" PORT="${PORT}" UUID="${UUID}" FLOW="${FLOW}" REALITY_MODE="${REALITY_MODE}" XHTTP_PATH="${XHTTP_PATH}" REALITY_PUBLIC_KEY="${REALITY_PUBLIC_KEY}" REALITY_PRIVATE_KEY="${REALITY_PRIVATE_KEY}" REALITY_SERVER_NAMES="${REALITY_SERVER_NAMES}" REALITY_SHORT_IDS="${REALITY_SHORT_IDS}" REALITY_DEST="${REALITY_DEST}" IP="${IP}" NODE_TYPE=vless NODE_INDEX=${node_index} EOF chmod 600 "$config_dir/vless_node${node_index}.conf" elif [[ "$node_type" == "naive" ]]; then cat > "$config_dir/naive_node${node_index}.conf" <<EOF NODE_ALIAS="${NODE_ALIAS}" DOMAIN="${DOMAIN}" PORT="${PORT}" NAIVE_USER="${NAIVE_USER}" NAIVE_PASS="${NAIVE_PASS}" CERT_FILE="${CERT_FILE}" KEY_FILE="${KEY_FILE}" NODE_TYPE=naive NODE_INDEX=${node_index} EOF chmod 600 "$config_dir/naive_node${node_index}.conf" fi } showVLESSInstall() { echo " =======================================" colorEcho $GREEN " VLESS + Reality 配置 - ${NODE_ALIAS}" echo " =======================================" echo " 地址: $IP | 端口: $PORT" echo " UUID: $UUID" if [[ "$REALITY_MODE" == "xhttp" ]]; then echo " 模式: XHTTP | 路径: $XHTTP_PATH" else echo " 模式: Vision | 流控: ${FLOW}" fi echo " 公钥: $REALITY_PUBLIC_KEY" echo " SNI: $REALITY_SERVER_NAMES" echo "" local link="vless://$UUID@$IP:$PORT?encryption=none&security=reality" if [[ "$REALITY_MODE" == "xhttp" ]]; then link="${link}&type=xhttp&path=$(url_encode "$XHTTP_PATH")" else link="${link}&type=tcp&flow=$(url_encode "${FLOW}")" fi link="${link}&sni=$(echo $REALITY_SERVER_NAMES | cut -d',' -f1)&pbk=$REALITY_PUBLIC_KEY&sid=$(echo $REALITY_SHORT_IDS | cut -d',' -f1)#$(url_encode "$NODE_ALIAS")" colorEcho $BLUE " 客户端分享链接 (复制导入):" echo "$link" echo "" } showNaiveInstall() { echo " =======================================" colorEcho $GREEN " NaiveProxy + TLS 配置 - ${NODE_ALIAS}" echo " =======================================" echo " 域名: $DOMAIN | 端口: $PORT" echo " 用户: $NAIVE_USER | 密码: $NAIVE_PASS" echo "" colorEcho $BLUE " 客户端分享链接 (复制导入):" echo "naive+https://$(url_encode "$NAIVE_USER"):$(url_encode "$NAIVE_PASS")@${DOMAIN}:${PORT}#$(url_encode "$NODE_ALIAS")" echo "" } showAllConfigs() { colorEcho $BLUE "正在获取本地节点记录..." local found=false for conf in /etc/xray/nodes/vless_node*.conf; do [[ -f "$conf" ]] || continue found=true safe_read_conf "$conf" showVLESSInstall done for conf in /etc/xray/nodes/naive_node*.conf; do [[ -f "$conf" ]] || continue found=true safe_read_conf "$conf" showNaiveInstall done if [[ "$found" == false ]]; then colorEcho $YELLOW " 未找到任何节点配置" fi } install() { if [[ "$PROTOCOL_TYPE" == "vless" ]]; then getDataVLESS setFirewall installXray genXrayConf if startSvc; then showVLESSInstall saveNodeConfig "vless" fi else getDataNaive || { colorEcho $YELLOW " 操作已取消"; return; } setFirewall installCaddy genNaiveConf if startSvc; then showNaiveInstall saveNodeConfig "naive" fi fi } uninstall() { read -p " 确认卸载所有组件(包括节点配置)?[y/n]: " answer if [[ "${answer,,}" != "y" ]]; then return fi systemctl disable --now xray.service naiveproxy.service 2>/dev/null || true rm -f /usr/bin/xray /usr/bin/caddy /usr/bin/geoip.dat /usr/bin/geosite.dat rm -f /etc/systemd/system/xray.service /etc/systemd/system/naiveproxy.service rm -f "$XRAY_CONFIG" rm -rf /etc/naiveproxy /var/log/xray /etc/xray/nodes rm -f /etc/xray/public_key.txt systemctl daemon-reload colorEcho $GREEN " 卸载清理完成" colorEcho $BLUE " 所有组件和配置已彻底清除" } menu() { while true; do clear echo "##################################################################" echo -e "# ${GREEN}程序员歌${PLAIN} #" echo -e "# ${GREEN}酒醒只在屏前坐,酒醉还是屏下眠。${PLAIN} #" echo -e "# ${GREEN}梦里只见她依旧,回首自是单身狗。${PLAIN} #" echo -e "# ${GREEN}相思只能出粘液,相见才能起泡沫。${PLAIN} #" echo -e "# ${GREEN}但见满街漂亮妹,哪个归得程序员。${PLAIN} #" echo "##################################################################" echo "" echo " =======================================" echo -e " ${GREEN}最稳三剑客${PLAIN}" echo -e " ${GREEN}VLESS + Reality + Vision${PLAIN}" echo -e " ${GREEN}VLESS + Reality + XHTTP${PLAIN}" echo -e " ${GREEN}NaiveProxy + TLS${PLAIN}" echo " =======================================" echo -e " ${GREEN}1.${PLAIN} 安装 VLESS + Reality (XHTTP/Vision)" echo -e " ${GREEN}2.${PLAIN} 安装 NaiveProxy + TLS" echo " ---------------------------------------" echo -e " ${GREEN}3.${PLAIN} 卸载全部" echo -e " ${GREEN}4.${PLAIN} 重启服务" echo -e " ${GREEN}5.${PLAIN} 查看所有节点配置" echo " ---------------------------------------" echo -e " ${GREEN}00.${PLAIN} 退出脚本" echo "" read -p " 请选择你的操作 [1-5, 00退出]: " choice case "$choice" in 1) PROTOCOL_TYPE="vless"; install read -p " 操作完成,是否返回主菜单?[y/n 默认y]: " _back if [[ "${_back,,}" != "n" ]]; then continue; else exit 0; fi ;; 2) PROTOCOL_TYPE="naive"; install read -n1 -s -p " 按任意键返回主菜单..." ;; 3) uninstall read -p " 操作完成,是否返回主菜单?[y/n 默认y]: " _back if [[ "${_back,,}" != "n" ]]; then continue; else exit 0; fi ;; 4) systemctl restart xray.service naiveproxy.service 2>/dev/null || colorEcho $YELLOW " 部分服务不存在" colorEcho $GREEN " 指令已下发,所有在线服务已重启" ;; 5) showAllConfigs ;; 00) exit 0 ;; *) colorEcho $RED " 无效选择,请重新输入" ;; esac echo "" read -n1 -s -p " 按任意键继续..." done } # --- 脚本入口 --- checkSystem if ! getIP; then colorEcho $RED "[致命错误] 无法获取服务器 IP,请检查网络连接" exit 1 fi installBaseDeps enableBBR menu ```
↑
💬 评论