#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" . "$ROOT_DIR/scripts/lib.sh" load_env DRY_RUN=false [ "${1:-}" = "--dry-run" ] && DRY_RUN=true CERT_DIR="${CERT_DIR:-$ROOT_DIR/config/nginx/certs}" DOMAIN="${CONSOLE_DOMAIN:-}" DOMAIN="${DOMAIN#https://}" DOMAIN="${DOMAIN%%/*}" audit "renew-cert" "STARTED" "domain=$DOMAIN dry_run=$DRY_RUN" progress "renew-cert" "STARTED" "domain=$DOMAIN" [ -n "$DOMAIN" ] || fail_json "XUQM_PRIVATE_5010" "CONSOLE_DOMAIN not set" "renew-cert" mkdir -p "$CERT_DIR" # Backup existing certs CERT_FILE="$CERT_DIR/${DOMAIN}.crt" KEY_FILE="$CERT_DIR/${DOMAIN}.key" if [ -f "$CERT_FILE" ]; then CERT_BACKUP="${CERT_FILE}.$(date +%Y%m%d%H%M%S).bak" cp "$CERT_FILE" "$CERT_BACKUP" cp "$KEY_FILE" "${KEY_FILE}.$(date +%Y%m%d%H%M%S).bak" audit "renew-cert" "BACKUP_DONE" "backed up to $CERT_BACKUP" fi if [ "$DRY_RUN" = "true" ]; then printf '[DRY-RUN] Would renew certificate for %s\n' "$DOMAIN" printf '[DRY-RUN] Would reload nginx after renewal\n' audit "renew-cert" "DRY_RUN" "domain=$DOMAIN" exit 0 fi # Try acme.sh first, then certbot if command -v acme.sh >/dev/null 2>&1; then acme.sh --renew -d "$DOMAIN" --force 2>&1 | tee -a "$ROOT_DIR/logs/cert-renew.log" || { audit "renew-cert" "FAILED" "acme.sh renewal failed for $DOMAIN" "$ROOT_DIR/scripts/alert-webhook.sh" "CERT_RENEW_FAILED" "Certificate renewal failed for $DOMAIN" 2>/dev/null || true fail_json "XUQM_PRIVATE_5011" "certificate renewal failed for $DOMAIN" "renew-cert" } # Copy renewed cert to nginx config dir acme.sh --install-cert -d "$DOMAIN" \ --cert-file "$CERT_FILE" \ --key-file "$KEY_FILE" \ --reloadcmd "docker exec $(compose ps -q nginx | head -1) nginx -s reload" 2>/dev/null || true elif command -v certbot >/dev/null 2>&1; then certbot renew --cert-name "$DOMAIN" --non-interactive 2>&1 | tee -a "$ROOT_DIR/logs/cert-renew.log" || { audit "renew-cert" "FAILED" "certbot renewal failed for $DOMAIN" "$ROOT_DIR/scripts/alert-webhook.sh" "CERT_RENEW_FAILED" "Certificate renewal failed for $DOMAIN" 2>/dev/null || true fail_json "XUQM_PRIVATE_5011" "certificate renewal failed for $DOMAIN" "renew-cert" } # Reload nginx NGINX_CTR="$(compose ps -q nginx 2>/dev/null | head -1 || true)" [ -n "$NGINX_CTR" ] && docker exec "$NGINX_CTR" nginx -s reload else fail_json "XUQM_PRIVATE_5012" "neither acme.sh nor certbot found" "renew-cert" fi audit "renew-cert" "DONE" "domain=$DOMAIN" progress "renew-cert" "DONE" "domain=$DOMAIN" printf 'Certificate renewed for %s\n' "$DOMAIN" # Crontab setup hint (output to stdout) printf '\n# Add to crontab for automatic renewal:\n' printf '# 0 3 * * * %s/scripts/renew-cert.sh >> %s/logs/cert-renew.log 2>&1\n' "$ROOT_DIR" "$ROOT_DIR"