#!/usr/bin/env bash
set -Eeuo pipefail
# ============================================================
# S-UI + sing-box 一键部署
# Target: Ubuntu 24.04 / 1C1G / 10G NVMe
# ============================================================
PANEL_PORT="2095"
PANEL_PATH="/app/"
SUB_PORT="2096"
SUB_PATH="/sub/"
VLESS_PORT="443"
HY2_PORT="8443"
SUI_INSTALL_URL="https://raw.githubusercontent.com/alireza0/s-ui/main/install.sh"
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
# ============================================================
# Root check
# ============================================================
if [[ "$(id -u)" -ne 0 ]]; then
echo -e "${RED}请使用 root 权限运行${NC}"
exit 1
fi
# ============================================================
# OS check
# ============================================================
if [[ -f /etc/os-release ]]; then
source /etc/os-release
else
echo -e "${RED}无法识别系统${NC}"
exit 1
fi
echo
echo "============================================================"
echo " S-UI + sing-box 一键部署"
echo "============================================================"
echo "OS: ${PRETTY_NAME:-Unknown}"
echo "ARCH: $(uname -m)"
echo
# ============================================================
# Base packages
# ============================================================
echo -e "${GREEN}[1/7] 安装基础工具...${NC}"
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y \
curl \
wget \
ca-certificates \
openssl \
jq \
ufw \
iproute2
# ============================================================
# Timezone
# ============================================================
echo -e "${GREEN}[2/7] 设置时区...${NC}"
timedatectl set-timezone Asia/Hong_Kong || true
# ============================================================
# Swap
# 1GB RAM VPS 推荐配置 1GB swap
# ============================================================
echo -e "${GREEN}[3/7] 检查 Swap...${NC}"
if [[ -z "$(swapon --show --noheadings 2>/dev/null)" ]]; then
echo "未检测到 Swap,创建 1GB /swapfile"
if [[ ! -f /swapfile ]]; then
if ! fallocate -l 1G /swapfile 2>/dev/null; then
dd if=/dev/zero of=/swapfile bs=1M count=1024 status=progress
fi
fi
chmod 600 /swapfile
if ! file /swapfile | grep -qi "swap file"; then
mkswap /swapfile
fi
swapon /swapfile
if ! grep -q '^/swapfile ' /etc/fstab; then
echo '/swapfile none swap sw 0 0' >> /etc/fstab
fi
else
echo "Swap 已存在,跳过。"
fi
# ============================================================
# Memory tuning
# ============================================================
cat >/etc/sysctl.d/99-sui-memory.conf <<'EOF'
vm.swappiness=10
vm.vfs_cache_pressure=50
EOF
sysctl --system >/dev/null 2>&1 || true
# ============================================================
# journald limit
# 10GB SSD 防止日志长期占满磁盘
# ============================================================
echo -e "${GREEN}[4/7] 设置日志限制...${NC}"
mkdir -p /etc/systemd/journald.conf.d
cat >/etc/systemd/journald.conf.d/99-sui-limit.conf <<'EOF'
[Journal]
SystemMaxUse=100M
RuntimeMaxUse=50M
MaxRetentionSec=7day
EOF
systemctl restart systemd-journald || true
# ============================================================
# BBR
# ============================================================
echo -e "${GREEN}[5/7] 配置 BBR...${NC}"
if modprobe tcp_bbr 2>/dev/null; then
cat >/etc/sysctl.d/99-bbr.conf <<'EOF'
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF
sysctl --system >/dev/null 2>&1 || true
echo "BBR:"
sysctl net.ipv4.tcp_congestion_control 2>/dev/null || true
else
echo -e "${YELLOW}当前内核无法加载 tcp_bbr,跳过。${NC}"
fi
# ============================================================
# S-UI
# ============================================================
echo -e "${GREEN}[6/7] 安装 S-UI + sing-box...${NC}"
INSTALLER="$(mktemp /tmp/sui-install.XXXXXX.sh)"
cleanup() {
rm -f "$INSTALLER"
}
trap cleanup EXIT
curl \
-fL \
--retry 3 \
--retry-delay 2 \
"$SUI_INSTALL_URL" \
-o "$INSTALLER"
chmod 700 "$INSTALLER"
# ------------------------------------------------------------
# 判断是否为首次安装
# ------------------------------------------------------------
FRESH_INSTALL=1
if [[ -f /usr/local/s-ui/db/s-ui.db ]]; then
FRESH_INSTALL=0
fi
if [[ "$FRESH_INSTALL" == "1" ]]; then
# 随机管理员账号
ADMIN_USER="admin_$(openssl rand -hex 3)"
# 32位 hex 密码
ADMIN_PASS="$(openssl rand -hex 16)"
echo
echo "首次安装 S-UI"
echo "自动配置:"
echo "Panel: ${PANEL_PORT}${PANEL_PATH}"
echo "Subscription: ${SUB_PORT}${SUB_PATH}"
echo
# 官方安装器输入顺序:
#
# 是否修改设置 y
# Panel Port
# Panel Path
# Subscription Port
# Subscription Path
# 修改管理员 y
# Username
# Password
printf "y\n%s\n%s\n%s\n%s\ny\n%s\n%s\n" \
"$PANEL_PORT" \
"$PANEL_PATH" \
"$SUB_PORT" \
"$SUB_PATH" \
"$ADMIN_USER" \
"$ADMIN_PASS" \
| SUI_LANG=zhtw bash "$INSTALLER"
else
echo
echo "检测到已有 S-UI 数据库。"
echo "执行更新/修复安装,并保留现有配置和账号。"
echo
# 不修改现有设置
printf "n\n" | SUI_LANG=zhtw bash "$INSTALLER"
fi
# ============================================================
# Start service
# ============================================================
systemctl daemon-reload
systemctl enable s-ui >/dev/null 2>&1 || true
systemctl restart s-ui
sleep 2
# ============================================================
# Firewall
#
# 不主动启用 UFW,避免 SSH 被误锁。
#
# 如果用户本来就启用了 UFW,则仅放通节点端口:
# 443 TCP = VLESS Reality
# 8443 UDP = Hysteria2
#
# 面板 2095 默认不自动开放
# ============================================================
echo -e "${GREEN}[7/7] 检查防火墙...${NC}"
if ufw status 2>/dev/null | grep -q "Status: active"; then
echo "检测到 UFW 已启用"
ufw allow "${VLESS_PORT}/tcp" >/dev/null
ufw allow "${HY2_PORT}/udp" >/dev/null
echo "已放行:"
echo " TCP ${VLESS_PORT}"
echo " UDP ${HY2_PORT}"
echo
echo "为安全起见未自动开放:"
echo " TCP ${PANEL_PORT} S-UI Panel"
echo " TCP ${SUB_PORT} Subscription"
else
echo "UFW 当前未启用。"
echo "脚本不会自动启用,以避免 SSH 被锁。"
fi
# ============================================================
# Result
# ============================================================
echo
echo
echo "============================================================"
echo -e "${GREEN} 安装完成${NC}"
echo "============================================================"
echo
echo "S-UI Service:"
systemctl is-active s-ui || true
echo
if [[ -x /usr/local/s-ui/sui ]]; then
echo "S-UI Version:"
/usr/local/s-ui/sui -v || true
fi
echo
echo "------------------------------------------------------------"
echo "面板"
echo "------------------------------------------------------------"
echo
echo "Panel Port : ${PANEL_PORT}"
echo "Panel Path : ${PANEL_PATH}"
echo
echo "Subscription Port : ${SUB_PORT}"
echo "Subscription Path : ${SUB_PATH}"
echo
if [[ "$FRESH_INSTALL" == "1" ]]; then
echo "------------------------------------------------------------"
echo -e "${YELLOW}管理员账号,请保存${NC}"
echo "------------------------------------------------------------"
echo
echo "Username : ${ADMIN_USER}"
echo "Password : ${ADMIN_PASS}"
echo
fi
# 获取公网 IPv4
PUBLIC_IPV4="$(curl -4 -fsS --max-time 5 https://api.ipify.org 2>/dev/null || true)"
if [[ -n "$PUBLIC_IPV4" ]]; then
echo "------------------------------------------------------------"
echo "Web Panel"
echo "------------------------------------------------------------"
echo
echo "http://${PUBLIC_IPV4}:${PANEL_PORT}${PANEL_PATH}"
echo
echo "Subscription Base:"
echo
echo "http://${PUBLIC_IPV4}:${SUB_PORT}${SUB_PATH}"
fi
echo
echo "------------------------------------------------------------"
echo "推荐:通过 SSH Tunnel 管理 S-UI"
echo "------------------------------------------------------------"
echo
if [[ -n "$PUBLIC_IPV4" ]]; then
echo "在你 Windows PowerShell 执行:"
echo
echo "ssh -L ${PANEL_PORT}:127.0.0.1:${PANEL_PORT} root@${PUBLIC_IPV4}"
else
echo "ssh -L ${PANEL_PORT}:127.0.0.1:${PANEL_PORT} root@你的VPS_IP"
fi
echo
echo "然后浏览器打开:"
echo
echo "http://127.0.0.1:${PANEL_PORT}${PANEL_PATH}"
echo
echo "------------------------------------------------------------"
echo "S-UI 管理命令"
echo "------------------------------------------------------------"
echo
echo "s-ui"
echo "s-ui status"
echo "s-ui restart"
echo "s-ui log"
echo "s-ui update"
echo
echo "查看监听端口:"
echo
echo "ss -lntup"
echo
echo "查看资源:"
echo
echo "free -h"
echo "df -h"
echo
echo "============================================================"
0%
0%