月色真美

月色真美

docker使用certbot自动申请和续期SSL证书

9
2025-09-24

1.先拉取certbot镜像

docker pull certbot/certbot:v3.1.0

2.nginx的的配置

需要先配置好 nginx 的配置,并且生效

注:nginx容器的 /usr/share/nginx/html 需要和宿主机的 /app/nginx/html 映射

server {
    listen 80;
    server_name a.domain.com;
  
    location ^~ /.well-known/acme-challenge/ {
         allow all;
         root /usr/share/nginx/html;
     }
  
     location / {
        return 301 https://$server_name$request_uri;
    }
}
  
server {
    listen       443 ssl;
    server_name a.domain.com;

    ssl_certificate   /etc/nginx/cert/a.domain.com.pem;
    ssl_certificate_key  /etc/nginx/cert/a.domain.com.key;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;

    location / {
		proxy_pass http://app; # 需要修改为自己的应用
		proxy_set_header HOST $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

3.创建自动申请和续期的bash脚本

变量 DOMAINS 是待申请和续期的域名,是一个数组,自行修改

LINUX目录结构,修改时需要完整替换

/app/nginx/cert nginx上使用的ssl证书目录
/app/nginx/certbot/ssl certbot上的ssl缓存
/app/nginx/certbot/logs certbot日志
/app/nginx/html nginx默认的html路径

DOMAINS=("a.domain.com" "b.domain.com")
NUMBER=0
ISAPPLY=0
CREATION_TIME=$(date +%s)
CURRENT_TIME=$(date +%s)
DIFF_DAYS=0

for DOMAIN in "${DOMAINS[@]}"; do
    echo ${DOMAIN}
    if [ -e "/app/nginx/cert/${DOMAIN}.pem" ]; then
        # 检查当前证书是否过期
        let CREATION_TIME=$(stat -c %Y "/app/nginx/cert/${DOMAIN}.pem")
        if [ "$CREATION_TIME" -eq 0 ]; then
            echo "该系统不支持获取文件的修改时间"
            continue
        fi
        let CURRENT_TIME=$(date +%s)
        let DIFF_DAYS=$(( (CURRENT_TIME - CREATION_TIME) / 86400 ))
    else
        echo "当前证书不存在,准备申请证书..."
        let DIFF_DAYS=100
    fi

    # 证书创建时间与当前时间相差80天,执行创建
    if [ "$DIFF_DAYS" -ge 80 ]; then
        let ISAPPLY=1
        # 清空上次证书
        rm -rf /app/nginx/certbot/ssl/${DOMAIN}
        
        # 生成证书
        if [ -n "$(docker ps -q -f name=certbot)" ]; then
            docker rm -f certbot
            echo "容器 certbot 已被移除。"
        fi
        docker run -d \
          --name certbot \
          -v /app/nginx/certbot/ssl:/etc/letsencrypt/archive \
          -v /app/nginx/certbot/logs:/var/log/letsencrypt \
          -v /app/nginx/html:/data/letsencrypt \
          certbot/certbot:v3.1.0 certonly --webroot --email abc@qq.com --agree-tos --no-eff-email --webroot-path=/data/letsencrypt -d ${DOMAIN}

        NUMBER=0
        # 等待,并循环检查
        while true; do
            if [ -e "/app/nginx/certbot/ssl/${DOMAIN}" ]; then
                if [ -f "/app/nginx/certbot/ssl/${DOMAIN}/fullchain1.pem" ]; then
                    echo "${DOMAIN}的私钥文件[fullchain.pem]存在,正在重命名并移动..."
                    mv -f /app/nginx/certbot/ssl/${DOMAIN}/fullchain1.pem /app/nginx/cert/${DOMAIN}.pem
                    echo "${DOMAIN}的私钥文件移动成功"
                else
                    echo "${DOMAIN}的私钥文件[fullchain.pem]不存在"
                fi

                if [ -f "/app/nginx/certbot/ssl/${DOMAIN}/privkey1.pem" ]; then
                    echo "${DOMAIN}的公钥文件[privkey1.pem]存在,正在重命名并移动..."
                    mv -f /app/nginx/certbot/ssl/${DOMAIN}/privkey1.pem /app/nginx/cert/${DOMAIN}.key
                    echo "${DOMAIN}的公钥文件移动成功"
                else
                    echo "${DOMAIN}的公钥文件[privkey1.pem]不存在"
                fi
                docker rm -f certbot
                echo "${DOMAIN}证书申请成功"
                break
            else
                echo "等待证书申请中...$NUMBER"
            fi
            sleep 5
            let NUMBER=NUMBER+1

            # 如果等待时间超过60s,则继续休眠10分钟(Let's Encrypt限制请求)
            if [ "$NUMBER" -ge 12 ]; then
                echo "限制请求,等待10分钟后继续执行..."
                sleep 600
                docker restart certbot
                let NUMBER=0
            fi
        done
    else
        echo "未满足到期前10天,终止证书申请"
    fi
done
if [ $ISAPPLY -eq 1 ]; then
    docker restart nginx
fi

脚本原理:通过检测 nginxcert证书 是否已使用80天(按照3个月的证书有效期,即为到期前10天),当满足条件时,自动创建 certbot 容器,certbot 会按照当前域名去申请证书。当 certbot 执行时,会在 /app/nginx/html 里生成验证文件,并且由于 nginx 的事前配置,会顺利通过所有权的验证,进而生成证书。

certbot 的容器会生成四个证书,我们只需要 fullchain1.pemprivkey1.pem,将两个证书文件重命名,并移动到 nginx 所需要的证书目录下,即完成证书的申请和续期。

4.自动续期

使用 crontab 定时执行脚本,达到自动续期的效果

crontab -e

输入定时任务,示例为每日凌晨1点执行脚本,假定脚本位置是:/app/nginx/certbot/install.sh

0 1 * * * /app/nginx/certbot/install.sh

如果当前linux未使用bash执行,需要指定为bash执行

0 1 * * * /bin/bash /app/nginx/certbot/install.sh