Certbot 和 Let’s Encrypt 的关系

Let’s Encrypt

  • 一个免费、自动化、开放的公共证书颁发机构(CA)。
  • 通过 ACME(Automatic Certificate Management Environment,RFC 8555)协议向域名所有者颁发 DV(Domain Validation)TLS / SSL 证书。

Certbot

  • 由 Electronic Frontier Foundation(EFF)维护的开源 ACME 客户端。
  • 主要目标是简化与 Let’s Encrypt 之间的交互:
    • 自动化域名验证(HTTP-01、DNS-01、TLS-ALPN-01)
    • 安装并续期证书
    • 更新 Web 服务器配置(Apache、Nginx、Lighttpd 等)
  • Certbot 也能与任何兼容 ACME 的 CA 通信,不限于 Let’s Encrypt。

ACME 挑战怎么选

挑战适用场景限制
HTTP-0180 端口可达、单域名 / 多域名(最常用)不能签 wildcard
DNS-01任意域名 —— wildcard 唯一选项需要 DNS provider API 或手动加 TXT
TLS-ALPN-01443 可达但 80 被封,常用于反代后端较少用,需要 ACME-aware 反代支持

典型工作流程

  1. 解析参数并检测服务器类型。
  2. 选择并执行挑战(HTTP-01 例:在 /.well-known/acme-challenge/ 下写入 token)。
  3. Let’s Encrypt 回访验证域名归属。
  4. 验证通过后签发证书;Certbot 下载并安装到本地。
  5. 创建 systemd timer(snap 安装会自动配置)周期性 certbot renew 续期。

开始实验

实验环境:Amazon Linux 2023(AL2023)。AL2023 没有官方 certbot 包,社区维护的 snap 仓库是当前最省事的安装路径。

启动 EC2 并配置 nginx

  • Instance type: t2.nano
  • Username: ec2-user
  • Security Groups: 放行 HTTP 和 HTTPS
  • Public IPv4: 35.86.90.4
sudo dnf update -y
sudo dnf install -y nginx
nginx -v
# nginx version: nginx/1.28.0

sudo systemctl enable --now nginx

直接访问 http://35.86.90.4,会看到默认的 nginx 欢迎页面。

sudo vi /etc/nginx/conf.d/default.conf
server {
    listen       80;
    listen       [::]:80;
    server_name  tmp.yifans.net;
    root         /usr/share/nginx/html;

    error_page 404 /404.html;
    location = /404.html {
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    }
}
sudo nginx -t
sudo systemctl reload nginx

把域名 tmp.yifans.net 解析到 35.86.90.4,访问 http://tmp.yifans.net 同样看到 nginx 欢迎页面,浏览器显示 Not Secure。

安装 certbot

# AL2023 暂无官方 snapd,用社区维护的仓库
sudo wget -O /etc/yum.repos.d/snapd.repo \
    https://bboozzoo.github.io/snapd-amazon-linux/al2023/snapd.repo
sudo dnf install -y snapd
sudo systemctl enable --now snapd.socket

sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

certbot --version
# certbot 4.1.1
# 前置 nginx 配置中要有 server_name tmp.yifans.net;
sudo certbot --nginx -d tmp.yifans.net
# 全自动完成

访问 http://tmp.yifans.net 会自动跳转 https。

# 看看被 certbot 自动修改的 nginx 配置
cat /etc/nginx/conf.d/default.conf
server {
    server_name  tmp.yifans.net;
    root         /usr/share/nginx/html;

    error_page 404 /404.html;
    location = /404.html {
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/tmp.yifans.net/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/tmp.yifans.net/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = tmp.yifans.net) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen       80;
    listen       [::]:80;
    server_name  tmp.yifans.net;
    return 404; # managed by Certbot
}

注意:Certbot 默认不启用 HTTP/2。要开 HTTP/2 在 listen 443 ssl; 后手动加一行 http2 on;(NGINX 1.25.1+)或改用老语法 listen 443 ssl http2;。详见 NGINX 启用 HTTP/2

# 看看 certbot 预置的 SSL 参数
cat /etc/letsencrypt/options-ssl-nginx.conf
# Contents are based on https://ssl-config.mozilla.org

ssl_session_cache shared:le_nginx_SSL:10m;
# 内存里建一个 10 MiB 共享会话缓存。
# 加速同客户端后续的 TLS 握手;约可保存 40000 个会话条目。

ssl_session_timeout 1440m;
# 单位是分钟(1440 m = 24 h),缓存条目过期时间。

ssl_session_tickets off;
# 关闭 TLS Session Tickets。
# 早期实现 ticket 加密密钥不便轮换,会损害前向安全;Mozilla modern 至今仍建议关闭。

ssl_protocols TLSv1.2 TLSv1.3;
# 只允许 1.2 / 1.3;关掉 SSLv3、TLS 1.0 / 1.1。

ssl_prefer_server_ciphers off;
# 让客户端从交集里挑套件。仅开放现代套件的前提下,这样兼容性更好。

ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...";
# 仅保留 GCM / ChaCha20-Poly1305 这一代 AEAD 套件,弃用 CBC / RC4 / 3DES。
# ECDHE-ECDSA:椭圆曲线 DH + ECDSA 证书
# ECDHE-RSA:  椭圆曲线 DH + RSA  证书
# DHE-RSA:    传统 DH,给极旧设备兜底

ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; 告诉 NGINX:DHE(Diffie-Hellman Ephemeral)握手时别用默认的 1024 位弱参数,改用 Certbot 预生成的更安全 DH 参数文件。

DHE 是 TLS 的一种密钥交换算法,做两件事:

  • 让客户端和服务器在公网信道上安全地协商一把对称加密用的临时密钥。
  • 提供前向安全(Forward Secrecy):即便日后服务器长期私钥泄露,过去抓到的密文仍然解不开。
# 看看证书
sudo openssl x509 -in /etc/letsencrypt/live/tmp.yifans.net/fullchain.pem -noout -text | grep yifans.net

        Subject: CN=tmp.yifans.net
                DNS:tmp.yifans.net

只获取证书(certonly)

tmp-certonly.yifans.net 解析到 35.86.90.4,让 HTTP-01 挑战能回访。

sudo vi /etc/nginx/conf.d/certonly.conf
server {
    listen       80;
    listen       [::]:80;
    server_name  tmp-certonly.yifans.net;
    root         /usr/share/nginx/html;
}
sudo nginx -t
sudo systemctl reload nginx

sudo certbot certonly --nginx

# 选项里勾选要签的域名
1: tmp.yifans.net
2: tmp-certonly.yifans.net

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/tmp-certonly.yifans.net/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/tmp-certonly.yifans.net/privkey.pem
This certificate expires on 2025-10-01.

certonly 模式只拿证书,不会改你的 nginx 配置,需要手动在 server block 里加 ssl_certificate 等指令。

Wildcard 证书

  • Wildcard 强制走 DNS-01:HTTP-01 没法证明你对 *.example.com 下所有子域的控制权。
  • *.example.com 不覆盖 example.com 本身,也不覆盖 hello.goodbye.example.com
  • *.goodbye.example.com 可覆盖 hello.goodbye.example.com
  • 不可以申请 *.*.example.com,只能一个 *

有 DNS API 的话可用 DNS Plugins,没有就 manual:

sudo certbot certonly --manual -d '*.tmp.yifans.net' --preferred-challenges dns

Please deploy a DNS TXT record under the name:
  _acme-challenge.tmp.yifans.net.
with the following value: ...

# 用 dig 验证再回车,否则 Certbot 失败后会消耗速率额度
# https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.tmp.yifans.net.

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/tmp.yifans.net-0001/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/tmp.yifans.net-0001/privkey.pem
sudo openssl x509 -in /etc/letsencrypt/live/tmp.yifans.net-0001/fullchain.pem -noout -text | grep yifans.net

        Subject: CN=*.tmp.yifans.net
                DNS:*.tmp.yifans.net

挂到 nginx:

server {
    server_name  *.tmp.yifans.net;
    root         /usr/share/nginx/html;

    listen 443 ssl;
    http2 on;
    ssl_certificate     /etc/letsencrypt/live/tmp.yifans.net-0001/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/tmp.yifans.net-0001/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

server {
    listen 80;
    server_name *.tmp.yifans.net;
    return 301 https://$host$request_uri;
}
sudo nginx -t && sudo systemctl reload nginx

1.tmp.yifans.net 解析到 35.86.90.4,访问 https://1.tmp.yifans.net,Chrome 证书信息里能看到 Common Name (CN) *.tmp.yifans.net

续期

snap 安装的 Certbot 自带 systemd timer,每天跑两次,只在 30 天内到期时才真正续期:

systemctl list-timers | grep certbot
sudo certbot renew --dry-run            # 演练一次,不真签发

如果是 certonly 模式(Certbot 不管 nginx),需要让续期后自动 reload:

# 一次性:
sudo certbot renew --deploy-hook 'systemctl reload nginx'

# 或永久放在 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh:
#!/bin/sh
systemctl reload nginx

几个常被踩到的坑

  • 速率限制。Let’s Encrypt 每个注册域名每周最多 50 张证书。调试或写脚本时务必先用 --test-cert(staging 服务器),免得被 ban 一周。staging 颁发的证书浏览器不信任,但能验通整个流程。
  • 证书有效期 90 天。Certbot 在 30 天阈值内才会真正续期,所以 renew 跑了"没动作"是正常的。
  • ECDSA 默认。Let’s Encrypt 2023 起对新证书默认签 ECDSA P-256(更小更快);想要 RSA 反而要显式 --key-type rsa
  • HSTS 不会自动配。拿到证书只完成了一半,浏览器仍会接受 http 跳转;建议在 server block 加:
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    
    正式开 HSTS 前先用较短 max-age 灰度,避免误配把整个域锁死。
  • 改了证书别忘 reload--nginx 插件帮你做了;certonly 自己做(见上面 deploy-hook)。

References

– EOF –