Docker使用Certbot颁发更新证书

概述

使用Docker容器运行Certbot,通过Cloudflare DNS验证方式自动化颁发和更新Let’s Encrypt SSL证书。

适用版本:Certbot 4.0.0 及以上。

准备工作

1. Cloudflare API凭证配置

创建Cloudflare API凭证文件 cloudflare.ini

方式一:邮箱+全局API密钥(不推荐)

# Cloudflare API credentials used by Certbot
dns_cloudflare_email = user@example.com
dns_cloudflare_api_key = xxxx

方式二:API令牌模式(推荐,更安全)

# Cloudflare API credentials used by Certbot
dns_cloudflare_api_token = xxxxx

API令牌创建步骤

  1. 登录Cloudflare控制台
  2. 进入”My Profile” → “API Tokens”
  3. 点击”Create Token”
  4. 选择”Edit zone DNS”模板
  5. 配置权限:Zone → DNS → Edit
  6. 选择要管理的域名
  7. 生成令牌并复制到配置文件中

2. 目录结构准备

# 创建证书存储目录
mkdir -p /data/nginx/cert
# 创建Cloudflare凭证文件
vim /data/nginx/cert/cloudflare.ini
# 设置文件权限(重要!)
chmod 600 /data/nginx/cert/cloudflare.ini

颁发新证书

单次运行命令

docker run --rm \
  -v /data/nginx/cert:/etc/letsencrypt \
  certbot/dns-cloudflare certonly \
  --non-interactive \
  --agree-tos \
  --email user@example.com \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d host.domain.com

参数说明

  • --rm:容器运行后自动删除
  • -v:挂载证书存储目录
  • certonly:仅获取证书,不安装
  • --non-interactive:非交互模式
  • --agree-tos:同意服务条款
  • --email:通知邮箱
  • --dns-cloudflare:使用Cloudflare DNS验证
  • -d:要申请证书的域名(可多个)

自动化更新证书

宿主机脚本方案

先确认 Nginx 容器在其 Compose 文件中使用固定名称 nginx,并确认宿主机证书目录 /data/nginx/cert 同时挂载到 Nginx 容器的 /etc/letsencrypt

在宿主机创建 /usr/local/sbin/renew-certificates.sh

#!/usr/bin/env bash
set -eu
 
docker run --rm \
  -v /data/nginx/cert:/etc/letsencrypt \
  certbot/dns-cloudflare renew \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  --non-interactive \
&& docker exec nginx nginx -s reload

宿主机凭据文件必须位于 /data/nginx/cert/cloudflare.ini,并执行:

chmod 600 /data/nginx/cert/cloudflare.ini
chmod 750 /usr/local/sbin/renew-certificates.sh

由宿主机 root 的 crontab 每天运行脚本:

0 2 * * * /usr/local/sbin/renew-certificates.sh >> /var/log/certbot-renew.log 2>&1

&& 保证 Certbot 命令失败时不会重载 Nginx。certbot renew 在本次没有证书达到续期条件时仍可能返回成功,因此 Nginx 可能执行一次无必要的 reload;它只会重新读取当前有效证书,不改变证书内容。该方案不向 Certbot 容器挂载 Docker socket,Docker 控制命令全部由宿主机执行。

Certbot Renew工作机制详解

1. 证书续期流程

Certbot执行 renew 命令时的完整流程:

1. 扫描宿主机 /data/nginx/cert/renewal/ 目录

2. 读取每个证书的 renewal configuration 文件

3. 检查证书剩余有效期

4. 判断是否需要续期(Certbot 4.0.0 及以上在证书剩余寿命不足三分之一时续期;寿命不超过 10 天的证书在剩余不足二分之一时续期)

5. 需要续期 → 执行验证流程

6. 不需要续期 → 跳过

2. Renewal配置文件

每个已申请证书对应一个配置文件:

/data/nginx/cert/renewal/example.com.conf

文件内容包含:

  • 域名列表
  • 验证方式(dns-cloudflare/webroot等)
  • 插件参数
  • 证书路径
  • 原始申请参数

3. 续期触发条件

# 查看证书过期时间
docker run --rm \
  -v /data/nginx/cert:/etc/letsencrypt \
  certbot/dns-cloudflare certificates
 
# 手动强制续期(即使未到期)
docker run --rm \
  -v /data/nginx/cert:/etc/letsencrypt \
  certbot/dns-cloudflare renew \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  --non-interactive \
  --force-renewal

4. Cloudflare DNS验证流程

当证书需要续期时:

1. 调用 dns-cloudflare 插件

2. 使用 cloudflare.ini 中的 API Token

3. 在 Cloudflare DNS 中创建 _acme-challenge TXT 记录

4. Let's Encrypt ACME 服务器查询该记录

5. 验证成功后签发新证书

6. 删除临时 DNS 记录

证书文件结构

证书存储位置

/data/nginx/cert/
├── live/                    # 当前使用的证书
│   └── example.com/
│       ├── cert.pem        # 证书
│       ├── chain.pem       # 中间证书
│       ├── fullchain.pem   # 完整证书链
│       └── privkey.pem     # 私钥
├── archive/                 # 历史证书存档
│   └── example.com/
│       ├── cert1.pem
│       ├── cert2.pem
│       └── ...
└── renewal/                 # 续期配置文件
    └── example.com.conf

Nginx配置示例

以下路径是宿主机 /data/nginx/cert 挂载到 Nginx 容器后的容器内路径:

server {
    listen 443 ssl;
    server_name example.com;
 
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
 
    # 其他配置...
}

常见问题与解决方案

问题1:证书续期失败

症状:宿主机续期脚本返回错误

可能原因

  1. Cloudflare API Token权限不足
  2. 网络连接问题
  3. DNS记录创建失败

解决方案

# 1. 检查API Token权限
# 2. 测试DNS记录创建
docker run --rm -v /data/nginx/cert:/etc/letsencrypt certbot/dns-cloudflare certificates
 
# 3. 手动测试续期(带详细日志)
docker run --rm \
  -v /data/nginx/cert:/etc/letsencrypt \
  certbot/dns-cloudflare renew \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  --non-interactive \
  --dry-run

问题2:证书文件权限问题

症状:Nginx无法读取证书文件

解决方案

# 设置正确的权限
chmod 755 /data/nginx/cert/live/
chmod 755 /data/nginx/cert/archive/
chmod 644 /data/nginx/cert/live/example.com/*.pem
chmod 600 /data/nginx/cert/live/example.com/privkey.pem

问题3:多域名证书

申请多域名证书

docker run --rm \
  -v /data/nginx/cert:/etc/letsencrypt \
  certbot/dns-cloudflare certonly \
  --non-interactive \
  --agree-tos \
  --email user@example.com \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d example.com \
  -d www.example.com \
  -d api.example.com

最佳实践

安全建议

  1. 使用API Token而非全局API密钥
  2. 限制API Token权限:仅授予DNS编辑权限
  3. 保护凭证文件chmod 600 cloudflare.ini
  4. 定期轮换API Token:每3-6个月更新一次

监控建议

  1. 证书过期监控

    # 检查证书状态
    docker run --rm \
      -v /data/nginx/cert:/etc/letsencrypt \
      certbot/dns-cloudflare certificates | grep -A2 "VALID"
  2. 续期日志监控

    # 查看宿主机 cron 写入的续期日志
    tail -f /var/log/certbot-renew.log
  3. 设置告警:证书剩余天数<15天时发送通知

备份策略

  1. 备份证书文件

    # 备份宿主机证书目录
    tar -czf /backup/letsencrypt-$(date +%Y%m%d).tar.gz /data/nginx/cert/
  2. 备份Cloudflare凭证:安全存储cloudflare.ini文件

扩展功能

通配符证书

docker run --rm \
  -v /data/nginx/cert:/etc/letsencrypt \
  certbot/dns-cloudflare certonly \
  --non-interactive \
  --agree-tos \
  --email user@example.com \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d "*.example.com" \
  -d example.com

测试环境使用

# 使用Let's Encrypt测试环境(避免频率限制)
docker run --rm \
  -v /data/nginx/cert:/etc/letsencrypt \
  certbot/dns-cloudflare certonly \
  --test-cert \
  --non-interactive \
  --agree-tos \
  --email user@example.com \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d example.com

Obsidian关联

相关笔记

  • Nginx配置与优化 - 如何配置Nginx使用Certbot生成的SSL证书
  • 系统运维 - 系统运维知识库目录

反向链接

使用场景

  1. Web服务器SSL自动化:与此笔记配合使用 → SSL证书配置
  2. Docker化部署:容器化的证书管理方案
  3. 生产环境部署:结合监控和备份策略

工作流程

graph TD
    A[准备Cloudflare API] --> B[Docker运行Certbot]
    B --> C[生成SSL证书]
    C --> D[Nginx配置证书路径]
    D --> E[自动化更新证书]
    E --> F[证书更新后重载Nginx]

    style A fill:#e1f5fe
    style C fill:#f3e5f5
    style F fill:#e8f5e8