#!/bin/bash if [ -z "$BASH_VERSION" ]; then exec bash "$0" "$@" fi set -e # 终端色彩与美化输出定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' NC='\033[0m' BOLD='\033[1m' log_title() { echo -e "\n${PURPLE}${BOLD}==========================================${NC}"; echo -e " ${CYAN}${BOLD}$1${NC}"; echo -e "${PURPLE}${BOLD}==========================================${NC}"; } log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_succ() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_err() { echo -e "${RED}[ERROR]${NC} $1"; } CORRECT_KEY="urTMjh522lnFkJwOt7anJMvhwdp0KQhk" PROVIDED_KEY="" # 1. 密钥输入与校验 if [ -z "$PROVIDED_KEY" ]; then echo -e -n "${YELLOW}请输入授权 Key: ${NC}" read -s PROVIDED_KEY echo "" fi if [ "$PROVIDED_KEY" != "$CORRECT_KEY" ]; then log_err "密钥无效,拒绝安装!" exit 1 fi log_title "密钥校验成功!开始配置系统与自动化部署" # 2. 自动强行校准系统时间 log_info "[1/7] 检查并强行校准系统时间..." sudo systemctl restart systemd-timesyncd 2>/dev/null || true HTTP_DATE=$(curl -sI --connect-timeout 5 https://www.baidu.com | grep -i '^date:' | cut -d' ' -f2- | tr -d '\r') if [ -n "$HTTP_DATE" ]; then sudo date -s "$HTTP_DATE" >/dev/null 2>&1 || true log_succ "系统时间已自动强行同步为: $(date)" fi # 3. 网络偏好设置:强行优先 IPv4 log_info "[2/7] 优化网络首选项 (强行优先 IPv4)..." if [ -f /etc/gai.conf ]; then sudo sed -i 's/#precedence ::ffff:0:0\/96 100/precedence ::ffff:0:0\/96 100/g' /etc/gai.conf 2>/dev/null || true else echo "precedence ::ffff:0:0/96 100" | sudo tee /etc/gai.conf > /dev/null fi # 4. 自动解锁并终止后台 APT 占用 log_info "[3/7] 检查并解除 apt/dpkg 后台占用..." sudo systemctl stop unattended-upgrades.service apt-daily.service apt-daily-upgrade.service 2>/dev/null || true while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 || fuser /var/lib/dpkg/lock >/dev/null 2>&1 || fuser /var/lib/apt/lists/lock >/dev/null 2>&1; do log_warn "后台自动更新进程占用中,等待 3 秒..." sleep 3 done # 5. 更新系统基础环境与辅助工具 log_info "[4/7] 更新系统基础软件包与辅助依赖..." export DEBIAN_FRONTEND=noninteractive sudo apt-get update -y && sudo apt-get install -y curl wget git unzip jq ca-certificates gnupg lsb-release sshpass net-tools # 6. 安装与配置 Docker log_info "[5/7] 配置 Docker 容器引擎与镜像加速..." if ! command -v docker &> /dev/null; then log_info "正在通过阿里云镜像源安装 Docker..." sudo mkdir -p /etc/apt/keyrings curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg --yes 2>/dev/null || true echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update -y && sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin || true if ! command -v docker &> /dev/null; then curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun || true fi sudo systemctl enable docker || true sudo systemctl start docker || true sudo usermod -aG docker $USER || true else log_succ "Docker 已存在,跳过基础安装。" fi sudo mkdir -p /etc/docker cat < /dev/null { "registry-mirrors": [ "https://docker.1ms.run", "https://docker.aipub.pro", "https://docker.hpcloud.cloud" ] } EOF sudo systemctl daemon-reload sudo systemctl restart docker if ! docker compose version &> /dev/null; then sudo apt-get install -y docker-compose-plugin || true fi # 部署 Portainer 图形化界面 if [ ! "$(sudo docker ps -q -f name=portainer 2>/dev/null)" ]; then log_info "部署 Portainer 管理面板..." sudo docker volume create portainer_data || true sudo docker run -d \ -p 0.0.0.0:9000:9000 \ -p 0.0.0.0:9443:9443 \ --name portainer \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v portainer_data:/var/lib/docker/volumes/portainer_data/_data \ portainer/portainer-ce:latest >/dev/null log_succ "Portainer 启动成功!" fi # 7. 交互式部署 RustDesk 服务端 log_title "配置 RustDesk 服务端部署" echo -e -n "${YELLOW}请输入要创建的 RustDesk 实例数量 (默认 1): ${NC}" read INSTANCE_COUNT INSTANCE_COUNT=${INSTANCE_COUNT:-1} LOCAL_IP=$(hostname -I | awk '{print $1}') RUSTDESK_SUMMARY="" for (( i=1; i<=$INSTANCE_COUNT; i++ )); do echo -e "\n${CYAN}---------------- [配置第 $i / $INSTANCE_COUNT 个实例] ----------------${NC}" # 实例名称 echo -e -n "${YELLOW}1. 实例名称 [默认: rustdesk-server-$i]: ${NC}" read INST_NAME INST_NAME=${INST_NAME:-"rustdesk-server-$i"} # 公网域名/IP echo -e -n "${YELLOW}2. 该实例绑定的公网域名/IP [默认: $LOCAL_IP]: ${NC}" read PUBLIC_HOST PUBLIC_HOST=${PUBLIC_HOST:-"$LOCAL_IP"} # 基准端口 DEFAULT_BASE_PORT=$((21115 + (i-1)*10)) echo -e -n "${YELLOW}3. 起始端口 offset (hbbs 默认 21115, 当前推荐: $DEFAULT_BASE_PORT): ${NC}" read BASE_PORT BASE_PORT=${BASE_PORT:-$DEFAULT_BASE_PORT} P_21115=$BASE_PORT P_21116=$((BASE_PORT + 1)) P_21117=$((BASE_PORT + 2)) P_21118=$((BASE_PORT + 3)) P_21119=$((BASE_PORT + 4)) # 自定义 Key echo -e -n "${YELLOW}4. 自定义公钥/Key (直接回车将自动生成强密钥): ${NC}" read CUSTOM_KEY DATA_DIR="/opt/rustdesk/$INST_NAME" sudo mkdir -p "$DATA_DIR" if [ -n "$CUSTOM_KEY" ]; then echo -n "$CUSTOM_KEY" | sudo tee "$DATA_DIR/id_ed25519.pub" >/dev/null fi sudo docker rm -f "${INST_NAME}-hbbs" "${INST_NAME}-hbbr" 2>/dev/null || true log_info "正在启动 ${INST_NAME} (仅 IPv4 模式)..." # 启动 hbbs sudo docker run -d \ --name "${INST_NAME}-hbbs" \ --restart=always \ -v "$DATA_DIR:/root" \ -p 0.0.0.0:${P_21115}:21115 \ -p 0.0.0.0:${P_21116}:21116 \ -p 0.0.0.0:${P_21116}:21116/udp \ -p 0.0.0.0:${P_21118}:21118 \ rustdesk/rustdesk-server:latest hbbs -r ${PUBLIC_HOST}:${P_21117} -4 >/dev/null # 启动 hbbr sudo docker run -d \ --name "${INST_NAME}-hbbr" \ --restart=always \ -v "$DATA_DIR:/root" \ -p 0.0.0.0:${P_21117}:21117 \ -p 0.0.0.0:${P_21119}:21119 \ rustdesk/rustdesk-server:latest hbbr -4 >/dev/null # 动态轮询等待密钥文件生成 (最多等待 15 秒) FINAL_KEY="" log_info "等待密钥初始化..." for retry in {1..15}; do if [ -s "$DATA_DIR/id_ed25519.pub" ]; then FINAL_KEY=$(sudo cat "$DATA_DIR/id_ed25519.pub" 2>/dev/null) break fi sleep 1 done if [ -z "$FINAL_KEY" ]; then FINAL_KEY="密钥生成较慢,请稍后前往 $DATA_DIR/id_ed25519.pub 查看" fi RUSTDESK_SUMMARY+="${BOLD}${GREEN}► 实例名称:${NC} $INST_NAME\n" RUSTDESK_SUMMARY+=" ${BOLD}ID Server:${NC} $PUBLIC_HOST:$P_21116\n" RUSTDESK_SUMMARY+=" ${BOLD}Relay Server:${NC} $PUBLIC_HOST:$P_21117\n" RUSTDESK_SUMMARY+=" ${BOLD}Public Key:${NC} $FINAL_KEY\n" RUSTDESK_SUMMARY+=" ${BOLD}端口映射 (TCP/UDP):${NC} $P_21115, $P_21116, $P_21117, $P_21118, $P_21119\n\n" # 8. OpenWrt 自动化端口转发联动 (适配 UCI 名称兼容性) echo -e -n "${YELLOW}是否自动配置主路由 OpenWrt 端口转发 (DNAT)? (y/N): ${NC}" read DO_OP if [[ "$DO_OP" =~ ^[Yy]$ ]]; then echo -e -n " 请输入 OpenWrt 路由器 IP [默认 192.168.1.1]: " read OP_IP OP_IP=${OP_IP:-"192.168.1.1"} echo -e -n " 请输入 OpenWrt root 密码: " read -s OP_PASS echo "" log_info "正在连接 OpenWrt (${OP_IP}) 并配置防火墙重定向规则..." # 将实例名称中的中划线转换成下划线,避免 OpenWrt UCI 参数合法性报错 SAFE_INST_NAME=$(echo "$INST_NAME" | tr '-' '_') sshpass -p "$OP_PASS" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 root@$OP_IP "sh -s" << EOF_OP for p in ${P_21115} ${P_21116} ${P_21117} ${P_21118} ${P_21119}; do RULE_NAME="RustDesk_${SAFE_INST_NAME}_${p}" uci delete firewall.${RULE_NAME} 2>/dev/null || true uci set firewall.${RULE_NAME}=redirect uci set firewall.${RULE_NAME}.name="${RULE_NAME}" uci set firewall.${RULE_NAME}.src='wan' uci set firewall.${RULE_NAME}.src_dport="${p}" uci set firewall.${RULE_NAME}.dest='lan' uci set firewall.${RULE_NAME}.dest_ip="${LOCAL_IP}" uci set firewall.${RULE_NAME}.dest_port="${p}" uci set firewall.${RULE_NAME}.proto='tcp udp' uci set firewall.${RULE_NAME}.target='DNAT' done uci commit firewall /etc/init.d/firewall restart >/dev/null 2>&1 EOF_OP if [ $? -eq 0 ]; then log_succ "OpenWrt 端口转发规则同步配置完成!" else log_err "连接 OpenWrt 失败,请检查密码或 SSH 权限。" fi fi done # 9. 最终汇总面板展示 log_title "安装与部署全部完成!汇总信息如下" echo -e "${BOLD}${CYAN}[1] Portainer Docker 图形管理界面:${NC}" echo -e " - HTTP 地址: http://${LOCAL_IP}:9000" echo -e " - HTTPS 地址: https://${LOCAL_IP}:9443\n" echo -e "${BOLD}${CYAN}[2] RustDesk 服务端配置明细 (可直接复制填入客户端):${NC}" echo -e "$RUSTDESK_SUMMARY" echo -e "${PURPLE}${BOLD}==========================================${NC}\n"