- 1. TLS/SSL 证书基本概念
- 2. 什么是 Let's Encrypt
- 3. ACME 协议工作原理
- 4. Certbot 安装
- 5. 证书签发方式
- 6. 签发出的文件结构
- 7. 自动续期配置
- 8. Rate Limits 注意事项
- 9. 替代工具
- 10. 故障排查
- 11. 安全最佳实践
- 12. 结论
1. TLS/SSL 证书基本概念
1.1 为什么需要 TLS 证书
TLS(Transport Layer Security)证书为 Web 通信提供三个核心安全要素。
- 加密(Encryption):加密客户端与服务器之间的数据,防止窃听
- 认证(Authentication):证明服务器确实是该域名的所有者
- 完整性(Integrity):保证数据在传输过程中未被篡改
1.2 证书链结构
┌───────────────────────┐
│ Root CA Certificate │ ← 浏览器/OS 内置的信任锚
│ (Self-Signed) │
└───────────┬───────────┘
│ 签名
┌───────────▼───────────┐
│ Intermediate CA Cert │ ← 中间 CA 证书
│ (Signed by Root CA) │
└───────────┬───────────┘
│ 签名
┌───────────▼───────────┐
│ Server Certificate │ ← 域名证书 (Leaf)
│ (Signed by Inter. CA) │
└───────────────────────┘
1.3 公钥/私钥对
┌──────────┐ CSR 生成 ┌──────────────┐
│ 私钥 │ ──────────────> │ CSR │
│(Private │ │(证书签名 │
│ Key) │ │ 请求) │
└──────────┘ └──────┬───────┘
│ │ 提交给 CA
│ ┌──────▼───────┐
│ │ CA │
│ │ (签名发放) │
│ └──────┬───────┘
│ │
│ ┌──────▼───────┐
└───────── 一起使用 ──────>│ 证书 │
│ (Certificate) │
└──────────────┘
- 私钥(Private Key):仅服务器持有,绝对禁止外泄
- CSR(Certificate Signing Request):公钥 + 域名信息,提交给 CA
- 证书(Certificate):CA 签名后的公钥 + 域名信息
2. 什么是 Let's Encrypt
2.1 概述
Let's Encrypt 是由 ISRG(Internet Security Research Group)运营的免费、自动化、开放的证书颁发机构(CA)。
核心特点:
- 免费:免费签发域名验证(DV)证书
- 自动化:通过 ACME 协议完全自动化证书签发与续期
- 开放:使用开源协议与工具
- 有效期:90 天(用较短周期强化安全性)
- 可信:所有主流浏览器与操作系统均信任
2.2 证书类型对比
| 类型 | 验证级别 | 签发时间 | 费用 | Let's Encrypt |
|---|---|---|---|---|
| DV (Domain Validation) | 确认域名所有 | 数分钟 | 免费~低价 | 支持 |
| OV (Organization Validation) | 确认组织 | 数日 | 收费 | 不支持 |
| EV (Extended Validation) | 扩展验证 | 数周 | 高价 | 不支持 |
3. ACME 协议工作原理
ACME(Automatic Certificate Management Environment)是将证书签发自动化的协议。
3.1 整体流程
┌────────┐ ┌──────────────┐
│ Certbot│ │ Let's Encrypt│
│(Client)│ │ (CA/ACME) │
└───┬────┘ └──────┬───────┘
│ 1. Account Registration │
│──────────────────────────────────────>│
│ 2. Account Created │
│<──────────────────────────────────────│
│ │
│ 3. Order (domain list) │
│──────────────────────────────────────>│
│ 4. Authorizations + Challenges │
│<──────────────────────────────────────│
│ │
│ 5. Respond to Challenge │
│ (HTTP-01 / DNS-01 / TLS-ALPN-01) │
│──────────────────────────────────────>│
│ │
│ 6. Challenge Validated │
│<──────────────────────────────────────│
│ │
│ 7. Finalize (send CSR) │
│──────────────────────────────────────>│
│ 8. Certificate issued │
│<──────────────────────────────────────│
3.2 HTTP-01 Challenge
最常见的挑战方式。通过端口 80 验证能否访问特定文件。
Let's Encrypt → http://yourdomain.com/.well-known/acme-challenge/TOKEN_VALUE
验证过程:
1. Certbot 将令牌文件放置到 Web 服务器
2. Let's Encrypt 通过 HTTP 访问该文件
3. 文件内容与期望值一致则确认域名所有权
优点:
- 最简单、最通用
- 无需额外的 DNS 配置
- 在大多数 Web 服务器上都容易配置
限制:
- 端口 80 必须能从外部访问
- 无法签发通配符证书
- 位于负载均衡器之后时配置可能变复杂
3.3 DNS-01 Challenge
通过创建 DNS TXT 记录来证明域名所有权。签发通配符证书时必须使用此方式。
Let's Encrypt → DNS 查询: _acme-challenge.yourdomain.com TXT
验证过程:
1. Certbot 计算令牌
2. 在 _acme-challenge.yourdomain.com TXT 记录中设置令牌值
3. Let's Encrypt 查询 DNS 确认该值
4. 一致则确认域名所有权
优点:
- 可以签发通配符证书
- 没有 Web 服务器也可以完成
- 端口 80 不开放也没关系
- 多台服务器可共用同一份证书
限制:
- 需要 DNS Provider API(自动化时)
- 可能存在 DNS 传播延迟
- 手动配置比较繁琐
3.4 TLS-ALPN-01 Challenge
在端口 443 建立 TLS 连接时,通过 ALPN(Application-Layer Protocol Negotiation)扩展进行验证。
验证过程:
1. Let's Encrypt 尝试通过端口 443 建立 TLS 连接
2. 用 ALPN 协商 "acme-tls/1" 协议
3. 服务器用自签名证书响应(包含特定扩展)
4. 用证书的 acmeIdentifier 扩展值进行验证
优点:
- 不需要端口 80
- 仅凭端口 443 即可完成验证
限制:
- 大多数 Web 服务器默认不支持
- 需要额外的软件(例如 Caddy)
3.5 挑战方式对比表
| 功能 | HTTP-01 | DNS-01 | TLS-ALPN-01 |
|---|---|---|---|
| 端口 | 80 | 无 (DNS) | 443 |
| 通配符 | X | O | X |
| 需要 Web 服务 | O | X | O |
| 自动化难度 | 低 | 中 (需要 DNS API) | 高 |
| 需要 DNS 配置 | X | O | X |
| 主要用途 | 普通 Web 服务器 | 通配符、内部服务器 | 特殊环境 |
4. Certbot 安装
4.1 Ubuntu / Debian
# 使用 snap 安装(推荐)
sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
4.2 CentOS / RHEL / Rocky Linux
# 启用 EPEL 仓库
sudo dnf install epel-release
sudo dnf install certbot
# Nginx 插件
sudo dnf install python3-certbot-nginx
4.3 macOS
brew install certbot
4.4 Docker
docker run -it --rm \
-v /etc/letsencrypt:/etc/letsencrypt \
-v /var/lib/letsencrypt:/var/lib/letsencrypt \
certbot/certbot certonly --help
5. 证书签发方式
5.1 Standalone 模式
Certbot 启动自带的 Web 服务器来处理 HTTP-01 挑战。需要先停止现有的 Web 服务器。
# 因为要占用端口 80,需要先停止现有 Web 服务器
sudo systemctl stop nginx
sudo certbot certonly --standalone \
-d example.com \
-d www.example.com \
--agree-tos \
--email admin@example.com \
--non-interactive
# 重启 Web 服务器
sudo systemctl start nginx
5.2 Webroot 模式
无需停止现有 Web 服务器即可签发证书。需要配置 Web 服务器对外提供特定目录。
Nginx 配置追加:
server {
listen 80;
server_name example.com www.example.com;
# ACME 挑战用目录
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# 其余请求重定向到 HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# 创建目录
sudo mkdir -p /var/www/certbot
# 以 Webroot 模式签发证书
sudo certbot certonly --webroot \
-w /var/www/certbot \
-d example.com \
-d www.example.com \
--agree-tos \
--email admin@example.com
5.3 Nginx 插件
Certbot 会自动修改 Nginx 配置。
sudo certbot --nginx \
-d example.com \
-d www.example.com \
--agree-tos \
--email admin@example.com
Certbot 自动完成的工作:
- 处理 HTTP-01 挑战
- 在 Nginx 配置中添加 SSL 相关指令
- 添加将 HTTP 重定向到 HTTPS 的配置
5.4 Apache 插件
sudo certbot --apache \
-d example.com \
-d www.example.com \
--agree-tos \
--email admin@example.com
5.5 DNS 插件(通配符证书)
通配符证书必须使用 DNS-01 挑战。
Cloudflare DNS 插件:
# 安装插件
sudo snap install certbot-dns-cloudflare
# 创建 API 令牌文件
sudo mkdir -p /etc/letsencrypt
cat > /etc/letsencrypt/cloudflare.ini << 'CFEOF'
dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN
CFEOF
sudo chmod 600 /etc/letsencrypt/cloudflare.ini
# 签发通配符证书
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d "example.com" \
-d "*.example.com" \
--agree-tos \
--email admin@example.com
AWS Route 53 DNS 插件:
sudo snap install certbot-dns-route53
# 配置 AWS 凭证(IAM Role 或环境变量)
sudo certbot certonly \
--dns-route53 \
-d "example.com" \
-d "*.example.com" \
--agree-tos \
--email admin@example.com
手动 DNS 挑战:
sudo certbot certonly --manual \
--preferred-challenges dns \
-d "example.com" \
-d "*.example.com" \
--agree-tos \
--email admin@example.com
# 当 Certbot 提示 TXT 记录值时
# 在 DNS 管理页面手动添加 TXT 记录:
# _acme-challenge.example.com TXT "提示的_令牌_值"
6. 签发出的文件结构
# 证书文件路径
ls -la /etc/letsencrypt/live/example.com/
# 文件结构
/etc/letsencrypt/live/example.com/
cert.pem # 服务器证书(仅域名证书)
chain.pem # 中间 CA 证书链
fullchain.pem # cert.pem + chain.pem(服务器使用)
privkey.pem # 私钥
# 实际文件位于 archive 目录,live 是符号链接
/etc/letsencrypt/archive/example.com/
cert1.pem
chain1.pem
fullchain1.pem
privkey1.pem
| 文件 | 内容 | 用途 |
|---|---|---|
| cert.pem | 服务器证书 | 单独使用(很少用到) |
| chain.pem | 中间 CA 证书 | 用于 OCSP Stapling |
| fullchain.pem | 服务器证书 + 中间 CA | 用于 Nginx ssl_certificate |
| privkey.pem | 私钥 | 用于 Nginx ssl_certificate_key |
应用到 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;
}
7. 自动续期配置
7.1 续期测试
# 不实际续期,仅执行测试
sudo certbot renew --dry-run
7.2 systemd Timer(推荐)
通过 snap 安装 Certbot 时会自动创建 systemd timer。
# 确认 Timer 状态
sudo systemctl status snap.certbot.renew.timer
# Timer 列表
sudo systemctl list-timers | grep certbot
自行创建时:
# /etc/systemd/system/certbot-renew.timer
[Unit]
Description=Certbot renewal timer
[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=3600
Persistent=true
[Install]
WantedBy=timers.target
# /etc/systemd/system/certbot-renew.service
[Unit]
Description=Certbot renewal service
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"
sudo systemctl daemon-reload
sudo systemctl enable --now certbot-renew.timer
7.3 Crontab
# crontab -e
# 每天凌晨 2 点、下午 2 点尝试续期
0 2,14 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx"
7.4 续期钩子(Hook)配置
# 续期前钩子
sudo mkdir -p /etc/letsencrypt/renewal-hooks/pre
cat > /etc/letsencrypt/renewal-hooks/pre/stop-service.sh << 'HOOKEOF'
#!/bin/bash
# Standalone 模式用:停止 Web 服务器
systemctl stop nginx
HOOKEOF
chmod +x /etc/letsencrypt/renewal-hooks/pre/stop-service.sh
# 续期后钩子
sudo mkdir -p /etc/letsencrypt/renewal-hooks/post
cat > /etc/letsencrypt/renewal-hooks/post/start-service.sh << 'HOOKEOF'
#!/bin/bash
# 重启 Web 服务器
systemctl start nginx
HOOKEOF
chmod +x /etc/letsencrypt/renewal-hooks/post/start-service.sh
# 部署钩子(仅在续期成功时执行)
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << 'HOOKEOF'
#!/bin/bash
systemctl reload nginx
echo "Certificate renewed and nginx reloaded at $(date)" >> /var/log/certbot-deploy.log
HOOKEOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
8. Rate Limits 注意事项
Let's Encrypt 为防止滥用,适用以下 Rate Limits。
| 限制 | 值 | 说明 |
|---|---|---|
| Certificates per Registered Domain | 50 / 周 | 同一域名每周 50 个 |
| Duplicate Certificate | 5 / 周 | 同一域名集合 5 个 |
| Failed Validations | 5 / 小时 / 账号 / 主机名 | 验证失败限制 |
| New Orders | 300 / 3 小时 | 新订单限制 |
| Accounts per IP | 10 / 3 小时 | 每个 IP 的账号创建限制 |
Staging 环境的利用:
# 测试时使用 Staging 服务器(Rate Limit 宽松得多)
sudo certbot certonly --standalone \
--staging \
-d example.com \
--agree-tos \
--email admin@example.com
9. 替代工具
9.1 acme.sh
用纯 Shell 脚本编写的 ACME 客户端。
# 安装
curl https://get.acme.sh | sh
# 签发证书
acme.sh --issue -d example.com -w /var/www/html
# 通配符(使用 DNS API)
acme.sh --issue \
-d example.com \
-d "*.example.com" \
--dns dns_cf \
--dnssleep 120
# 安装到 Nginx
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/example.com.key \
--fullchain-file /etc/nginx/ssl/example.com.fullchain.pem \
--reloadcmd "systemctl reload nginx"
9.2 Caddy(自动 HTTPS)
Caddy 在 Web 服务器自身就自动处理 HTTPS。
# Caddyfile
example.com {
root * /var/www/html
file_server
}
# 仅此而已就能自动签发并续期 Let's Encrypt 证书!
9.3 lego
用 Go 编写的 ACME 客户端。
# 安装
go install github.com/go-acme/lego/v4/cmd/lego@latest
# HTTP 挑战
lego --email admin@example.com \
--domains example.com \
--http \
run
# DNS 挑战(Cloudflare)
CLOUDFLARE_DNS_API_TOKEN=xxx \
lego --email admin@example.com \
--domains "*.example.com" \
--dns cloudflare \
run
9.4 cert-manager(Kubernetes)
在 Kubernetes 环境中自动管理证书。
# ClusterIssuer 定义
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx
# 在 Ingress 中申请证书
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- app.example.com
secretName: app-tls-secret
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
# 直接创建 Certificate 资源
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: app-certificate
namespace: default
spec:
secretName: app-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- app.example.com
- api.example.com
renewBefore: 720h # 提前 30 天续期
10. 故障排查
10.1 常见错误与解决方法
Challenge 失败:
# 确认 HTTP-01 挑战文件是否可访问
curl -v http://yourdomain.com/.well-known/acme-challenge/test
# 确认防火墙(端口 80)
sudo ufw status
sudo iptables -L -n | grep 80
# 确认 DNS
dig +short yourdomain.com
nslookup yourdomain.com
证书续期失败:
# 确认续期状态
sudo certbot certificates
# 确认详细日志
sudo certbot renew --dry-run -v
# 确认日志文件
sudo cat /var/log/letsencrypt/letsencrypt.log
确认证书信息:
# 确认证书内容
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -text -noout
# 确认到期日
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -enddate -noout
# 确认远程服务器证书
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | \
openssl x509 -text -noout
# 验证证书链
openssl verify -CAfile /etc/letsencrypt/live/example.com/chain.pem \
/etc/letsencrypt/live/example.com/cert.pem
10.2 证书吊销(Revoke)
# 吊销证书
sudo certbot revoke --cert-path /etc/letsencrypt/live/example.com/cert.pem
# 删除证书(清理文件)
sudo certbot delete --cert-name example.com
11. 安全最佳实践
- 保护私钥:将 privkey.pem 文件权限设为 600,仅 root 可读
- 监控自动续期:续期失败时配置告警(邮件、Slack 等)
- 先用 Staging:新配置务必先在 Staging 环境测试
- 应用 HSTS:签发证书后配置 HSTS 头
- 监控证书透明度:在 crt.sh 等站点监控域名证书签发记录
- 备份:定期备份 /etc/letsencrypt 目录
# 确认私钥权限
ls -la /etc/letsencrypt/live/example.com/privkey.pem
# -rw------- 1 root root ... privkey.pem
# 查询证书透明度日志
# https://crt.sh/?q=example.com
12. 结论
Let's Encrypt 与 Certbot 已经成为免费 TLS 证书的标准。核心要点:
- HTTP-01 挑战最简单,但通配符证书必须用 DNS-01
- Certbot 的 Webroot 或 Nginx 插件最为实用
- 自动续期用 systemd timer 或 crontab 配置
- Kubernetes 环境中 cert-manager 是标准方案
- 理解 Rate Limits,测试时充分利用 Staging 服务器
- 签发证书后务必优化 Nginx TLS 安全配置
현재 단락 (1/396)
TLS(Transport Layer Security)证书为 Web 通信提供三个核心安全要素。