替换IP面板证书

#!/bin/bash

# =================配置区域=================
# 证书源目录的基础路径 (脚本会自动追加 IPv4 地址)
# 最终寻找路径例如: /etc/ssl/ip/1.2.3.4/fullchain.pem
SOURCE_BASE_DIR="/etc/ssl/ip"

# 距离到期还剩多少天时进行替换 (默认 1 天)
EXPIRY_DAYS=1

# 日志文件路径
LOG_FILE="/var/log/panel_ssl_update.log"
# =================配置结束=================

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# 日志函数
log() {
    echo -e "$(date "+%Y-%m-%d %H:%M:%S") $1" | tee -a "$LOG_FILE"
}

# 获取公网 IPv4
get_public_ip() {
    # 尝试多个接口获取 IP
    IP=$(curl -s4m 5 https://api.ipify.org || curl -s4m 5 https://ifconfig.me || curl -s4m 5 https://myip.ipip.net | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')
    if [[ -z "$IP" ]]; then
        log "${RED}[Error] 无法获取公网 IPv4 地址,请检查网络连接。${NC}"
        exit 1
    fi
    echo "$IP"
}

# 核心逻辑:检测面板并生成任务列表
detect_targets() {
    # 定义全局数组存储任务
    # 格式: "证书路径|私钥路径|重启命令|任务描述|文件权限用户"
    TARGETS=()

    # ---------------------------------------------------------
    # 1. CyberPanel 检测模块
    # ---------------------------------------------------------
    # 检测 CyberPanel 面板端口 (8090) - 这是必须存在的
    if [[ -f "/usr/local/lscp/conf/cert.pem" ]]; then
        log "${BLUE}[Info] 检测到 CyberPanel 面板服务 (Port 8090)。${NC}"
        # 添加 8090 面板任务
        TARGETS+=("/usr/local/lscp/conf/cert.pem|/usr/local/lscp/conf/key.pem|systemctl restart lscpd|CyberPanel_8090|lsadm")

        # 接下来检测 LSWS WebAdmin (Port 7080)
        # 逻辑:先找 webadmin.crt,找不到再找 admin.crt
        if [[ -f "/usr/local/lsws/admin/conf/webadmin.crt" ]]; then
            log "${BLUE}[Info] 检测到 LSWS WebAdmin (Port 7080) - 使用 webadmin.crt 路径。${NC}"
            TARGETS+=("/usr/local/lsws/admin/conf/webadmin.crt|/usr/local/lsws/admin/conf/webadmin.key|systemctl restart lsws|LSWS_WebAdmin_7080|lsadm")
        
        elif [[ -f "/usr/local/lsws/admin/conf/cert/admin.crt" ]]; then
            log "${BLUE}[Info] 检测到 LSWS WebAdmin (Port 7080) - 使用 admin.crt 路径。${NC}"
            TARGETS+=("/usr/local/lsws/admin/conf/cert/admin.crt|/usr/local/lsws/admin/conf/cert/admin.key|systemctl restart lsws|LSWS_WebAdmin_7080|lsadm")
        
        else
            log "${YELLOW}[Warn] 未找到 LSWS WebAdmin (7080) 的证书文件,跳过该端口的处理。${NC}"
        fi

    # ---------------------------------------------------------
    # 2. aaPanel/宝塔 检测模块
    # ---------------------------------------------------------
    elif [[ -f "/www/server/panel/ssl/certificate.pem" ]]; then
        log "${BLUE}[Info] 检测到 aaPanel/宝塔面板。${NC}"
        # 宝塔不需要指定特定用户(root即可),这里留空或填root
        TARGETS+=("/www/server/panel/ssl/certificate.pem|/www/server/panel/ssl/privateKey.pem|bt restart|aaPanel_Main|root")
    
    else
        log "${RED}[Error] 未检测到任何已知的面板证书路径。无法继续。${NC}"
        exit 1
    fi
}

# 检查证书状态
# 返回 0 表示需要替换,返回 1 表示不需要
check_cert_status() {
    local cert_path=$1
    
    # 文件不存在,必须替换
    if [[ ! -f "$cert_path" ]]; then return 0; fi

    local issuer=$(openssl x509 -in "$cert_path" -noout -issuer 2>/dev/null)
    
    # 1. 检查是否为 Let's Encrypt
    if [[ "$issuer" != *"Let's Encrypt"* ]]; then
        log "[Check] ${cert_path} 不是 Let's Encrypt 证书,准备替换。"
        return 0
    fi

    # 2. 检查过期时间
    local end_date_str=$(openssl x509 -in "$cert_path" -noout -enddate | cut -d= -f2)
    local end_date_epoch=$(date -d "$end_date_str" +%s)
    local current_epoch=$(date +%s)
    local threshold_seconds=$((EXPIRY_DAYS * 86400))
    local remaining_seconds=$((end_date_epoch - current_epoch))

    if [[ $remaining_seconds -lt $threshold_seconds ]]; then
        log "[Check] ${cert_path} 即将过期 (剩余 $((remaining_seconds/86400)) 天),准备替换。"
        return 0
    else
        log "[Check] ${cert_path} 有效期充足,跳过。"
        return 1
    fi
}

# 执行替换流程
process_targets() {
    local ip=$1
    local src_fullchain="${SOURCE_BASE_DIR}/${ip}/fullchain.pem"
    local src_key="${SOURCE_BASE_DIR}/${ip}/private.key"

    # 检查源证书是否存在
    if [[ ! -s "$src_fullchain" ]] || [[ ! -s "$src_key" ]]; then
        log "${RED}[Error] 源 IP 证书不存在或为空: ${src_fullchain}${NC}"
        log "${YELLOW}[Tip] 请确认证书是否已申请并放置在 /etc/ssl/ip/${ip}/ 目录下${NC}"
        exit 1
    fi

    # 遍历任务列表
    for target in "${TARGETS[@]}"; do
        IFS='|' read -r t_cert t_key t_cmd t_desc t_user <<< "$target"
        
        check_cert_status "$t_cert"
        if [ $? -eq 0 ]; then
            log "${YELLOW}[Action] 开始替换: $t_desc ...${NC}"
            
            # 1. 备份
            cp -f "$t_cert" "${t_cert}.bak_$(date +%F_%H%M)" 2>/dev/null
            cp -f "$t_key" "${t_key}.bak_$(date +%F_%H%M)" 2>/dev/null
            
            # 2. 覆盖 (使用 cat 保持 inode 和部分属性)
            cat "$src_fullchain" > "$t_cert"
            cat "$src_key" > "$t_key"
            
            # 3. 权限修正
            if [[ -n "$t_user" ]] && [[ "$t_user" != "root" ]]; then
                if id "$t_user" &>/dev/null; then
                    chown "$t_user:$t_user" "$t_cert" "$t_key"
                fi
            fi
            chmod 600 "$t_cert" "$t_key"
            
            # 4. 重启服务
            log "[Restart] 执行重启: $t_cmd"
            eval "$t_cmd" >/dev/null 2>&1
            
            if [ $? -eq 0 ]; then
                log "${GREEN}[Success] $t_desc 替换完成。${NC}"
            else
                log "${RED}[Error] $t_desc 重启命令失败。${NC}"
            fi
        fi
    done
}

# 添加 Crontab
add_cron() {
    local script_path=$(readlink -f "$0")
    local cron_job="0 1 * * * /bin/bash $script_path >> $LOG_FILE 2>&1"
    
    if ! crontab -l 2>/dev/null | grep -Fq "$script_path"; then
        (crontab -l 2>/dev/null; echo "$cron_job") | crontab -
        log "[Init] 已添加计划任务 (每天凌晨 1 点)。"
    fi
}

# =================主逻辑入口=================

# 必须 root 运行
if [[ $EUID -ne 0 ]]; then echo "请使用 root 运行此脚本"; exit 1; fi

PUBLIC_IP=$(get_public_ip)
log "=== 开始执行面板 SSL 维护脚本 (IP: $PUBLIC_IP) ==="

# 1. 探测环境
detect_targets

# 2. 确保计划任务
add_cron

# 3. 执行处理
process_targets "$PUBLIC_IP"

log "=== 脚本执行结束 ==="
发布日期:
分类:未分类

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理

退出移动版