Nginx Proxy Manager批量证书更新脚本:Shell自动化与定时任务配置

Nginx Proxy Manager批量证书更新脚本:Shell自动化与定时任务配置

【免费下载链接】nginx-proxy-manager Docker container for managing Nginx proxy hosts with a simple, powerful interface 【免费下载链接】nginx-proxy-manager 项目地址: https://gitcode.com/GitHub_Trending/ng/nginx-proxy-manager

你是否还在为Nginx Proxy Manager中数十个SSL证书到期手动更新而焦头烂额?本文将带你构建一套企业级证书自动化更新解决方案,通过Shell脚本实现Let's Encrypt证书批量续期,并配置高可靠定时任务,彻底解决证书过期问题。

证书管理痛点与解决方案

企业级Nginx Proxy Manager(以下简称NPM)部署中,证书管理面临三大核心痛点:

痛点传统解决方案自动化方案优势
证书到期风险日历提醒+手动更新提前30天自动检测并续期
批量操作繁琐逐个点击Web界面更新一次执行完成所有证书更新
续期状态不透明手动记录更新结果完整日志+失败告警机制

NPM内置的证书自动续期功能存在一定局限性,特别是在处理大量证书或复杂DNS验证场景时表现不稳定。通过本文提供的Shell脚本方案,可实现更精细的控制和更高的可靠性。

核心实现原理

证书更新流程设计

mermaid

NPM证书存储结构解析

NPM将Let's Encrypt证书存储在特定目录结构中,证书文件命名遵循固定规则:

/etc/letsencrypt/live/
├── npm-1/                # 证书ID为1的证书目录
│   ├── fullchain.pem     # 完整证书链
│   ├── privkey.pem       # 私钥文件
│   └── cert.pem          # 证书文件
├── npm-2/                # 证书ID为2的证书目录
└── ...

证书元数据存储在SQLite数据库中,可通过以下命令查询:

sqlite3 /data/database.sqlite "SELECT id, domain_names, expires_on FROM certificates WHERE provider='letsencrypt' AND is_deleted=0;"

批量更新脚本开发

基础版脚本实现

创建/usr/local/bin/npm-cert-renew.sh文件,实现基本批量更新功能:

#!/bin/bash
# Nginx Proxy Manager证书批量更新脚本
# 版本: 1.0
# 作者: 运维自动化团队

# 配置参数
LOG_FILE="/var/log/npm-cert-renew.log"
MAX_RETRIES=2
RENEW_BEFORE_DAYS=30
NPM_CONTAINER_NAME="nginx-proxy-manager"

# 初始化日志
echo "===== $(date '+%Y-%m-%d %H:%M:%S') 证书更新开始 =====" >> $LOG_FILE

# 获取所有活跃的Let's Encrypt证书ID
CERT_IDS=$(docker exec $NPM_CONTAINER_NAME sqlite3 /data/database.sqlite \
  "SELECT id FROM certificates WHERE provider='letsencrypt' AND is_deleted=0 AND expires_on < date('now', '+${RENEW_BEFORE_DAYS} days');" 2>> $LOG_FILE)

if [ -z "$CERT_IDS" ]; then
  echo "没有需要更新的证书" >> $LOG_FILE
  echo "===== $(date '+%Y-%m-%d %H:%M:%S') 证书更新结束 =====" >> $LOG_FILE
  exit 0
fi

# 遍历证书ID并更新
echo "发现$(echo "$CERT_IDS" | wc -l)个需要更新的证书" >> $LOG_FILE
for CERT_ID in $CERT_IDS; do
  echo "正在更新证书ID: $CERT_ID" >> $LOG_FILE
  
  # 执行证书更新(重试机制)
  RETRY=0
  SUCCESS=0
  while [ $RETRY -lt $MAX_RETRIES ]; do
    # 调用NPM内部API触发证书更新
    docker exec $NPM_CONTAINER_NAME node - <<EOF >> $LOG_FILE 2>&1
const certificate = require('/app/backend/internal/certificate');
certificate.renew({
  can: () => Promise.resolve({ permission_visibility: 'all' }),
  token: { getUserId: () => 1 }
}, { id: $CERT_ID })
.then(() => process.exit(0))
.catch(err => { console.error(err); process.exit(1); });
EOF
    
    if [ $? -eq 0 ]; then
      SUCCESS=1
      break
    fi
    
    RETRY=$((RETRY + 1))
    echo "证书ID: $CERT_ID 更新失败,正在进行第$RETRY次重试..." >> $LOG_FILE
    sleep 10
  done
  
  if [ $SUCCESS -eq 1 ]; then
    echo "证书ID: $CERT_ID 更新成功" >> $LOG_FILE
  else
    echo "证书ID: $CERT_ID 所有重试均失败" >> $LOG_FILE
    # 发送告警通知(可集成邮件/钉钉/企业微信)
    echo "NPM证书更新失败: 证书ID $CERT_ID" | mail -s "NPM证书更新告警" admin@example.com
  fi
done

# 重新加载Nginx配置
docker exec $NPM_CONTAINER_NAME nginx -s reload >> $LOG_FILE 2>&1
echo "Nginx配置已重新加载" >> $LOG_FILE

echo "===== $(date '+%Y-%m-%d %H:%M:%S') 证书更新结束 =====" >> $LOG_FILE

高级版脚本特性增强

为满足企业级需求,对基础脚本进行增强:

#!/bin/bash
# Nginx Proxy Manager企业级证书批量更新脚本
# 版本: 2.0
# 支持: 并发控制/健康检查/详细报告/多存储后端

# 配置参数
LOG_FILE="/var/log/npm-cert-renew.log"
JSON_REPORT="/var/log/npm-cert-renew-report.json"
MAX_RETRIES=3
RENEW_BEFORE_DAYS=30
CONCURRENT_LIMIT=5  # 并发更新限制
NPM_DB_PATH="/data/database.sqlite"
CERT_STORAGE_PATH="/etc/letsencrypt/live"

# 初始化报告
echo '{"start_time": "'$(date '+%Y-%m-%dT%H:%M:%S')'", "total": 0, "success": 0, "failed": 0, "certificates": []}' > $JSON_REPORT

# 数据库连接函数
db_query() {
  if [ -f "$NPM_DB_PATH" ]; then
    sqlite3 "$NPM_DB_PATH" "$1"
  else
    docker exec nginx-proxy-manager sqlite3 /data/database.sqlite "$1"
  fi
}

# 证书健康检查函数
check_certificate_health() {
  local CERT_ID=$1
  local CERT_PATH="${CERT_STORAGE_PATH}/npm-${CERT_ID}"
  
  # 检查证书文件是否存在
  if [ ! -f "${CERT_PATH}/fullchain.pem" ] || [ ! -f "${CERT_PATH}/privkey.pem" ]; then
    return 1
  fi
  
  # 验证证书有效性
  openssl x509 -in "${CERT_PATH}/fullchain.pem" -noout -checkend 86400 >/dev/null 2>&1
  return $?
}

# 并发控制函数
run_with_concurrency() {
  local COMMAND=$1
  local MAX_CONCURRENT=$2
  
  # 使用xargs控制并发数
  echo "$3" | xargs -I {} -P $MAX_CONCURRENT bash -c "$COMMAND {}"
}

# 完整实现请参考高级版脚本(略)

脚本权限与安全配置

# 设置脚本权限
chmod 700 /usr/local/bin/npm-cert-renew.sh
chown root:root /usr/local/bin/npm-cert-renew.sh

# 配置日志轮转
cat > /etc/logrotate.d/npm-cert-renew <<EOF
/var/log/npm-cert-renew.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    create 0600 root root
}
EOF

定时任务配置与优化

基础定时任务设置

使用crontab配置定期执行:

# 编辑crontab
crontab -e

# 添加以下内容(每天凌晨2点执行)
0 2 * * * /usr/local/bin/npm-cert-renew.sh

# 验证定时任务
crontab -l

高级定时任务方案

对于企业级部署,推荐使用systemd timer替代crontab,实现更精确的控制和状态监控:

# 创建service文件
cat > /etc/systemd/system/npm-cert-renew.service <<EOF
[Unit]
Description=Nginx Proxy Manager证书批量更新服务
After=docker.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/npm-cert-renew.sh
User=root
Group=root
EOF

# 创建timer文件
cat > /etc/systemd/system/npm-cert-renew.timer <<EOF
[Unit]
Description=定期执行Nginx Proxy Manager证书批量更新

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=30m

[Install]
WantedBy=timers.target
EOF

# 启用并启动timer
systemctl daemon-reload
systemctl enable --now npm-cert-renew.timer

# 检查timer状态
systemctl list-timers --all | grep npm-cert-renew

执行时间优化

证书更新任务应避开业务高峰期,可通过以下策略优化执行时间:

  1. 随机延迟启动:使用RandomizedDelaySec=30m参数,避免所有服务器同时执行更新
  2. 分批次执行:将证书按域名分组,不同组在不同时间段更新
  3. 资源控制:通过cgroups限制证书更新进程的CPU/内存使用
# 分批次执行示例(按证书ID末位数字)
for i in {0..9}; do
  echo "0 2 * * * [ \$((\$(date +\%d) \% 10)) -eq $i ] && /usr/local/bin/npm-cert-renew.sh --batch $i" >> /etc/crontab
done

监控与告警实现

日志分析与状态监控

# 创建日志分析脚本
cat > /usr/local/bin/analyze-cert-log.sh <<EOF
#!/bin/bash
LOG_FILE="/var/log/npm-cert-renew.log"
JSON_REPORT="/var/log/npm-cert-renew-report.json"

# 提取关键指标
TOTAL=$(jq .total $JSON_REPORT)
SUCCESS=$(jq .success $JSON_REPORT)
FAILED=$(jq .failed $JSON_REPORT)
START_TIME=$(jq -r .start_time $JSON_REPORT)
END_TIME=$(date '+%Y-%m-%dT%H:%M:%S')

# 计算耗时
DURATION=\$(( $(date -d "$END_TIME" +%s) - $(date -d "$START_TIME" +%s) ))

# 输出监控指标(可接入Prometheus)
echo "npm_cert_renew_total $TOTAL"
echo "npm_cert_renew_success $SUCCESS"
echo "npm_cert_renew_failed $FAILED"
echo "npm_cert_renew_duration_seconds \$DURATION"
EOF

# 设置执行权限
chmod +x /usr/local/bin/analyze-cert-log.sh

企业微信告警集成

# 添加企业微信告警函数到更新脚本
send_wechat_alert() {
  local CERT_ID=$1
  local STATUS=$2
  local MESSAGE=$3
  
  # 企业微信API配置
  CORP_ID="your_corp_id"
  AGENT_ID="your_agent_id"
  APP_SECRET="your_app_secret"
  PARTY_ID="your_party_id"
  
  # 获取access_token
  ACCESS_TOKEN=$(curl -s "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$CORP_ID&corpsecret=$APP_SECRET" | jq -r .access_token)
  
  # 发送消息
  curl -s "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$ACCESS_TOKEN" -H "Content-Type: application/json" -d '{
    "touser": "@all",
    "toparty": "'$PARTY_ID'",
    "msgtype": "text",
    "agentid": '$AGENT_ID',
    "text": {
      "content": "【NPM证书更新通知】\n证书ID: '$CERT_ID'\n状态: '$STATUS'\n时间: '$(date '+%Y-%m-%d %H:%M:%S')'\n详情: '$MESSAGE'"
    },
    "safe": 0
  }' > /dev/null
}

常见问题解决方案

证书更新失败处理

错误类型可能原因解决方案
Certbot进程冲突多个更新任务同时运行实现进程锁机制,确保只有一个实例运行
DNS验证超时DNS解析延迟或TTL设置问题增加--dns-propagation-seconds参数至300秒
存储空间不足/tmp分区空间不足调整Certbot工作目录至空间充足分区
API调用失败NPM后端服务不稳定增加API调用重试机制和超时控制

进程冲突解决方案

实现文件锁机制防止并行执行冲突:

# 添加到脚本开头
LOCK_FILE="/var/run/npm-cert-renew.lock"
if [ -f "$LOCK_FILE" ]; then
  echo "另一个更新进程正在运行,退出本次执行" >> $LOG_FILE
  exit 1
fi
trap "rm -f $LOCK_FILE" EXIT
touch $LOCK_FILE

大规模部署优化

当管理超过100个证书时,建议采用以下架构优化:

  1. 分布式证书管理:按业务线拆分证书管理任务
  2. 证书缓存机制:缓存已更新证书信息,避免重复检查
  3. 预先生成证书:提前7天预先生成新证书,到期后无缝切换
  4. 蓝绿部署:维护两套证书目录,更新时切换 symbolic link
# 证书预生成与切换示例
PRE_GEN_DIR="/etc/letsencrypt/pre-generated"
LIVE_DIR="/etc/letsencrypt/live"

# 预生成证书
certbot certonly --non-interactive --agree-tos -d example.com -m admin@example.com --webroot -w /var/www/html -o "$PRE_GEN_DIR/npm-$CERT_ID"

# 切换证书(原子操作)
ln -sfn "$PRE_GEN_DIR/npm-$CERT_ID" "$LIVE_DIR/npm-$CERT_ID"

脚本扩展与定制

支持DNS验证方式

对于无法通过HTTP验证的场景,可扩展脚本支持DNS验证:

# 添加DNS验证支持
cat >> /usr/local/bin/npm-cert-renew.sh <<EOF
--dns-provider)
  DNS_PROVIDER=\$2
  shift 2
  ;;
--dns-credentials)
  DNS_CREDENTIALS=\$2
  shift 2
  ;;
EOF

# 更新Certbot命令参数
if [ -n "$DNS_PROVIDER" ]; then
  ARGS+=(--authenticator "dns-$DNS_PROVIDER" --dns-$DNS_PROVIDER-credentials "$DNS_CREDENTIALS")
fi

多环境支持

为开发、测试、生产环境配置不同更新策略:

# 环境配置文件示例
cat > /etc/npm-cert-config.env <<EOF
# 开发环境配置
[development]
RENEW_BEFORE_DAYS=15
MAX_RETRIES=1
LOG_LEVEL=debug

# 生产环境配置
[production]
RENEW_BEFORE_DAYS=30
MAX_RETRIES=3
LOG_LEVEL=info
ALERT_RECIPIENTS=admin@example.com,security@example.com
EOF

# 在脚本中加载环境配置
ENV=$(hostname | grep -q "prod" && echo "production" || echo "development")
source <(grep -A 100 "^\[$ENV\]" /etc/npm-cert-config.env | grep -v "^\[" | grep -v "^#")

总结与最佳实践

通过本文介绍的Shell脚本和定时任务配置,你已掌握Nginx Proxy Manager证书批量更新的完整解决方案。企业级部署建议遵循以下最佳实践:

  1. 分层防御:同时启用NPM内置自动更新和外部脚本更新,双重保障
  2. 渐进式部署:新脚本先在测试环境验证,再逐步推广到生产环境
  3. 定期演练:每季度进行一次证书更新故障演练,验证应急响应流程
  4. 文档即代码:将证书更新流程和脚本纳入版本控制,确保可追溯性

作为DevOps最佳实践,建议将证书更新脚本与CI/CD流水线集成,实现证书管理代码化、自动化和标准化。通过持续优化证书更新流程,可将证书相关运维工作量减少90%以上,同时将证书过期风险降至接近零。

最后,请记住证书安全是Web安全的基础,定期审查和更新证书管理策略同样重要。建议每半年对证书管理流程进行一次全面审计,确保符合最新安全标准和最佳实践。

【免费下载链接】nginx-proxy-manager Docker container for managing Nginx proxy hosts with a simple, powerful interface 【免费下载链接】nginx-proxy-manager 项目地址: https://gitcode.com/GitHub_Trending/ng/nginx-proxy-manager

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值