Files
server-deploy/vps-xray/deploy.sh
T

560 lines
14 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
# ============================================
# Xray VPS 一键部署脚本
# 支持模式:reality (VLESS-Reality) / fast (VLESS TCP 无 TLS)
# 适用于 Debian 11+ / Ubuntu 20.04+
# ============================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
log() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
step() { echo -e "\n${CYAN}========== $* ==========${NC}"; }
# ===== 使用说明 =====
usage() {
echo "用法: bash deploy.sh [选项]"
echo ""
echo "选项:"
echo " --mode reality VLESS-Reality 模式(默认,推荐,抗封锁)"
echo " --mode fast VLESS TCP 模式(无 TLS,极速但易被检测)"
echo " -h, --help 显示帮助"
exit 0
}
# ===== 解析参数 =====
MODE="reality"
while [[ $# -gt 0 ]]; do
case $1 in
--mode) MODE="$2"; shift 2 ;;
-h|--help) usage ;;
*) error "未知参数: $1"; usage ;;
esac
done
if [[ "$MODE" != "reality" && "$MODE" != "fast" ]]; then
error "无效模式: $MODE(支持 reality / fast"
exit 1
fi
# ===== 检查 root =====
if [ "$(id -u)" -ne 0 ]; then
error "请使用 root 用户运行: sudo bash deploy.sh"
exit 1
fi
# ===== 加载现有配置(如存在)=====
if [ -f .env ]; then
sed -i 's/\r$//' .env
set -a; source .env; set +a
fi
XRAY_PORT="${XRAY_PORT:-443}"
# ===== 0. 检查并安装依赖 =====
# 精简版系统镜像(如部分 VPS 商的 Debian/Ubuntu minimal)常缺 curl / unzip
ensure_deps() {
step "0/5 检查依赖"
local missing=()
local cmd
for cmd in curl unzip openssl; do
command -v "$cmd" &>/dev/null || missing+=("$cmd")
done
if [ ! -f /etc/ssl/certs/ca-certificates.crt ]; then
missing+=("ca-certificates")
fi
if [ "${#missing[@]}" -eq 0 ]; then
log "依赖齐全: curl unzip openssl ca-certificates"
return
fi
warn "缺少依赖: ${missing[*]},正在自动安装..."
if command -v apt-get &>/dev/null; then
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq "${missing[@]}"
elif command -v dnf &>/dev/null; then
dnf install -y "${missing[@]}"
elif command -v yum &>/dev/null; then
yum install -y "${missing[@]}"
elif command -v apk &>/dev/null; then
apk add --no-cache "${missing[@]}"
else
error "未识别的包管理器,请手动安装: ${missing[*]}"
exit 1
fi
for cmd in curl unzip openssl; do
if ! command -v "$cmd" &>/dev/null; then
error "依赖安装失败: ${cmd},请手动安装后重试"
exit 1
fi
done
log "依赖安装完成"
}
# ===== 1. 安装 Xray =====
install_xray() {
step "1/5 安装 Xray"
if [ -x /usr/local/bin/xray ]; then
log "Xray 已安装: $(/usr/local/bin/xray version | head -1)"
log "将更新到最新版本..."
fi
# 不用 bash <(curl ...)curl 失败时进程替换只产生空脚本,
# 外层 bash 仍返回 0,会掩盖下载失败
local installer
installer="$(mktemp)"
local url ok=0
for url in \
"https://raw.githubusercontent.com/XTLS/Xray-install/main/install-release.sh" \
"https://github.com/XTLS/Xray-install/raw/main/install-release.sh"
do
if curl -fsSL --max-time 60 "$url" -o "$installer" && [ -s "$installer" ]; then
ok=1
break
fi
warn "下载失败,尝试备用地址: $url"
done
if [ "$ok" -ne 1 ]; then
rm -f "$installer"
error "无法下载 Xray 安装脚本,请检查服务器网络 / DNS"
exit 1
fi
bash "$installer"
rm -f "$installer"
if [ ! -x /usr/local/bin/xray ]; then
error "Xray 安装失败:/usr/local/bin/xray 不存在"
exit 1
fi
log "Xray 安装完成: $(/usr/local/bin/xray version | head -1)"
}
# ===== 2. 生成密钥 =====
generate_keys() {
step "2/5 生成密钥"
XRAY_UUID="${XRAY_UUID:-$(cat /proc/sys/kernel/random/uuid)}"
log "UUID: ${XRAY_UUID}"
if [ "$MODE" = "reality" ]; then
if [ -z "${XRAY_PRIVATE_KEY:-}" ] || [ -z "${XRAY_PUBLIC_KEY:-}" ]; then
local keys
keys=$(/usr/local/bin/xray x25519 2>&1)
XRAY_PRIVATE_KEY=$(echo "$keys" | grep -i 'private' | awk -F': ' '{print $2}' | tr -d '[:space:]')
XRAY_PUBLIC_KEY=$(echo "$keys" | grep -i 'public' | awk -F': ' '{print $2}' | tr -d '[:space:]')
fi
XRAY_SHORT_ID="${XRAY_SHORT_ID:-$(openssl rand -hex 8)}"
if [ -z "$XRAY_PRIVATE_KEY" ] || [ -z "$XRAY_PUBLIC_KEY" ]; then
error "密钥生成失败!xray x25519 输出:"
echo "$keys"
exit 1
fi
log "x25519 密钥对已生成或已加载"
log "ShortId: ${XRAY_SHORT_ID}"
fi
}
# ===== 3. 选择 Reality 伪装目标 =====
select_reality_dest() {
if [ "$MODE" != "reality" ]; then
return
fi
step "3/5 选择 Reality 伪装目标"
local candidates=("www.microsoft.com" "dl.google.com" "www.apple.com" "www.amazon.com")
REALITY_DEST="${REALITY_DEST:-}"
REALITY_SNI="${REALITY_SNI:-}"
if [ -n "$REALITY_DEST" ] && [ -n "$REALITY_SNI" ]; then
log "使用 .env 中指定的伪装目标: ${REALITY_DEST}"
return
fi
local best_dest="www.microsoft.com"
local best_ms=9999
for site in "${candidates[@]}"; do
local ms
ms=$(curl -so /dev/null -w '%{time_connect}' --max-time 3 "https://${site}" 2>/dev/null \
| awk '{printf "%d", $1*1000}') || true
if [ -n "$ms" ] && [ "$ms" -lt "$best_ms" ] 2>/dev/null; then
best_ms=$ms
best_dest=$site
fi
done
REALITY_DEST="$best_dest"
REALITY_SNI="$best_dest"
log "伪装目标: ${REALITY_DEST} (延迟 ${best_ms}ms)"
}
# ===== 4. 写入 Xray 配置 =====
write_config() {
if [ "$MODE" = "reality" ]; then
write_config_reality
else
write_config_fast
fi
}
write_config_reality() {
step "3/5 写入 Xray 配置 (Reality)"
cat > /usr/local/etc/xray/config.json << EOF
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"listen": "0.0.0.0",
"port": ${XRAY_PORT},
"protocol": "vless",
"settings": {
"clients": [
{
"id": "${XRAY_UUID}",
"flow": "xtls-rprx-vision"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"show": false,
"dest": "${REALITY_DEST}:443",
"xver": 0,
"serverNames": [
"${REALITY_SNI}"
],
"privateKey": "${XRAY_PRIVATE_KEY}",
"shortIds": [
"${XRAY_SHORT_ID}"
]
}
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls", "quic"],
"routeOnly": true
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "direct"
},
{
"protocol": "blackhole",
"tag": "block"
}
],
"routing": {
"rules": [
{
"type": "field",
"outboundTag": "block",
"protocol": ["bittorrent"]
}
]
}
}
EOF
log "配置已写入: /usr/local/etc/xray/config.json"
}
write_config_fast() {
step "3/5 写入 Xray 配置 (Fast TCP)"
cat > /usr/local/etc/xray/config.json << EOF
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"listen": "0.0.0.0",
"port": ${XRAY_PORT},
"protocol": "vless",
"settings": {
"clients": [
{
"id": "${XRAY_UUID}"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "none"
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls", "quic"],
"routeOnly": true
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "direct"
},
{
"protocol": "blackhole",
"tag": "block"
}
],
"routing": {
"rules": [
{
"type": "field",
"outboundTag": "block",
"protocol": ["bittorrent"]
}
]
}
}
EOF
log "配置已写入: /usr/local/etc/xray/config.json"
}
# ===== 5. 防火墙 =====
configure_firewall() {
step "4/5 配置防火墙"
if command -v ufw &>/dev/null; then
ufw allow "${XRAY_PORT}/tcp"
ufw allow 22/tcp
ufw --force enable
log "ufw 已放行端口 ${XRAY_PORT}/tcp"
elif command -v firewall-cmd &>/dev/null; then
firewall-cmd --permanent --add-port="${XRAY_PORT}/tcp"
firewall-cmd --reload
log "firewalld 已放行端口 ${XRAY_PORT}/tcp"
else
warn "未检测到防火墙工具,请手动放行端口 ${XRAY_PORT}/tcp"
fi
}
# ===== 6. BBR + 网络调优 =====
tune_network() {
step "5/5 BBR + 网络调优"
cat > /etc/sysctl.d/99-xray-turbo.conf << 'SYSEOF'
# BBR 拥塞控制
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
# TCP 缓冲区(提升大文件 / 视频流吞吐量)
net.core.rmem_max=16777216
net.core.wmem_max=16777216
net.ipv4.tcp_rmem=4096 87380 16777216
net.ipv4.tcp_wmem=4096 65536 16777216
# 连接优化
net.ipv4.tcp_fastopen=3
net.ipv4.tcp_slow_start_after_idle=0
net.ipv4.tcp_mtu_probing=1
net.ipv4.tcp_fin_timeout=15
net.ipv4.tcp_keepalive_time=300
net.ipv4.tcp_keepalive_intvl=30
net.ipv4.tcp_keepalive_probes=5
net.ipv4.tcp_max_tw_buckets=262144
net.ipv4.tcp_max_syn_backlog=8192
net.core.somaxconn=8192
net.core.netdev_max_backlog=8192
SYSEOF
sysctl --system > /dev/null 2>&1
log "BBR 已启用,网络参数已优化"
}
# ===== 新增:系统与服务加固 =====
harden_system() {
step "加固系统与服务"
# 文件描述符上限
if ! grep -q '99-xray-nofile' /etc/security/limits.conf 2>/dev/null; then
cat >> /etc/security/limits.conf << 'EOF'
# 99-xray-nofile
* soft nofile 1000000
* hard nofile 1000000
root soft nofile 1000000
root hard nofile 1000000
EOF
fi
# systemd 服务加固
mkdir -p /etc/systemd/system/xray.service.d
cat > /etc/systemd/system/xray.service.d/override.conf << 'EOF'
[Service]
LimitNOFILE=1000000
Restart=always
RestartSec=3
EOF
# 定时重启 timer(每日凌晨 4 点)
cat > /etc/systemd/system/xray-restart.service << 'EOF'
[Unit]
Description=Daily restart of Xray
After=network.target
[Service]
Type=oneshot
ExecStart=/bin/systemctl restart xray
EOF
cat > /etc/systemd/system/xray-restart.timer << 'EOF'
[Unit]
Description=Daily restart Xray at 04:00 CST
[Timer]
OnCalendar=*-*-* 04:00:00
TimeZone=Asia/Shanghai
Persistent=true
[Install]
WantedBy=timers.target
EOF
}
# ===== 7. 启动服务 =====
start_service() {
systemctl daemon-reload
systemctl restart xray
systemctl enable xray
systemctl enable --now xray-restart.timer
sleep 2
if systemctl is-active --quiet xray; then
log "Xray 服务已启动 ✅"
else
error "Xray 启动失败,请检查: journalctl -u xray -f"
exit 1
fi
}
# ===== 8. 保存配置到 .env =====
save_env() {
cat > "$SCRIPT_DIR/.env" << EOF
# Xray 部署配置(由 deploy.sh 自动生成)
# 生成时间: $(date '+%Y-%m-%d %H:%M:%S')
XRAY_MODE=${MODE}
XRAY_PORT=${XRAY_PORT}
XRAY_UUID=${XRAY_UUID}
EOF
if [ "$MODE" = "reality" ]; then
cat >> "$SCRIPT_DIR/.env" << EOF
XRAY_PRIVATE_KEY=${XRAY_PRIVATE_KEY}
XRAY_PUBLIC_KEY=${XRAY_PUBLIC_KEY}
XRAY_SHORT_ID=${XRAY_SHORT_ID}
REALITY_DEST=${REALITY_DEST}
REALITY_SNI=${REALITY_SNI}
EOF
fi
chmod 600 "$SCRIPT_DIR/.env"
log "配置已保存到 .env(权限 600"
}
# ===== 9. 输出连接信息 =====
print_result() {
local server_ip
server_ip=$(curl -s --max-time 5 ipv4.ip.sb || curl -s --max-time 5 ifconfig.me || echo "获取失败")
echo ""
echo "=========================================================="
if [ "$MODE" = "reality" ]; then
echo " Xray VLESS-Reality 部署完成 ✅"
echo "=========================================================="
echo ""
echo " IP : ${server_ip}"
echo " 端口 : ${XRAY_PORT}"
echo " 协议 : VLESS"
echo " UUID : ${XRAY_UUID}"
echo " 流控 : xtls-rprx-vision"
echo " 传输 : tcp"
echo " 安全 : reality"
echo " SNI : ${REALITY_SNI}"
echo " Fingerprint : chrome"
echo " PublicKey : ${XRAY_PUBLIC_KEY}"
echo " ShortId : ${XRAY_SHORT_ID}"
echo ""
echo "=========================================================="
echo ""
echo ">>> VLESS 分享链接(可直接导入客户端):"
echo ""
echo "vless://${XRAY_UUID}@${server_ip}:${XRAY_PORT}?encryption=none&flow=xtls-rprx-vision&security=reality&sni=${REALITY_SNI}&fp=chrome&pbk=${XRAY_PUBLIC_KEY}&sid=${XRAY_SHORT_ID}&type=tcp#vps-reality"
else
echo " Xray VLESS TCP 部署完成 ✅"
echo "=========================================================="
echo ""
echo " IP : ${server_ip}"
echo " 端口 : ${XRAY_PORT}"
echo " 协议 : VLESS"
echo " UUID : ${XRAY_UUID}"
echo " 传输 : tcp"
echo " 安全 : none"
fi
echo ""
echo "=========================================================="
echo ""
echo "⚠️ 请妥善保存以上信息!"
echo " 配置文件: /usr/local/etc/xray/config.json"
echo " 凭据备份: ${SCRIPT_DIR}/.env"
echo " 查看日志: journalctl -u xray -f"
echo " 重启服务: systemctl restart xray"
echo "=========================================================="
}
# ===== 主流程 =====
main() {
echo ""
log "部署模式: ${MODE}"
[ "$MODE" = "reality" ] && log "VLESS-Reality(推荐,抗封锁)" || log "VLESS TCP(极速,无 TLS"
echo ""
ensure_deps
install_xray
generate_keys
select_reality_dest
write_config
configure_firewall
tune_network
harden_system
start_service
save_env
print_result
}
main