first commit
This commit is contained in:
239
test_head_clean.sh
Normal file
239
test_head_clean.sh
Normal file
@@ -0,0 +1,239 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 脚本名称: update_acme_cert.sh
|
||||
# 功能: 自动检测并更新 ACME SSL 证书
|
||||
# 作者: Assistant
|
||||
# 日期: 2026-03-03
|
||||
# sudo chmod +x acme_renew.sh
|
||||
|
||||
# ================= 配置区域 =================
|
||||
ACME_SH="/root/.acme.sh/acme.sh"
|
||||
NGINX_CONF_DIR="/etc/nginx/conf.d"
|
||||
WEBROOT="/var/www/letsencrypt"
|
||||
TLS_BASE_DIR="/etc/nginx/tls"
|
||||
LOG_FILE="/var/log/acme_update.log"
|
||||
# ===========================================
|
||||
|
||||
# 彩色输出(方便阅读)
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数 - 中文输出
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"; }
|
||||
log_warn() { echo -e "${YELLOW}[警告]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"; }
|
||||
log_error() { echo -e "${RED}[错误]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"; }
|
||||
log_step() { echo -e "${BLUE}[步骤]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"; }
|
||||
|
||||
# 检查是否 root 权限
|
||||
check_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "请使用 root 权限运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 输入域名并验证
|
||||
input_domain() {
|
||||
echo ""
|
||||
read -p "$(echo -e ${BLUE}[输入]${NC} 请输入要更新的域名(例如:example.com): " DOMAIN
|
||||
if [[ -z "$DOMAIN" ]]; then
|
||||
log_error "域名不能为空"
|
||||
exit 1
|
||||
fi
|
||||
log_info "目标域名: $DOMAIN"
|
||||
}
|
||||
|
||||
# 检查 nginx 配置文件是否存在
|
||||
check_nginx_conf() {
|
||||
CONF_FILE="${NGINX_CONF_DIR}/${DOMAIN}.conf"
|
||||
if [[ ! -f "$CONF_FILE" ]]; then
|
||||
log_warn "未找到配置文件: $CONF_FILE"
|
||||
# 尝试模糊匹配
|
||||
CONF_FILE=$(grep -l "server_name.*$DOMAIN" ${NGINX_CONF_DIR}/*.conf 2>/dev/null | head -n1)
|
||||
if [[ -z "$CONF_FILE" ]]; then
|
||||
log_error "在 $NGINX_CONF_DIR 中未找到包含域名 $DOMAIN 的 nginx 配置"
|
||||
exit 1
|
||||
fi
|
||||
log_info "通过模糊匹配找到配置文件: $CONF_FILE"
|
||||
else
|
||||
log_info "找到配置文件: $CONF_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# 从 nginx 配置中提取证书路径
|
||||
extract_cert_path() {
|
||||
log_step "解析配置文件,提取证书路径..."
|
||||
|
||||
# 提取 ssl_certificate 路径
|
||||
CERT_PATH=$(grep -E "^\s*ssl_certificate\s+" "$CONF_FILE" | head -n1 | awk '{print $2}' | tr -d ';')
|
||||
KEY_PATH=$(grep -E "^\s*ssl_certificate_key\s+" "$CONF_FILE" | head -n1 | awk '{print $2}' | tr -d ';')
|
||||
|
||||
if [[ -z "$CERT_PATH" || -z "$KEY_PATH" ]]; then
|
||||
log_warn "未在配置中找到 ssl_certificate 或 ssl_certificate_key 指令"
|
||||
# 使用默认路径
|
||||
CERT_DIR="${TLS_BASE_DIR}/${DOMAIN}"
|
||||
CERT_FILE="${CERT_DIR}/cert.pem"
|
||||
KEY_FILE="${CERT_DIR}/key.pem"
|
||||
log_info "使用默认证书目录: $CERT_DIR"
|
||||
else
|
||||
CERT_DIR=$(dirname "$CERT_PATH")
|
||||
CERT_FILE="$CERT_PATH"
|
||||
KEY_FILE="$KEY_PATH"
|
||||
log_info "证书文件: $CERT_FILE"
|
||||
log_info "密钥文件: $KEY_FILE"
|
||||
fi
|
||||
|
||||
# 确保目录存在
|
||||
mkdir -p "$CERT_DIR"
|
||||
}
|
||||
|
||||
# 检查证书是否过期(返回 0=已过期,1=未过期)
|
||||
check_cert_expired() {
|
||||
local cert_file="$1"
|
||||
if [[ ! -f "$cert_file" ]]; then
|
||||
log_warn "证书文件不存在: $cert_file,视为需要更新"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 获取过期时间
|
||||
EXPIRE_DATE=$(openssl x509 -in "$cert_file" -noout -enddate 2>/dev/null | cut -d= -f2)
|
||||
if [[ -z "$EXPIRE_DATE" ]]; then
|
||||
log_warn "无法读取证书过期时间,视为需要更新"
|
||||
return 0
|
||||
fi
|
||||
|
||||
EXPIRE_TIMESTAMP=$(date -d "$EXPIRE_DATE" +%s 2>/dev/null)
|
||||
NOW_TIMESTAMP=$(date +%s)
|
||||
DAYS_LEFT=$(( (EXPIRE_TIMESTAMP - NOW_TIMESTAMP) / 86400 ))
|
||||
|
||||
log_info "证书过期时间: $EXPIRE_DATE"
|
||||
log_info "距离过期还有: ${DAYS_LEFT} 天"
|
||||
|
||||
# 提前 30 天预警更新
|
||||
if [[ $DAYS_LEFT -lt 30 ]]; then
|
||||
log_warn "证书即将过期(<30天),需要更新"
|
||||
return 0
|
||||
else
|
||||
log_info "证书仍在有效期内,无需更新"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 使用 acme.sh 颁发/更新证书
|
||||
renew_certificate() {
|
||||
log_step "开始更新证书: $DOMAIN"
|
||||
|
||||
# 方式1: 设置默认 CA(可选)
|
||||
if ! "$ACME_SH" --set-default-ca --server letsencrypt >/dev/null 2>&1; then
|
||||
log_warn "设置 CA 失败,继续尝试颁发证书..."
|
||||
fi
|
||||
|
||||
# 方式2: 颁发证书(webroot 模式)
|
||||
log_info "【方式2】使用 webroot 模式颁发证书..."
|
||||
log_info "Webroot 路径: $WEBROOT"
|
||||
|
||||
local cmd="$ACME_SH --issue -d $DOMAIN --webroot $WEBROOT"
|
||||
if [[ "$FORCE_RENEW" == "true" ]]; then
|
||||
cmd="$cmd --force"
|
||||
log_info "⚠️ 已启用强制更新模式"
|
||||
fi
|
||||
|
||||
# 捕获输出用于分析,同时也显示在终端
|
||||
local tmp_out=$(mktemp)
|
||||
$cmd 2>&1 | tee "$tmp_out"
|
||||
local ret=${PIPESTATUS[0]}
|
||||
|
||||
# 分析结果
|
||||
if [[ $ret -eq 0 ]]; then
|
||||
log_info "✅ 证书颁发/更新成功!"
|
||||
rm -f "$tmp_out"
|
||||
return 0
|
||||
else
|
||||
# 检查是否是因为已经更新而跳过
|
||||
if grep -qE "Domains not changed|Skipping|already issued" "$tmp_out"; then
|
||||
log_warn "⚠️ ACME 提示证书无需更新或已存在,视为成功"
|
||||
rm -f "$tmp_out"
|
||||
return 0
|
||||
else
|
||||
log_error "❌ 证书颁发失败,请检查域名解析和 webroot 权限"
|
||||
rm -f "$tmp_out"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 验证证书内容(匹配用户提供的特征)
|
||||
verify_cert_success() {
|
||||
local cert_file="$1"
|
||||
if [[ ! -f "$cert_file" ]]; then
|
||||
log_warn "验证失败: 证书文件不存在 -> $cert_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查是否包含证书结束标记(用户提供的特征)
|
||||
# 使用 -- 避免 grep 将证书内容当作选项处理
|
||||
if grep -q -- "-----END CERTIFICATE-----" "$cert_file"; then
|
||||
# 可选:检查特定指纹(按需启用)
|
||||
# if grep -q "O1CA0HAB8LbWS" "$cert_file"; then
|
||||
log_info "✅ 证书内容验证通过"
|
||||
return 0
|
||||
# fi
|
||||
fi
|
||||
log_warn "证书内容验证未通过 (未找到结束标记)"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 安装证书到指定位置
|
||||
install_certificate() {
|
||||
log_step "安装证书到生产环境..."
|
||||
|
||||
INSTALL_CMD="$ACME_SH --install-cert -d $DOMAIN \
|
||||
--key-file ${KEY_FILE} \
|
||||
--fullchain-file ${CERT_FILE} \
|
||||
--reloadcmd \"service nginx force-reload\""
|
||||
|
||||
log_info "执行安装命令: $INSTALL_CMD"
|
||||
|
||||
if eval "$INSTALL_CMD"; then
|
||||
log_info "✅ 证书安装成功"
|
||||
return 0
|
||||
else
|
||||
log_error "❌ 证书安装失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 设置证书文件权限
|
||||
set_cert_permissions() {
|
||||
log_step "设置证书文件权限..."
|
||||
|
||||
# 确保使用实际目录
|
||||
CERT_DIR=$(dirname "$CERT_FILE")
|
||||
|
||||
if sudo chown www-data:www-data "${CERT_DIR}"/*.pem 2>/dev/null; then
|
||||
log_info "✅ 文件所有者已设置为 www-data:www-data"
|
||||
else
|
||||
log_warn "⚠️ chown 执行失败,请手动检查权限"
|
||||
fi
|
||||
|
||||
if sudo chmod 600 "${CERT_DIR}"/*.pem 2>/dev/null; then
|
||||
log_info "✅ 文件权限已设置为 600(仅所有者可读写)"
|
||||
else
|
||||
log_warn "⚠️ chmod 执行失败,请手动检查权限"
|
||||
fi
|
||||
|
||||
# 确保 nginx 可读(www-data 是 nginx 运行用户)
|
||||
log_info "✅ 权限设置完成,nginx 可正常读取证书"
|
||||
}
|
||||
|
||||
# 重载 nginx 使配置生效
|
||||
reload_nginx() {
|
||||
log_step "重载 nginx 配置..."
|
||||
if service nginx force-reload >/dev/null 2>&1 || nginx -s reload >/dev/null 2>&1; then
|
||||
log_info "✅ nginx 重载成功"
|
||||
|
||||
# 主流程
|
||||
main() {
|
||||
Reference in New Issue
Block a user