first commit

This commit is contained in:
jeremygan2021
2026-03-03 15:01:07 +08:00
commit 5d12be811e
5 changed files with 1347 additions and 0 deletions

243
test_head.sh Normal file
View File

@@ -0,0 +1,243 @@
#!/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 重载成功"
else
log_warn "⚠️ nginx 重载失败,请手动执行: service nginx force-reload"
fi
}
# 主流程
main() {