📘 知识库
Let’s Encrypt IP 证书(TLS‑ALPN‑01)完整原理与生产实践
1️⃣ 背景与目标
在以下场景中,需要为 IP 地址(IPv4 / IPv6) 签发 TLS 证书:
- 无法绑定域名
- IPv6‑only 主机
- API / 设备直连
- 临时实例
- 内部或受控公网服务
Let’s Encrypt 提供了 IP 证书支持,但其使用方式与传统域名证书完全不同。
2️⃣ IP 证书的基本事实(必须理解)
| 项目 | 结论 |
|---|---|
| CA | Let’s Encrypt |
| Profile | shortlived |
| 有效期 | ≈ 6 天 |
| 验证方式 | 仅 TLS‑ALPN‑01 |
| HTTP‑01 | ❌ 不支持 |
| DNS‑01 | ❌ 不支持 |
| CN 包含 IP | ❌ 禁止 |
| SAN 包含 IP | ✅ 必须 |
| IPv4 + IPv6 | ✅ 同一证书支持 |
3️⃣ TLS‑ALPN‑01 的协议本质
3.1 验证目标
TLS‑ALPN‑01 的验证目标是:
证明你控制了该 IP 在 TCP 443 上的 TLS 握手
不是:
- HTTP 服务
- DNS 记录
- 某个端口
- 某个进程
3.2 为什么强制 TCP 443
- 443 是 TLS 的事实标准入口
- IP 没有 DNS 作为额外信任层
- 唯一可信锚点是 公网 TLS 握手
✅ 这是 安全边界,不是实现限制
4️⃣ 为什么不会影响正常 443 业务
4.1 ALPN 的关键作用
| 客户端 | ALPN |
|---|---|
| 浏览器 / API | h2, http/1.1 |
| ACME 验证 | acme-tls/1 |
✅ ALPN 在 TLS ClientHello 中明文可见
4.2 协议级隔离
- TLS‑ALPN‑01 在 TLS 握手阶段 即可区分
- 不解密
- 不终止 TLS
- 不影响业务连接
5️⃣ 为什么不能直接让 Nginx listen 443
5.1 根本原因(协议层)
- TLS 只能被 终止一次
- ALPN 只能在 第一次握手 中判断
- lego 必须直接参与 TLS 握手
👉 Nginx http 层 listen 443 = 已终止 TLS = ACME 无法验证
6️⃣ 唯一的无停机方案:Nginx stream + ssl_preread
6.1 架构模型
Internet
|
| TCP 443
v
Nginx stream (ssl_preread)
|
|-- ALPN=acme-tls/1 → lego (843)
|
|-- ALPN=h2/http1.1 → HTTPS 服务 (4430)
6.2 生产级 Nginx 配置(完整)
stream {
map $ssl_preread_alpn_protocols $backend {
~\bacme-tls/1\b acme_backend;
default https_backend;
}
upstream acme_backend {
server 127.0.0.1:843;
}
upstream https_backend {
server 127.0.0.1:4430;
}
server {
listen 443;
listen [::]:443;
proxy_pass $backend;
ssl_preread on;
}
}
7️⃣ lego 的正确使用方式(IP 证书)
7.1 自动获取 IPv4 + IPv6
IPV4=$(curl -fsS https://api.ipify.org)
IPV6=$(curl -fsS https://api64.ipify.org)
7.2 正确签发命令(必须参数)
lego \
--accept-tos \
--email [email protected] \
--tls \
--tls.port 843 \
--domains "$IPV4" \
--domains "$IPV6" \
--disable-cn \
run \
--profile shortlived
关键参数说明
| 参数 | 作用 |
|---|---|
--tls | 启用 TLS‑ALPN‑01 |
--tls.port | lego 本地监听端口 |
--disable-cn | 防止 IP 出现在 CN |
--profile shortlived | IP 证书专用 |
8️⃣ 证书存储位置
默认路径:
/opt/.lego/certificates/
文件命名规则:
- 使用第一个
--domains(通常是 IPv4)
9️⃣ 为什么不能用 firewalld / iptables 实现分流
9.1 层级不匹配
| 层级 | 能看到 |
|---|---|
| L3/L4(防火墙) | IP / 端口 |
| ❌ L4 | 看不到 ALPN |
| ✅ L7 | 可解析 TLS ClientHello |
👉 ALPN 在 TLS 层,防火墙无法判断
9.2 防火墙只能做什么
- ✅ 无条件 DNAT
- ❌ 条件转发(基于 ALPN)
🔟 是否存在设计缺陷?
10.1 工程体验角度
✅ 是的,不优雅
✅ 部署复杂
✅ 心智负担高
10.2 协议安全角度
❌ 不是缺陷
✅ 是刻意选择的安全边界
✅ 防止 IP 证书被滥发
1️⃣1️⃣ 可选方案对比
| 方案 | 停机 | 复杂度 | 推荐 |
|---|---|---|---|
| stream + ssl_preread | ❌ | 中 | ✅✅✅ |
| 秒级停机 | ✅ | 低 | ✅ |
| firewalld | ❌ | — | ❌ |
| HTTP‑01 | ❌ | — | ❌ |
| DNS‑01 | ❌ | — | ❌ |
✅ 最终结论(工程级)
TLS‑ALPN‑01 强制 443 是安全边界,不是逻辑缺陷
IP 证书是高级能力,设计上刻意提高使用门槛
✅ stream 分流是唯一无停机解法
✅ 防火墙层无法替代
✅ IPv4 + IPv6 同证书完全支持
IDC头条