智慧水务管理系统 - 精河县供水工程综合管理平台

certbot-renew.sh 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #!/bin/bash
  2. # ============================================================
  3. # Let's Encrypt 证书自动续期脚本
  4. #
  5. # 首次申请证书:
  6. # certbot certonly --webroot -w /var/www/certbot \
  7. # -d your-domain.com -d www.your-domain.com \
  8. # --email admin@your-domain.com --agree-tos --no-eff-email
  9. #
  10. # 配置 cron 自动续期 (每天凌晨 3 点检查):
  11. # 0 3 * * * /opt/water-management/deploy/production/nginx/certbot-renew.sh >> /var/log/certbot-renew.log 2>&1
  12. # ============================================================
  13. set -euo pipefail
  14. # ==================== 配置 ====================
  15. DOMAIN="${DOMAIN:-water.example.com}"
  16. WEBROOT="/var/www/certbot"
  17. EMAIL="${CERTBOT_EMAIL:-admin@example.com}"
  18. NGINX_CONTAINER="${NGINX_CONTAINER:-wm-frontend}"
  19. RENEW_DAYS_BEFORE_EXPIRY=30
  20. # ==================== 函数 ====================
  21. log() {
  22. echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
  23. }
  24. # 检查证书是否需要续期
  25. check_cert_expiry() {
  26. local cert_path="/etc/letsencrypt/live/${DOMAIN}/fullchain.pem"
  27. if [ ! -f "$cert_path" ]; then
  28. log "⚠️ 证书文件不存在: $cert_path"
  29. return 1
  30. fi
  31. local expiry_date
  32. expiry_date=$(openssl x509 -enddate -noout -in "$cert_path" | cut -d= -f2)
  33. local expiry_epoch
  34. expiry_epoch=$(date -d "$expiry_date" +%s)
  35. local now_epoch
  36. now_epoch=$(date +%s)
  37. local days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
  38. log "📅 证书过期时间: $expiry_date (剩余 ${days_left} 天)"
  39. if [ $days_left -le $RENEW_DAYS_BEFORE_EXPIRY ]; then
  40. log "⚠️ 证书将在 ${days_left} 天内过期,需要续期"
  41. return 0
  42. else
  43. log "✅ 证书有效,无需续期"
  44. return 1
  45. fi
  46. }
  47. # 首次申请证书
  48. init_cert() {
  49. log "🔐 首次申请证书..."
  50. # 确保 webroot 目录存在
  51. mkdir -p "$WEBROOT"
  52. # 使用 webroot 方式申请(不需要停止 Nginx)
  53. certbot certonly --webroot \
  54. -w "$WEBROOT" \
  55. -d "$DOMAIN" \
  56. --email "$EMAIL" \
  57. --agree-tos \
  58. --no-eff-email \
  59. --non-interactive
  60. if [ $? -eq 0 ]; then
  61. log "✅ 证书申请成功"
  62. reload_nginx
  63. else
  64. log "❌ 证书申请失败"
  65. exit 1
  66. fi
  67. }
  68. # 续期证书
  69. renew_cert() {
  70. log "🔄 开始续期证书..."
  71. certbot renew \
  72. --webroot \
  73. -w "$WEBROOT" \
  74. --non-interactive \
  75. --quiet
  76. if [ $? -eq 0 ]; then
  77. log "✅ 证书续期成功"
  78. reload_nginx
  79. else
  80. log "❌ 证书续期失败"
  81. send_alert "证书续期失败,请手动检查!"
  82. exit 1
  83. fi
  84. }
  85. # 重载 Nginx
  86. reload_nginx() {
  87. log "🔄 重载 Nginx 配置..."
  88. if docker exec "$NGINX_CONTAINER" nginx -s reload 2>/dev/null; then
  89. log "✅ Nginx 重载成功"
  90. else
  91. log "⚠️ Docker 方式重载失败,尝试系统方式..."
  92. systemctl reload nginx 2>/dev/null || nginx -s reload 2>/dev/null || true
  93. fi
  94. }
  95. # 发送告警(企业微信 Webhook)
  96. send_alert() {
  97. local message="$1"
  98. local webhook="${WECOM_WEBHOOK:-}"
  99. if [ -z "$webhook" ]; then
  100. log "⚠️ 未配置企业微信 Webhook,跳过告警"
  101. return 0
  102. fi
  103. local payload
  104. payload=$(cat <<EOF
  105. {
  106. "msgtype": "text",
  107. "text": {
  108. "content": "🚨 证书告警 [${DOMAIN}]\n${message}\n时间: $(date '+%Y-%m-%d %H:%M:%S')"
  109. }
  110. }
  111. EOF
  112. )
  113. curl -s -X POST "$webhook" \
  114. -H "Content-Type: application/json" \
  115. -d "$payload" > /dev/null 2>&1 || true
  116. log "📤 告警已发送"
  117. }
  118. # ==================== 主逻辑 ====================
  119. log "========================================="
  120. log "Let's Encrypt 证书管理 - ${DOMAIN}"
  121. log "========================================="
  122. # 检查证书是否存在
  123. if [ ! -d "/etc/letsencrypt/live/${DOMAIN}" ]; then
  124. log "📋 证书不存在,执行首次申请"
  125. init_cert
  126. else
  127. # 检查是否需要续期
  128. if check_cert_expiry; then
  129. renew_cert
  130. fi
  131. fi
  132. log "========================================="
  133. log "执行完毕"
  134. log "========================================="