#!/usr/bin/env bash
# ==============================================================================
# Sing-Box (>=1.14) + Caddy + VLESS (H2/gRPC/Reality) + Hysteria 2 全自动加固部署脚本
# 系统支持: Debian 11+/12+ 或 Ubuntu 20.04+/22.04+/24.04+
# 安全加固版:
# 1. 凭据隔离: H2, gRPC, Reality 分别使用完全独立独立的 UUID
# 2. 证书联动: Hy2 自动挂载 Caddy 申请的正规 Let's Encrypt / ZeroSSL 证书 (拒绝 insecure=1)
# 3. 内核优化: 自动开启 TCP BBR 与 fq 拥塞控制
# 4. 证书健康巡检: 包含证书就绪轮询机制与自动兜底
# ==============================================================================
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
PLAIN='\033[0m'
function info() { echo -e "${GREEN}[INFO] $1${PLAIN}"; }
function warn() { echo -e "${YELLOW}[WARN] $1${PLAIN}"; }
function err() { echo -e "${RED}[ERROR] $1${PLAIN}"; exit 1; }
[[ $EUID -ne 0 ]] && err "请使用 root 权限运行此脚本!"
ARCH=$(uname -m)
case "$ARCH" in
x86_64) SB_ARCH="amd64" ;;
aarch64) SB_ARCH="arm64" ;;
armv7l) SB_ARCH="armv7" ;;
*) err "不支持的系统架构: $ARCH" ;;
esac
clear
echo "=================================================================="
echo " Sing-Box (1.14+) + Caddy 安全加固版全功能自动部署脚本 "
echo "=================================================================="
echo ""
# 域名与基础配置
read -rp "1. 请输入解析到本 VPS 的域名 (例如: node.example.com): " DOMAIN
[[ -z "$DOMAIN" ]] && err "域名不能为空!"
read -rp "2. 请输入 H2 反代伪装路径 (默认回车: /rayh2): " H2_PATH
H2_PATH=${H2_PATH:-"/rayh2"}
[[ "$H2_PATH" != /* ]] && H2_PATH="/$H2_PATH"
read -rp "3. 请输入 gRPC 服务名 (默认回车: vless-grpc): " GRPC_SERVICE
GRPC_SERVICE=${GRPC_SERVICE:-"vless-grpc"}
# Reality 配置
echo ""
echo "------------------- Reality 节点参数 -------------------"
read -rp "4. 请输入 Reality 监听端口 (默认回车: 8443): " REALITY_PORT
REALITY_PORT=${REALITY_PORT:-8443}
read -rp "5. 请输入 Reality 伪装域名/SNI (默认回车: addons.mozilla.org): " REALITY_DEST_DOMAIN
REALITY_DEST_DOMAIN=${REALITY_DEST_DOMAIN:-"addons.mozilla.org"}
# Hysteria 2 配置
echo ""
echo "------------------- Hysteria 2 节点参数 -------------------"
read -rp "6. 请输入 Hysteria 2 监听端口 (UDP, 默认回车: 8444): " HY2_PORT
HY2_PORT=${HY2_PORT:-8444}
# 获取 VPS 公网 IP
SERVER_IP=$(curl -s4m 8 ip.sb || curl -s4m 8 ifconfig.me || curl -s4m 8 api.ipify.org || echo "")
if [[ -z "$SERVER_IP" ]]; then
read -rp "未能自动获取公网 IPv4,请输入本机外网 IP: " SERVER_IP
fi
# 1. 内核优化:开启 BBR
info "优化系统内核网络参数 (开启 BBR & FQ)..."
if ! grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf; then
echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
fi
if ! grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
fi
sysctl -p >/dev/null 2>&1 || true
# 2. 安装基础依赖
info "更新源并安装依赖 (curl, jq, tar, openssl, socat)..."
apt-get update -y && apt-get install -y curl jq tar openssl socat debian-keyring debian-archive-keyring apt-transport-https
# 3. 安装 Caddy
info "安装官方最新 Caddy..."
curl -1sLF 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor --yes -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLF 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt-get update -y && apt-get install -y caddy
# 4. 下载安装 Sing-Box (>=1.14)
info "获取 Sing-Box 最新版本..."
LATEST_TAG=$(curl -s https://api.github.com/repos/SagerNet/sing-box/releases/latest | jq -r .tag_name)
[[ -z "$LATEST_TAG" || "$LATEST_TAG" == "null" ]] && LATEST_TAG="v1.14.0"
info "下载并安装 Sing-Box $LATEST_TAG ($SB_ARCH)..."
SB_VER=${LATEST_TAG#v}
DOWNLOAD_URL="https://github.com/SagerNet/sing-box/releases/download/${LATEST_TAG}/sing-box-${SB_VER}-linux-${SB_ARCH}.tar.gz"
curl -Lo /tmp/sing-box.tar.gz "$DOWNLOAD_URL"
tar -xzf /tmp/sing-box.tar.gz -C /tmp
mv /tmp/sing-box-${SB_VER}-linux-${SB_ARCH}/sing-box /usr/local/bin/sing-box
chmod +x /usr/local/bin/sing-box
rm -rf /tmp/sing-box*
# 5. 安全加固:凭据相互隔离
info "安全加固: 生成各协议完全隔离的凭证与密钥..."
UUID_H2=$(cat /proc/sys/kernel/random/uuid)
UUID_GRPC=$(cat /proc/sys/kernel/random/uuid)
UUID_REALITY=$(cat /proc/sys/kernel/random/uuid)
HY2_PASSWORD=$(openssl rand -base64 20 | tr -dc 'a-zA-Z0-9')
H2_PORT=10001
GRPC_PORT=10002
KEY_PAIR=$(/usr/local/bin/sing-box generate reality-keypair)
REALITY_PRIVATE_KEY=$(echo "$KEY_PAIR" | grep -i "PrivateKey:" | awk '{print $2}')
REALITY_PUBLIC_KEY=$(echo "$KEY_PAIR" | grep -i "PublicKey:" | awk '{print $2}')
REALITY_SHORT_ID=$(openssl rand -hex 8)
# 6. 部署伪装网页
info "部署伪装静态网站..."
mkdir -p /var/www/html
cat << 'EOF' > /var/www/html/index.html
Edge Acceleration & Cloud Services
Cloud Delivery Gateway
This edge cluster provides global asset distribution, caching optimizations, and routing infrastructure.
● All Systems Operational
EOF
# 7. 配置 Caddyfile 并启动以申请正式 SSL 证书
info "配置 Caddy 并申请正式 HTTPS 证书..."
cat << EOF > /etc/caddy/Caddyfile
$DOMAIN {
root * /var/www/html
file_server
@grpc {
header Content-Type application/grpc*
path /$GRPC_SERVICE/*
}
reverse_proxy @grpc 127.0.0.1:$GRPC_PORT {
transport http {
versions h2c 2
}
}
@h2 {
path $H2_PATH
}
reverse_proxy @h2 127.0.0.1:$H2_PORT {
transport http {
versions h2c 2
}
}
}
EOF
systemctl daemon-reload
systemctl enable --now caddy
systemctl restart caddy
CERT_DIR="/etc/sing-box/cert"
mkdir -p "$CERT_DIR"
info "正在验证 Caddy 证书申请状态..."
# 等待 Caddy 申请证书 (最多等待 25 秒)
CADDY_CERT_FOUND=false
for i in {1..25}; do
CERT_FILE=$(find /var/lib/caddy/.local/share/caddy/certificates -type f -name "$DOMAIN.crt" 2>/dev/null | head -n 1)
KEY_FILE=$(find /var/lib/caddy/.local/share/caddy/certificates -type f -name "$DOMAIN.key" 2>/dev/null | head -n 1)
if [[ -n "$CERT_FILE" && -n "$KEY_FILE" && -s "$CERT_FILE" && -s "$KEY_FILE" ]]; then
CADDY_CERT_FOUND=true
break
fi
sleep 1
done
if [[ "$CADDY_CERT_FOUND" == "true" ]]; then
info "成功捕获 Caddy 正规正式 SSL 证书!同步至 Sing-Box..."
cp -L "$CERT_FILE" "$CERT_DIR/server.crt"
cp -L "$KEY_FILE" "$CERT_DIR/server.key"
chmod 644 "$CERT_DIR/server.crt"
chmod 600 "$CERT_DIR/server.key"
HY2_INSECURE_PARAM=""
HY2_INSECURE_NOTE="启用严格校验证书 (抗中间人攻击)"
else
warn "未能在限定时间内直接获取 Caddy 签发证书 (可能 ACME 速率限制或 DNS 解析刚生效)。"
info "临时启用高质量 ECC 自签名证书兜底,可在证书生效后随时执行同步..."
openssl req -x509 -nodes -newkey ec:<(openssl ecparam -name prime256v1) \
-keyout "$CERT_DIR/server.key" \
-out "$CERT_DIR/server.crt" \
-days 3650 \
-subj "/CN=$DOMAIN" \
-addext "subjectAltName=DNS:$DOMAIN" >/dev/null 2>&1
chmod 644 "$CERT_DIR/server.crt"
chmod 600 "$CERT_DIR/server.key"
HY2_INSECURE_PARAM="&insecure=1"
HY2_INSECURE_NOTE="当前使用备用证书 (insecure=1)"
fi
# 8. 生成 Sing-Box 配置文件 (隔离各用户凭据)
info "生成 Sing-Box 配置文件..."
mkdir -p /etc/sing-box
cat << EOF > /etc/sing-box/config.json
{
"log": {
"level": "warn",
"timestamp": true
},
"inbounds": [
{
"type": "vless",
"tag": "vless-h2-in",
"listen": "127.0.0.1",
"listen_port": $H2_PORT,
"users": [
{
"uuid": "$UUID_H2",
"flow": ""
}
],
"transport": {
"type": "http",
"host": [
"$DOMAIN"
],
"path": "$H2_PATH"
}
},
{
"type": "vless",
"tag": "vless-grpc-in",
"listen": "127.0.0.1",
"listen_port": $GRPC_PORT,
"users": [
{
"uuid": "$UUID_GRPC",
"flow": ""
}
],
"transport": {
"type": "grpc",
"service_name": "$GRPC_SERVICE"
}
},
{
"type": "vless",
"tag": "vless-reality-in",
"listen": "::",
"listen_port": $REALITY_PORT,
"users": [
{
"uuid": "$UUID_REALITY",
"flow": "xtls-rprx-vision"
}
],
"tls": {
"enabled": true,
"server_name": "$REALITY_DEST_DOMAIN",
"reality": {
"enabled": true,
"handshake": {
"server": "$REALITY_DEST_DOMAIN",
"server_port": 443
},
"private_key": "$REALITY_PRIVATE_KEY",
"short_id": [
"$REALITY_SHORT_ID"
]
}
}
},
{
"type": "hysteria2",
"tag": "hy2-in",
"listen": "::",
"listen_port": $HY2_PORT,
"users": [
{
"password": "$HY2_PASSWORD"
}
],
"ignore_client_bandwidth": false,
"tls": {
"enabled": true,
"server_name": "$DOMAIN",
"certificate_path": "$CERT_DIR/server.crt",
"key_path": "$CERT_DIR/server.key"
}
}
],
"outbounds": [
{
"type": "direct",
"tag": "direct"
}
]
}
EOF
# 9. 创建守护进程并启动
info "创建 systemd 服务守护..."
cat << EOF > /etc/systemd/system/sing-box.service
[Unit]
Description=sing-box service
Documentation=https://sing-box.sagernet.org
After=network.target nss-lookup.target
[Service]
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
ExecStart=/usr/local/bin/sing-box run -c /etc/sing-box/config.json
Restart=on-failure
RestartSec=10s
LimitNOFILE=infinity
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now sing-box
systemctl restart sing-box
# 构建独立链接
LINK_HY2="hysteria2://${HY2_PASSWORD}@${DOMAIN}:${HY2_PORT}/?sni=${DOMAIN}${HY2_INSECURE_PARAM}#Hy2-${DOMAIN}"
LINK_REALITY="vless://${UUID_REALITY}@${SERVER_IP}:${REALITY_PORT}?encryption=none&flow=xtls-rprx-vision&security=reality&sni=${REALITY_DEST_DOMAIN}&fp=chrome&pbk=${REALITY_PUBLIC_KEY}&sid=${REALITY_SHORT_ID}&type=tcp#Reality-${REALITY_DEST_DOMAIN}"
LINK_H2="vless://${UUID_H2}@${DOMAIN}:443?encryption=none&security=tls&sni=${DOMAIN}&type=http&path=$(echo -n "$H2_PATH" | jq -sRr @uri)&host=${DOMAIN}#${DOMAIN}-H2"
LINK_GRPC="vless://${UUID_GRPC}@${DOMAIN}:443?encryption=none&security=tls&sni=${DOMAIN}&type=grpc&serviceName=${GRPC_SERVICE}#${DOMAIN}-gRPC"
# 保存并输出配置信息
INFO_FILE="/etc/sing-box/node_info.txt"
cat << EOF > "$INFO_FILE"
==================================================================
Sing-Box 节点配置与连接信息 (安全加固版)
==================================================================
主域名 (SNI): $DOMAIN
VPS 公网 IP: $SERVER_IP
H2 UUID (独立): $UUID_H2
gRPC UUID (独立): $UUID_GRPC
Reality UUID (独立):$UUID_REALITY
Hy2 强密码: $HY2_PASSWORD
Hy2 证书状态: $HY2_INSECURE_NOTE
Hy2 端口 (UDP): $HY2_PORT
Reality 端口 (TCP): $REALITY_PORT
Reality 伪装 SNI: $REALITY_DEST_DOMAIN
Reality 公钥: $REALITY_PUBLIC_KEY
Reality Short ID: $REALITY_SHORT_ID
------------------------------------------------------------------
[1] VLESS + Reality 节点 (主力推荐 / 直连低延迟):
$LINK_REALITY
[2] Hysteria 2 节点 (UDP加速 / 晚高峰与拥塞优选):
$LINK_HY2
[3] VLESS + H2 节点 (Caddy TLS 443 伪装反代):
$LINK_H2
[4] VLESS + gRPC 节点 (Caddy TLS 443 伪装反代):
$LINK_GRPC
==================================================================
EOF
clear
cat "$INFO_FILE"
echo ""
echo -e "${GREEN}配置已保存至文件: ${INFO_FILE}${PLAIN}"
echo "日常查看节点信息命令: cat ${INFO_FILE}"