概述
前情提要:
这会是一篇系列文章,本想先从 SSL 基本概念科普讲起,结果坑越挖越大。为了不鸽太久,就先把最近刚更完的证书相关操作先来逐步填坑,等后续再反补科普向内容(但愿)。
本系列内容大致分为四篇:
- SSL 相关基础知识-科普向 (有生之年)
- acme.sh 快速入门-主要功能及申请SSL证书实践 (本篇)
- acme.sh 最佳实践-自动签发与自动部署群晖 DSM
- acme.sh 进阶版-多域名自动化管理
- acme.sh 阶段性终结-批量部署
背景
早年间,因 certbot-auto 是基于 Python 2 编写的,而 Python 2 即将寿终正寝,将 certbot-auto 迁移至 Python 3 需要大量工作且非常困难,因此团队决定放弃 certbot-auto 的维护。所以笔者在后续改用了 Let’s Encrypt 上首推的 ACME 客户端(Certbot)来实现自助申请、更新 SSL 证书。
但因为 Certbot 本身强依赖 Python 3 运行环境,导致并不是所有客户端都能完美适用。而接着这次破站证书即将到期,重新看了一下目前的 ACME 客户端,从中找到了一款同样是针对 ACME 协议编写的工具 – acme.sh。
acme.sh 是什么
acme.sh 是一个基于 Shell 脚本的开源工具,用于自动化管理和获取 SSL/TLS 证书。它提供了一种简单而灵活的方法来与 Let’s Encrypt 进行交互,并支持其他证书颁发机构(CA)。
使用对比
Certbot 和 acme.sh 都是用于自动化管理和获取 SSL/TLS 证书的工具,但它们在实现方式和功能上有一些区别。下面是它们之间的两个主要对比:
实现语言和依赖关系:
- Certbot 是使用 Python 编写的,因此在使用之前需要确保系统上已经安装了 Python 解释器和相关依赖库。这意味着您需要处理 Python 环境的配置和维护。
- acme.sh 是一个基于 Shell 脚本的工具,它在各种 Linux 系统上运行,并且没有其他依赖关系。由于使用 Shell 脚本,它通常更加轻量级和易于安装和使用。
接口和功能:
- Certbot 提供了一个命令行接口和一个简单的交互式界面,使得使用和管理证书变得更加方便。它有更多的配置选项和功能,例如支持多种 Web 服务器软件和自动配置文件更新。
- acme.sh 同样提供了命令行接口,并且通过简单的命令和选项可以执行证书管理任务。虽然它的功能相对较少,但是它具有可扩展性和自定义性,通过插件机制可以添加更多功能,例如 DNS 验证插件。
以下,将围绕 acme.sh 来做主要的功能介绍,让你对该工具有个初步认识。
快速入门
环境准备
在开始之前需要升级系统的 CA 证书,以避免后续在申请 SSL 证书时遇到问题。
CentOS
yum install -y ca-certificates
Debian
apt update ; apt install -y ca-certificates
安装 acme.sh
快速安装
curl https://get.acme.sh | sh -s email=nestealin@gmail.com
安装程序将执行以下 3 个操作:
- 创建并复制 acme.sh 到你的主目录(
$HOME
):~/.acme.sh/
。所有的证书也将放置在这个文件夹中。 - 创建别名,例如:设置
alias acme.sh=~/.acme.sh/acme.sh
。 - 创建每日定时任务来检查并更新证书(如果需要)。
注: acme.sh 工具的安装与执行操作均不需要 root 权限,允许在其他系统用户下直接安装与执行。
手动安装
虽然普通用户和 root 用户都可以安装使用,但在本例中将以 root 用户进行安装。
使用如下命令自动安装:
export ACME_HOME="/usr/local/acme.sh"
mkdir -p $ACME_HOME/data
cd /usrCertbotithubusercontent.com/acmesh-official/acme.sh/master/acme.sh
chmod +x acme.sh && ./acme.sh --install-online \
--home $ACME_HOME \
--cert-home $ACME_HOME/data \
--accountemail "nestealin@gmail.com" \
--nocron
ls ./acme.sh && rm -f ./acme.sh && cd $ACME_HOME && ls -l
安装过程输出:
[Sat Aug 17 00:39:43 CST 2024] It is recommended to install socat first.
[Sat Aug 17 00:39:43 CST 2024] We use socat for the standalone server, which is used for standalone mode.
[Sat Aug 17 00:39:43 CST 2024] If you don't want to use standalone mode, you may ignore this warning.
[Sat Aug 17 00:39:43 CST 2024] Installing to /usr/local/acme.sh
[Sat Aug 17 00:39:43 CST 2024] Installed to /usr/local/acme.sh/acme.sh
[Sat Aug 17 00:39:43 CST 2024] Installing alias to '/root/.bashrc'
[Sat Aug 17 00:39:43 CST 2024] Close and reopen your terminal to start using acme.sh
[Sat Aug 17 00:39:43 CST 2024] Installing alias to '/root/.cshrc'
[Sat Aug 17 00:39:43 CST 2024] Installing alias to '/root/.tcshrc'
[Sat Aug 17 00:39:43 CST 2024] bash has been found. Changing the shebang to use bash as preferred.
[Sat Aug 17 00:39:44 CST 2024] OK
如果一切顺利出现上述输出,则表示安装完成。
根据上述输出,安装过程中会进行如下操作:
- 创建并复制
acme.sh
到你显式指定的主目录(--home
):/usr/local/acme.sh/
,并且工具文件acme.sh
也放于此,包括所有的证书也将放置在这个文件夹中(--config-home
):/usr/local/acme.sh/data
。 - 创建别名,例如:在
/root/.bashrc
环境文件中设置. "/usr/local/acme.sh/acme.sh.env"
环境声明。 - 创建每日定时任务来检查并更新证书(本次安装已显式忽略)。
acme.sh.env 文件内容参考:
export LE_WORKING_DIR="/usr/local/acme.sh" alias acme.sh="/usr/local/acme.sh/acme.sh"
或参考官方默认安装 (方式二选一即可)
git clone --depth 1 https://github.com/acmesh-official/acme.sh.git
cd acme.sh
./acme.sh --install-online \
--home ~/myacme \
--config-home ~/myacme/data \
--cert-home ~/mycerts \
--accountemail "my@example.com" \
--accountkey ~/myaccount.key \
--accountconf ~/myaccount.conf \
--useragent "this is my client."
参数释义:
--home
: 表示安装主目录(默认含config-home
),用于安装acme.sh
。默认情况下,它安装在~/.acme.sh
。--config-home
: 证书配置文件目录,需要是一个当前用户可写的文件夹,acme.sh
会在这里写入所有文件(主要是证书/密钥的配置文件)。默认情况下,它位于--home
。--cert-home
: 用于保存你签发的证书 ( 如:/usr/local/acme.sh/data/
)。默认情况下,它保存在--config-home
。--accountkey
: 是保存你账户私钥的文件。默认情况下,它保存在--config-home
。--accountemail
: 用于在 Let’s Encrypt 注册账户的电子邮件地址,以便后续收到续期通知邮件。--useragent
: 是acme.sh
发送给 CA 时的 UA 值。--nocron
: 安装acme.sh
后不自动创建定时任务。
Crontab 示例:
0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null
再次提醒:
- acme.sh 的安装不需要 root 权限,安装过程不会污染已有的系统任何功能和文件,所有的修改都被限制在
~/.acme.sh/
中- 如果你想在安装的时候指定各种参数,也可以克隆 GitHub 仓库手动安装,更多详细参数可参考官方说明
申请证书 | 手动签发
使用 Let’s Encrypt 作为签发 CA
acme.sh --set-default-ca --server letsencrypt
申请证书
./acme.sh --issue \
-d "nestealin.com" \
-d "*.nestealin.com"
参数释义:
- 默认以第一个
-d
参数域名作为证书主域名,如有多个域名,则继续用-d
以 SAN 域名进行追加即可,各 CA 证书支持域名的数量上限详见: 申请限制; - 默认采用”DNS-01“方式验证;
- 默认采用”ec-256“ KeyLength 申请;如需特殊需求可用
--keylength
参数声明,如: 2048, 3072, 4096, 8192 or ec-256, ec-384, ec-521 ; - 如果需要使用网页验证,需要用
-w /html/path
方式声明;
提示: 后续步骤中的所有证书操作,均以”证书主域名”为准,如有 SAN 域名无需携带。
关于如何联动各 DNS API 实现自动签发,查看: 最佳实践
查看维护中的证书
acme.sh --list
移除证书 | 删除证书
通过如下命令只会移除本地证书文件,不会注销证书
acme.sh --remove -d nestealin.com
移除后提示:
[Sun Aug 18 00:58:18 CST 2024] The domain 'nestealin.com' seems to already have an ECC cert, let's use it.
[Sun Aug 18 00:58:18 CST 2024] nestealin.com has been removed. The key and cert files are in /usr/local/acme.sh/data/nestealin.com_ecc
[Sun Aug 18 00:58:18 CST 2024] You can remove them by yourself.
意味着管理脚本已经移除域名管控,但证书文件仍在本地保留,需要手动移除。
证书续期
默认情况下,acme.sh 会在安装后默认添加 crontab 定时任务,每 60 天执行一次自动续期。
当然也可以根据实际情况使用如下命令进行手动续期。
acme.sh --renew -d nestealin.com --force
对于 ECC 证书:
acme.sh --renew -d nestealin.com --force --ecc
吊销证书
如果确认证书不需要了,可以通过如下命令先向 CA 注销,然后再删除本地证书文件。
首先通过下列命令吊销证书:
acme.sh --revoke -d nestealin.com
出现Revoke success
说明吊销成功
此时,只是 CA 注销成功,但证书文件还残留在服务器上,需要通过下列命令移除:
acme.sh --remove -d nestealin.com
然后手动删除本地证书文件:
rm -rf /usr/local/acme.sh/data/nest申请限制期定时任务
```bash
/usr/local/acme.sh/acme.sh --cron --home /usr/local/acme.sh/
升级 acme.sh | 更新 acme.sh
目前由于 ACME 协议和各大 CA 都会频繁地更新, 因此 acme.sh 也要经常更新以保持同步。或在每次执行前手动更新一次。
升至最新版
升级 acme.sh 到最新版:
acme.sh --upgrade
开启自动升级
如果你不想手动升级, 可以开启自动升级:
acme.sh --upgrade --auto-upgrade
之后,acme.sh 就会自动保持更新了。
还原环境
同样地,如果遭遇环境破坏,还可以通过 --force --upgrade
的方式修复:
cd /usr/local/acme.sh
./acme.sh --force --upgrade --nocron --home /usr/local/acme.sh
ref: Synology NAS Guide · acmesh-official/acme.sh Wiki · GitHub
关闭自动升级
你也可以随时关闭自动更新:
acme.sh --upgrade --auto-upgrade 0
更换签发 CA
其实,acme.sh 脚本并不只是针对 Let’s Encrypt 单一 CA 进行证书申请,它还支持向其它 CA 申请,如: Buypass、ZeroSSL、SSL.com 和 Google Public CA,而 acme.sh 当前默认的 CA 是 ZeroSSL。
注:
- 本篇文章使用 Let’s Encrypt 来做讲解;
- 如果 CA 不变,则命令只需要执行一次,可以不必每次执行 acme.sh 前都进行
--set-default-ca --server xxx
;- Google Public CA 需要按照官方博客申请内测,然后获取 Key;
切换 Let’s Encrypt
acme.sh --set-default-ca --server letsencrypt
只需执行一次,切换后在下次执行 acme.sh --issue ...
时默认就会向 Let’s Encrypt 进行申请。
切换 Buypass
acme.sh --set-default-ca --server buypass
切换 ZeroSSL
acme.sh --set-default-ca --server zerossl
如果已有 ZeroSSL 帐号,可以在后台控制面板拿到 API Key,然后执行如下命令:
apt update
apt install -y jq
curl -s -X POST "https://api.zerossl.com/acme/eab-credentials?access_key=你的API_Key" | jq
输出内容如下:
{
"success": true,
"eab_kid": "kid字符串",
"eab_hmac_key": "hmac_key字符串",
}
然后手动添加帐号:
acme.sh --register-account --server zerossl \
--eab-kid kid字符串 \
--eab-hmac-key hmac_key字符串
切换 SSL.com
acme.sh --set-default-ca --server ssl.com
切换 Google Public CA
acme.sh --set-default-ca --server google
更换证书链
如果 ACME CA 提供多个证书链,可以使用 --preferred-chain
选择其中一个。 否则,它将获取默认链。
例如 Let’s Encrypt 在测试环境提供了两种证书链:
Name | Default |
---|---|
(STAGING) Pretend Pear X1 | No |
(STAGING) Bogus Broccoli X2 | Yes |
可以根据关键名称(Name)任选一种关键字输入:
acme.sh --issue -d example.com ..... --test --preferred-chain "(STAGING) Pretend Pear X1"
acme.sh --issue -d example.com ..... --test --preferred-chain "X1"
acme.sh --issue -d example.com ..... --test --preferred-chain "x1"
ref: Preferred Chain · acmesh-official/acme.sh Wiki · GitHub
注意事项
申请限制
使用 acme.sh 申请证书时,证书的数量受到 Let’s Encrypt 的速率限制的影响。以下是主要的限制信息:
- 每个注册域名的证书数量:每周最多可以为每个注册域名申请 50 份证书。例如,如果你的域名是
example.com
,那么在一周内,你最多可以申请 50 个不同的证书。 - 每个账户的订单限制:每个账户每三小时最多可以创建 300 份新订单。每次申请证书时,都会生成一个新订单。
- 每份证书的域名数量:每份证书最多可以包含 100 个域名。这意味着你可以在一张证书中为多个域名申请 SSL/TLS 证书。
- 续期证书的特殊规则:续期证书不计入每个注册域名的证书数量限制,但每周最多只能续期 5 张重复证书。如果你尝试续期的证书与已有证书的域名列表完全相同,就会触发限制。
目前支持的 CA 厂商特性如下:
CA 厂商 | 证书有效期 | ECC 证书 | 域名数量 | 支持通配符 | NotAfter | 国际化域名 |
---|---|---|---|---|---|---|
Let’s Encrypt | 90 | 是 | 100 | 是 | 否 | 是 |
ZeroSSL | 90 | 是 | 100 | 是 | 是 | 是 |
90 | 是 | 100 | 是 | 是 | 否 | |
Buypass | 180 | 是 | 5 | 付费 | 否 | 是 |
SSL.com | 90 | 是 | 2 | 付费 | 否 | 是 |
小提示: 如果证书域名触发了申请上限,可以尝试增删或更改证书 SAN 域名进行规避。
注: 因为 SSL 证书签发限制只以域名组合(主域名 + SAN 域名)作为唯一判断,例如 SAN 域名的增删都会被认为是一个新的域名组合。
环境变量
- 如果准备使用脚本二次封装执行,请勿使用
CERT_PATH
和SSL_CERT_FILE
否则可能引起各种问题;
后记
1. 关于域名验证方式
通常情况下,可以支持两种域名验证方式:
- 端口验证,有且只能使用 80、443 端口访问测试;
- TXT 记录验证,通过添加指定域名的 TXT 记录验证归属;
此外,还支持一种隐式验证,即 DNS 别名模式,详情查看: DNS alias mode · acmesh-official/acme.sh Wiki · GitHub
2. 修改 acme.sh 安装目录 | config-home 路径修改
假设初始安装 acme.sh 的 config-home 目录位于 /opt/acme
目录,即执行 acme.sh 的执行脚本位于 /opt/acme/acme.sh
现在想将 config-home 路径修改至 /usr/local/acme.sh
可以通过如下方式修改。
修改流程
迁移目录:
mv /opt/acme /usr/local/acme.sh
修改环境变量:
vim ~/.bashrc
vim ~/.cshrc
vim ~/.tcshrc
一共需要修改上述三个环境变量文件
修改内容前后对照:
# ~/.bashrc
- . "/opt/acme/acme.sh.env"
+ . "/usr/local/acme.sh/acme.sh.env"
# ~/.cshrc
- source "/opt/acme/acme.sh.csh"
+ source "/usr/local/acme.sh/acme.sh.csh"
# ~/.tcshrc
- source "/opt/acme/acme.sh.csh"
+ source "/usr/local/acme.sh/acme.sh.csh"
原理说明
- 例如在 bash shell 下时,登陆时默认加载
~/.bashrc
文件 - 在
~/.bashrc
文件内默认加载/usr/local/acme.sh/acme.sh.env
环境变量 /usr/local/acme.sh/acme.sh.env
环境变量默认声明export LE_WORKING_DIR="/usr/local/acme.sh"
注:
- 如果 config-home 与 home 有单独拆分路径,可能还会有
export LE_CONFIG_HOME="/usr/local/acme.sh/data"
单独的环境变量。- ref: GitHub - acme.sh | 4-updating-cert
3. 自动 DNS API 简称及变量速查
服务商名称 | 服务商简称 | 所需 API 参数 | 获取 API 参数地址 |
---|---|---|---|
cloudxns | cx | export CX_Key="123456" export CX_Secret="abcdef" |
点击访问 |
dnspod.cn | dp | export DP_Id="123456" export DP_Key="abcdef" |
点击访问 |
aliyun | ali | export Ali_Key="123456" export Ali_Secret="abcdef" |
点击访问 |
cloudflare | cf | export CF_Key="123456" export CF_Email="abc@example.com" |
点击访问 |
linode | linode | export LINODE_API_KEY="123456" |
点击访问 |
he | he | export HE_Username="username" export HE_Password="password" |
he的用户名密码 |
digitalocean | dgon | export DO_API_KEY="123456" |
点击访问 |
namesilo | namesilo | export Namesilo_Key="123456" |
点击访问 |
aws | aws | export AWS_ACCESS_KEY_ID=123456 export AWS_SECRET_ACCESS_KEY=abcdef |
点击访问 |
namecom | namecom | export Namecom_Username="username" export Namecom_Token="123456" |
点击访问 |
freedns | freedns | export FREEDNS_User="username" export FREEDNS_Password="password" |
freedns的用户名密码 |
godaddy | gd | export GD_Key="123456" export GD_Secret="abcdef" |
点击访问 |
yandex | yandex | export PDD_Token="abcdef" |
点击访问 |
更多 dnsapi 的使用,可以查看文档。
4. 转换为p12
证书
虽然acme.sh
生成的证书可以直接配置到 Nginx、Apache 等等 Web 服务器使用,不过有少数时候如果说想直接配置证书到 Tomcat 中(Spring Boot)那就需要将证书转换成p12
格式了!
生成证书后,将证书文件和证书密钥文件复制出来,通过openssl
命令转换:
openssl pkcs12 -export -in "证书文件路径" -inkey "证书密钥文件路径" -out "指定生成的p12证书文件路径"
执行命令会让你设定p12
证书的密码,自行设定即可。
然后在 Spring Boot 配置文件配置如下:
# SSL证书设置
server.ssl.key-store=证书p12文件所在位置
server.ssl.key-store-password=证书密码
server.ssl.keyStoreType=PKCS12
server.ssl.key-store
配置项需要以classpath:
或者file:
开头,一般classpath:
开头的表示jar
包内路径,Maven 项目中src\main\resources
文件夹即可对应为classpath
的根目录,而file:
对应的是jar
包外相对路径或者绝对路径。
# classpath路径
server.ssl.key-store=classpath:ssl.p12
# jar包外路径
server.ssl.key-store=file:ssl/ssl.p12
5. 创建腾讯云 DNSPod Token
DNSPod - 账号中心 | API 密钥 ,在API秘钥 -> DNSPod Token
下创建一个秘钥
接着复制 ID 和 Token,执行如下命令将其配置到环境变量中:
export DP_Id="<DNSPod_Token_ID>"
export DP_Key="<DNSPod_Token>"
6. 创建阿里云 Key 和 Secret
阿里云 - RAM 访问控制 | AccessKey,点击”创建 AccessKey“
接着复制 AccessKey ID 和 Secret,执行如下命令将其配置到环境变量中:
export Ali_Key="<AccessKey_ID>"
export Ali_Secret="<AccessKey_Secret>"