#!/bin/bash # ============================================================ # 现有 VPS 证书过期 + 续期修复脚本 # 适用场景:用旧版 vless-ws-tls-setup.sh 部署的机器, # 证书用 --standalone 申请,nginx 起来后一直占 80 端口, # 导致 certbot renew 从第一次续期起就必定失败。 # # 本脚本做的事: # 1. 找到已部署的域名(从 /etc/letsencrypt/live 里找) # 2. 用 certbot 的 nginx 插件重新签发证书(nginx 不需要停) # 3. 把 renewal 配置里的 authenticator 改成 nginx 插件 # 4. 重写 cron 续期任务,加日志、用 deploy-hook # 5. 跑一次 --dry-run 验证以后能不能正常自动续期 # ============================================================ set -e RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' info() { echo -e "${CYAN}[INFO]${NC} $1"; } success() { echo -e "${GREEN}[OK]${NC} $1"; } warning() { echo -e "${YELLOW}[!]${NC} $1"; } error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } [ "$EUID" -ne 0 ] && error "请使用 root 用户运行" # 1. 找域名 if [ -n "$1" ]; then DOMAIN="$1" else DOMAIN=$(ls /etc/letsencrypt/live 2>/dev/null | grep -v README | head -1) fi [ -z "$DOMAIN" ] && error "找不到域名,请手动指定:bash $0 你的域名" info "目标域名:$DOMAIN" # 2. 确保 nginx 插件已安装 if ! dpkg -l | grep -q python3-certbot-nginx; then info "安装 python3-certbot-nginx..." apt-get update -qq && apt-get install -y -qq python3-certbot-nginx fi # 3. 检查 nginx 是否在跑(插件依赖 nginx 正常运行) if ! systemctl is-active --quiet nginx; then warning "nginx 当前未运行,先启动它" systemctl start nginx || error "nginx 启动失败,请先手动修复 nginx 再运行本脚本" fi # 4. 用 nginx 插件强制重签证书(不停 nginx,不改端口占用) info "用 nginx 插件重新签发证书(过程中不会中断现有连接)..." EMAIL_GUESS=$(grep -oP '(?<=--email )\S+' /var/log/letsencrypt/letsencrypt.log 2>/dev/null | tail -1) EMAIL_GUESS=${EMAIL_GUESS:-admin@${DOMAIN}} certbot certonly --nginx \ --cert-name "$DOMAIN" \ -d "$DOMAIN" \ --non-interactive \ --agree-tos \ --email "$EMAIL_GUESS" \ --force-renewal \ || error "证书重签失败,请检查:1. 域名解析是否指向本机 2. Cloudflare 是否切成灰色云朵(仅DNS)后再试" success "证书重签成功" # 5. 确认/修正 renewal 配置里的 authenticator RENEWAL_CONF="/etc/letsencrypt/renewal/${DOMAIN}.conf" if [ -f "$RENEWAL_CONF" ]; then sed -i 's/^authenticator = .*/authenticator = nginx/' "$RENEWAL_CONF" if grep -q '^installer = ' "$RENEWAL_CONF"; then sed -i 's/^installer = .*/installer = nginx/' "$RENEWAL_CONF" else sed -i '/^\[renewalparams\]/a installer = nginx' "$RENEWAL_CONF" fi success "已确认 $RENEWAL_CONF 中 authenticator = nginx" else warning "未找到 $RENEWAL_CONF,续期方式可能没有正确记录,请手动检查" fi # 6. 重写 cron 续期任务 mkdir -p /var/log touch /var/log/certbot-renew.log cat > /etc/cron.d/certbot-renew << CRONEOF # Let's Encrypt 证书自动续期 - 每周一凌晨3点执行 0 3 * * 1 root certbot renew --deploy-hook "systemctl reload nginx" >> /var/log/certbot-renew.log 2>&1 CRONEOF chmod 644 /etc/cron.d/certbot-renew systemctl restart cron success "已重写 cron 续期任务,日志输出到 /var/log/certbot-renew.log" # 7. 让 nginx 加载新证书 systemctl reload nginx success "nginx 已重新加载新证书" # 8. 验证以后能不能正常自动续期(这次 nginx 是开着的,应该会通过) info "验证自动续期是否正常(dry-run,nginx 全程不会停)..." if certbot renew --dry-run >> /var/log/certbot-renew.log 2>&1; then success "续期验证通过!以后不会再出现到期后失效的问题" else warning "验证仍未通过,详情见 /var/log/certbot-renew.log,请贴出来我再帮你看" fi CERT_PATH="/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" if [ -f "$CERT_PATH" ]; then CERT_EXPIRY=$(openssl x509 -enddate -noout -in "$CERT_PATH" | cut -d= -f2) echo "" success "当前证书有效期至:$CERT_EXPIRY" fi echo "" info "以后排查续期问题直接看日志:cat /var/log/certbot-renew.log"