引言
acme.sh 是一个完全用纯 Shell (Unix shell语言) 编写的开源脚本工具。它实现了 ACME 协议(一种用于自动化获取和管理 SSL 证书的互联网协议),充当了用户与证书颁发机构(CA)之间的“自动化中介”。
简单来说,它把原本繁琐、需要人工干预的 SSL 证书申请和部署过程,变成了一键自动化操作。它的核心作用包括:
- 自动申请免费证书:能够自动向 Let's Encrypt、ZeroSSL 等受信任的证书颁发机构申请免费的 SSL/TLS 证书。
- 自动化域名验证:支持 HTTP 验证和 DNS API 验证。通过对接阿里云、腾讯云、Cloudflare 等 100 多家 DNS 服务商的 API,它可以自动添加 TXT 记录来完成域名所有权验证,无需手动操作。
- 自动部署与平滑重载:申请成功后,它能自动将证书复制到 Nginx 或 Apache 等 Web 服务器的指定目录,并自动执行重载命令(如
nginx -s reload),实现零停机更新。 - 自动续期(核心优势):安装后会自动创建 Cron 定时任务,默认每 60 天检查一次证书状态。在证书到期前,它会自动完成续签、部署和重载的全套流程,彻底解放运维人员的双手。
- 支持泛域名证书:只需一条简单的命令,就能为
*.example.com及其所有子域名签发一张通配符证书,极大地简化了多子域名站点的管理。 - 轻量且零依赖:由于是纯 Shell 编写,它不需要 Python 等复杂的运行环境,不污染系统文件,几乎可以在所有主流的 Unix-like 系统(Linux、macOS 等)上直接运行。
产品对比
| 特性 | certd | certbot | acme.sh |
|---|---|---|---|
| 定位 | 国产证书自动化管理平台 | Let's Encrypt 官方客户端 | 社区流行的轻量 Shell 脚本 |
| 协议支持 | ACME v2 | ACME v2 | ACME v2 |
| 证书来源 | Let's Encrypt、ZeroSSL、Google Trust 等 | Let's Encrypt(官方) | Let's Encrypt、ZeroSSL、Google Trust、Buypass 等 |
| 部署方式 | 独立服务 + Web UI | 命令行工具 | 纯 Shell 脚本 |
| 自动续期 | ✅ 内置定时任务 | ✅ 需配置 cron/systemd | ✅ 内置 cron 自动安装 |
| 多服务器部署 | ✅ 原生支持 | ❌ 需手动复制或配合其他工具 | ❌ 需手动复制或 rsync |
| Web 管理界面 | ✅ 有 | ❌ 无 | ❌ 无 |
| 证书监控/告警 | ✅ 到期提醒、失败告警 | ❌ 无 | ❌ 无 |
| Nginx/Apache 自动配置 | ⚠️ 需手动配置或配合 | ✅ 自动修改配置 | ❌ 手动配置 |
| 安装复杂度 | 中等(需部署服务) | 低(包管理器安装) | 低(curl 一键安装) |
| 依赖 | Java/Node + 数据库 | Python + 依赖包 | 纯 Shell,几乎零依赖 |
| 适合场景 | 企业级、多服务器、集中管理 | 个人/单服务器、快速上手 | 轻量环境、Docker、嵌入式 |
一、 执行步骤
第一步:安全备份(回退保障)
在修改任何配置前,先备份现有的证书文件和 Nginx 配置文件(以编译安装的为例,通过系统包管理工具安装的目录可能不一致!)。如果后续任何环节出错,直接还原即可。
# 备份当前可用的 SSL 证书文件(如果不可用了可以忽略)
cp /usr/local/nginx/conf/ssl/XXX.pem /usr/local/nginx/conf/ssl/XXX.pem.bak
cp /usr/local/nginx/conf/ssl/XXX.key /usr/local/nginx/conf/ssl/XXX.key.bak
# 备份 Nginx 配置文件(主配置)
cp /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx.conf.bak
# 如果是单独配置
cp /usr/local/nginx/conf/vhost/XXXX.conf /usr/local/nginx/conf/vhost/XXXX.conf.bak
第二步:安装与配置 acme.sh
# 安装 acme.sh(替换为你的真实邮箱)
curl https://get.acme.sh | sh -s email=your_email@example.com
source ~/.bashrc
# 切换默认 CA 为 Let's Encrypt
acme.sh --set-default-ca --server letsencrypt
# 开启 acme.sh 自动升级
acme.sh --upgrade --auto-upgrade
如果是国内的服务器或者主机,请使用gitee
git clone https://gitee.com/neilpang/acme.sh.git
cd acme.sh
./acme.sh --install -m 你的邮箱
第三步:配置 DNS API 并申请证书
# 配置阿里云 DNS API 环境变量
export Ali_Key="你的AccessKey_ID"
export Ali_Secret="你的AccessKey_Secret"
# 签发证书(包含主域名和泛域名)
acme.sh --issue --dns dns_ali -d XXX.com -d '*.XXX.com'
第四步:安装证书与平滑重载
将证书复制到 Nginx 目录并绑定重载命令:
acme.sh --install-cert -d XXX.com \
--key-file /usr/local/nginx/conf/ssl/XXX.com.key \
--fullchain-file /usr/local/nginx/conf/ssl/XXX.com.pem \
--reloadcmd "/usr/local/nginx/sbin/nginx -s reload"
检查 Nginx 配置语法并平滑重载:
/usr/local/nginx/sbin/nginx -t
/usr/local/nginx/sbin/nginx -s reload
第五步:配置定时任务(自动续期)
acme.sh 在安装时会自动创建 Cron 定时任务,但生产环境必须手动验证其是否生效,防止因系统更新或权限问题导致续期失败。
- 检查现有 Cron 任务
crontab -l | grep acme.sh
正常输出应包含类似以下内容:
0 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
- 若任务缺失,手动添加
acme.sh --cron --install-cronjob
- 验证续期逻辑(Dry Run)
无需等待证书到期,可强制触发一次续期检查,确认 DNS API 调用及 Nginx Reload 是否正常:
acme.sh --cron --force
注意:
--force会强制触发续期流程,即使证书未到期。生产环境建议仅在首次配置后验证一次,避免频繁触发 Let's Encrypt 的 Rate Limit。
二、 🚨避坑指南
DNS API 的 AccessKey ?
为了生产环境安全,必须在 RAM 访问控制中创建专属子用户(如 acme-bot),仅授予 AliyunDNSFullAccess 权限。这样即使服务器被攻破,黑客也只能操作 DNS,无法动你的 ECS 或数据库。
为什么不能直接用 acme.sh 的默认目录?
acme.sh 默认的证书目录(~/.acme.sh/)是工具内部工作目录。必须通过 --install-cert 命令将证书复制到 Nginx 的固定目录中,并配合 --reloadcmd 参数,这样未来的自动续期才能正确触发 Nginx 重载。
常见错误 1:ERR_SSL_VERSION_OR_CIPHER_MISMATCH
现象:Nginx reload 成功,但浏览器访问提示“您的连接不是私密连接”或 ERR_SSL_VERSION_OR_CIPHER_MISMATCH。
原因分析:旧版 Nginx 配置中包含了 RC4-SHA 等已被现代浏览器彻底废弃的弱加密算法。
解决方案:替换 ssl_ciphers 为现代浏览器支持的强加密套件。
# 错误配置示例
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:...:RC4-SHA:...;
# 正确配置示例
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
常见错误 2:ECC 证书验证命令误用
现象:使用 openssl ec -noout -modulus 验证私钥时报错 unknown option -modulus,或者输出的 MD5 值为空字符串的哈希值(d41d8cd98f00b204e9800998ecf8427e)。
原因分析:-modulus 仅适用于 RSA 密钥。acme.sh 默认生成的 ECC 私钥为 PKCS#8 格式,不支持该参数。
解决方案:使用通用的 pkey 命令提取公钥进行比对。
# 验证证书公钥
openssl x509 -noout -pubkey -in /path/to/fullchain.cer | openssl md5
# 验证私钥公钥(注意使用 pkey)
openssl pkey -noout -pubout -in /path/to/private.key | openssl md5
注:两个命令输出的 MD5 值必须完全一致,否则证书与私钥不匹配。
常见错误 3:ERR_CERT_COMMON_NAME_INVALID
现象:访问 XXX.com 时,浏览器提示证书通用名称无效。点击证书发现仅包含 XXX.com。
原因分析:申请证书时仅指定了 -d XXX.com,未包含 www 子域名。现代浏览器要求 SAN(使用者可选名称)必须精确匹配访问的域名。
解决方案:强制重新签发包含所有域名的证书。
acme.sh --force --issue --dns dns_ali -d XXX.com -d XXX.com
注:如果希望一劳永逸,建议直接申请泛域名证书 -d '*.XXX.com'。
常见错误 4:签发时报错 curl error code: 60
现象:执行 acme.sh --issue 时,提示 Cannot init API for: https://acme-v02.api.letsencrypt.org/directory,且伴随 CURLE_PEER_FAILED_VERIFICATION (60) 错误。
原因分析:服务器的 CA 证书包过期或缺失,导致本地 curl 无法验证 Let's Encrypt 官方服务器的 SSL 证书信任链。
解决方案:更新系统 CA 证书包。
# CentOS / Alibaba Cloud Linux
yum update -y ca-certificates
# Ubuntu / Debian
apt-get update && apt-get install --reinstall ca-certificates
更新后,使用 curl -v https://acme-v02.api.letsencrypt.org/directory 验证能正常返回 JSON 数据即可重新签发。
三、 任务总结
- 安全性提升:全程通过备份机制保障生产环境可回退;使用 RAM 子用户保护云账号安全。
- 运维效率提升:彻底告别手动续签,
acme.sh的 crontab 任务会在证书到期前 30 天自动续期并平滑重载 Nginx,实现真正的“一次配置,永久免维护”。 - 可观测性增强:通过第五步的 Dry Run 验证,确保自动续期链路在首次配置时即通过验证,避免“证书到期才发现 Cron 失效”的生产事故。