3. ACME客户端选型与部署

ACME协议本身只是一套规范,真正与Let's Encrypt、ZeroSSL、Buypass等CA交互、完成账户注册、订单创建、挑战应答、证书签发与续期的,是运行在服务器上的ACME客户端。选错客户端,往往意味着后续运维要长期忍受依赖冲突、续期失败、配置难以自动化等问题。因此,客户端选型不是"随便装一个能签证书就行",而是要根据运行环境、自动化程度、是否已有Web服务器、是否需要DNS验证、团队技术栈等因素综合判断。

四类主流客户端的定位差异

目前生产环境中最常见的四类客户端,可以按"设计哲学"分成两组:一组是通用型命令行客户端,包括acme.sh、certbot、lego,它们只负责把证书签下来,如何部署、如何重载服务由使用者自己决定;另一组是集成型Web服务器,以Caddy为代表,它把ACME客户端内嵌进服务器本身,证书申请、续期、加载全自动完成,用户几乎不需要感知ACME的存在。

acme.sh是一个纯Shell脚本实现的客户端,最大特点是零依赖——只要有sh、curl或wget、openssl就能跑,不依赖Python、Go等运行时。它默认使用ZeroSSL作为CA,也支持切换到Let's Encrypt。acme.sh支持HTTP-01、DNS-01、TLS-ALPN-01三种挑战,尤其DNS-01配合各大DNS服务商的API可以实现通配符证书的全自动签发,这是它被大量用于内网、无公网80端口场景的核心原因。安装方式极简:

curl https://get.acme.sh | sh -s email=ops@example.com
# 或
git clone https://github.com/acmesh-official/acme.sh.git
cd acme.sh && ./acme.sh --install -m ops@example.com

安装后会在用户目录下生成~/.acme.sh,并自动注册一个cron任务用于续期。它的续期不是靠systemd timer,而是靠cron调用自身,因此对系统服务管理方式不敏感,在容器、精简系统、老式发行版上都能工作。

certbot由EFF主导开发,是Let's Encrypt官方早期推荐的客户端,Python实现,生态最成熟。它的优势在于与Nginx、Apache的深度集成:certbot --nginx可以自动读取现有server块、插入证书路径、配置跳转,甚至自动reload。对于已经用Nginx/Apache托管大量站点的团队,certbot的插件模式能显著降低手工改配置的成本。安装通常通过系统包管理器:

# Debian/Ubuntu
apt install certbot python3-certbot-nginx
# RHEL/CentOS
dnf install certbot python3-certbot-nginx

但certbot的Python依赖较重,在最小化容器镜像中安装体积较大,且不同发行版打包的版本差异明显,插件行为可能不一致。它更适合"传统虚拟机+系统包管理"的运维模式。

lego是Go语言编写的单二进制客户端,编译后无运行时依赖,跨平台分发方便。它同时支持ACME v2和多种DNS服务商,命令行参数清晰,也常被当作库嵌入其他Go程序。安装只需下载对应平台的二进制:

# 以Linux amd64为例
curl -L https://github.com/go-acme/lego/releases/latest/download/lego_v4_linux_amd64.tar.gz | tar xz
mv lego /usr/local/bin/

lego适合Kubernetes环境、CI流水线、以及需要把证书签发逻辑写进自有程序的场景。它本身不管理续期调度,需要配合cron或K8s CronJob。

Caddy则完全不同。它是一个用Go写的Web服务器,内置ACME客户端,只要域名解析正确、80/443可达,启动时就会自动申请证书并启用HTTPS。配置文件中只需写域名,不需要写证书路径:

example.com {
    root * /var/www/html
    file_server
}

Caddy会自动完成HTTP-01或TLS-ALPN-01挑战、证书存储、到期前续期和热加载。它适合新项目、反向代理、内部工具站点,但不适合"已有Nginx且不想更换"的存量环境。

选型判断:从运行环境反推客户端

与其背客户端的特性表,不如从实际约束出发做判断。下面这张图把常见约束与推荐客户端对应起来,可作为选型时的快速参照。

ACME客户端选型决策路径 从运行环境与验证方式出发,逐层收敛到合适客户端 需要签发/续期证书 是否愿意更换/新建 Web服务器? 是 Caddy 内置ACME,全自动 否 已有Nginx/Apache 且用系统包管理? 是 certbot 插件自动改配置 否 是否需要DNS-01 或通配符证书? 是 acme.sh 零依赖,DNS API丰富 否 lego 单二进制,适合容器/CI 补充:容器/K8s 优先 lego 或 acme.sh;极简系统优先 acme.sh;新项目直接上 Caddy 最省心

安装方式与运行环境对照

把四类客户端的安装与运行特征放在一起对比,能更直观地看出各自适合的土壤。

客户端 实现语言 典型安装方式 运行依赖 续期调度 适用场景
acme.sh Shell curl脚本 / git clone sh、curl/wget、openssl 内置cron 精简系统、容器、DNS-01、通配符
certbot Python apt/dnf/pip Python3及插件包 systemd timer / cron 已有Nginx/Apache的虚拟机
lego Go 二进制下载 / 包管理 无(静态二进制) 外部cron / K8s CronJob K8s、CI、嵌入式调用
Caddy Go 二进制 / 包管理 / 容器 无(静态二进制) 内置自动续期 新站点、反向代理、全自动HTTPS

部署时的关键注意点

无论选哪个客户端,有几条工程经验值得提前确认。第一,续期调度必须可观测。acme.sh的cron、certbot的systemd timer、lego的外部cron,都可能因为环境变量缺失、路径变化而静默失败。建议在续期命令后接一个通知钩子,或定期检查证书剩余有效期。第二,证书私钥权限要收紧。acme.sh默认把私钥放在~/.acme.sh下,certbot放在/etc/letsencrypt下,部署到Web服务器目录时应确保只有服务账户可读。第三,CA选择要明确。acme.sh默认ZeroSSL,certbot默认Let's Encrypt,lego和Caddy也各有默认值,生产环境应显式指定CA,避免因默认值变化导致行为漂移。第四,测试环境先用staging。Let's Encrypt和ZeroSSL都提供staging端点,调试阶段用staging可以避免触发生产环境的速率限制。

如果团队已经在用Ansible、Terraform等IaC工具,优先选择有成熟模块或Provider的客户端:certbot有社区role,acme.sh有官方Ansible模块,lego常被写进自定义脚本,Caddy则可通过配置文件整体纳入版本管理。

选型的本质是匹配约束,而不是追求"最强"。一个只有80/443可达、跑着Nginx的虚拟机,用certbot最省事;一个K8s集群里需要为Ingress自动签发通配符证书,lego或acme.sh配合DNS API更合适;一个全新项目从零开始,直接上Caddy可以把证书这件事彻底交给服务器。理解每个客户端的设计取舍,才能在具体环境中做出不后悔的选择。