内网服务器若完全无法访问外网,无法直接运行 ACME 客户端,需通过外部机器配合 DNS 验证完成续签,核心是分离密钥与验证过程。
先说结论:DNS-01 验证解决入站限制,但 ACME 协议仍需出站访问 CA,完全隔离时需借用外网机器代理申请。
- 适合:无法开放 80/443 端口且无出站 HTTPS 权限的严格内网环境
- 关键安全点:私钥必须在内网生成,外网仅处理 CSR 与验证
- 先准备:域名 DNS 管理 API 权限,以及内外网文件传输通道
- 验收:使用 OpenSSL 检查证书有效期及链完整性
命令与配置准备
在外网机器上配置 DNS Provider 的 API Token,以 Cloudflare 为例,建议通过环境变量传递,避免明文留在脚本中:
export CF_Token="your_cloudflare_api_token" export CF_Account_ID="your_account_id"
安全提示:API Token 仅需授予编辑特定域名 DNS 记录的权限,切勿使用全局 API Key。
若使用 acme.sh 配合 DNS API 申请(需提前配置 DNS Provider 的 Token):
acme.sh `--issue` `--csr` /path/to/csr.csr `--dns` dns_cf
若使用 Certbot 手动模式,需在外网机器运行(不适合自动化):
certbot certonly `--manual` `--preferred-challenges` dns -d example.com
原理简述
默认 HTTP-01 验证要求 Let's Encrypt 服务器能访问你的 80 端口,内网环境通常无法满足。DNS-01 验证改为要求你在域名解析中添加特定 TXT 记录,CA 通过查询 DNS 来确认所有权,不依赖服务器 inbound 连接。但 ACME 协议本身要求客户端与 CA 进行 API 通信,因此客户端仍需出站权限,若内网服务器无出站权限,必须在外网机器执行客户端逻辑。
分步处理
1. 确认网络策略:检查内网服务器是否能访问 acme-v02.api.letsencrypt.org。若不能,准备一台可上网的外部机器。
2. 生成私钥与 CSR(推荐内网完成):在内网服务器生成私钥和证书签名请求,确保私钥不出内网。
openssl req -new -newkey rsa:2048 -nodes -keyout private.key -out csr.csr -subj "/CN=example.com"
3. 执行验证:
- 将 csr.csr 复制到外网机器。
- 在外网机器使用 ACME 客户端加载 CSR 并完成 DNS 验证(推荐配置 DNS API 实现自动化)。
- 验证通过后,外网机器生成证书文件。
4. 部署证书:仅将公钥证书(fullchain.pem)传回内网,与内网生成的 private.key 配合配置到 Web 服务器。
怎么验证是否生效
使用以下命令检查证书有效期:
openssl x509 -noout -dates -in /path/to/fullchain.pem
检查证书主题是否正确:
openssl x509 -noout -subject -in /path/to/fullchain.pem
检查 Web 服务配置是否正确加载:
nginx -t # 或 apache2ctl configtest
重启服务后,通过浏览器或 curl 访问域名,查看证书详情。
常见坑
- DNS 传播延迟:添加 TXT 记录后需等待生效,过早验证会失败,API 模式通常会自动等待。
- 私钥安全:若在外网机器运行 ACME 客户端,尽量避免私钥离开内网,采用 CSR 模式更安全。
- API 密钥泄露:外网机器操作完成后,建议清除历史命令中的敏感环境变量。
- 自动化困难:手动 DNS 模式无法自动化续签,建议使用 DNS API 插件。
- CAA 记录:检查域名 DNS 是否设置了 CAA 记录限制颁发机构。
参考来源
- Let's Encrypt Official Documentation, "DNS Challenge", letsencrypt.org
- Certbot Documentation, "Getting certificates", certbot.eff.org
- ACME Protocol RFC 8555, IETF