使用acme.sh给Web网站自动安装免费的SSL证书并自动更新,轻松实现HTTPS

本文最后更新于 2024年5月17日 凌晨

出于安全考虑,现在大部分的网站都已经配置了SSL证书,直观的感觉就是现在大部分的网站都是HTTPS,而不是HTTP了。甚至,Chrome 从90版本开始,就已经是默认请求HTTPS:

Chromium系请求页面

那么,我们个人在部署网站的时候,如何部署SSL,快速实现HTTPS呢?方法很多,个人觉得,使用acme.sh是一个很不错的工具。

acme.sh

acme.sh是一个用纯Shell(Unix shell语言)写成的ACME协议客户端,作为对比,acme协议是Let’s Encrypt和其他CA机构使用的一种网络交互协议,用于自动验证网站/域名并颁发SSL/TLS证书。
acme.sh就是利用这个acme协议,使用单一的Shell脚本自动为网站颁发和续订SSL证书。主要特点和功能包括:

  • 纯Shell语言实现,无其他依赖,很容易安装和使用。
  • 支持多种操作系统,如Linux、macOS、Windows(需要Cygwin)等。
  • 支持www模式、独立模式、DNS模式来验证域名。可以自动通过API完成DNS记录验证。
  • 可以为单域名或泛域名(通配符)颁发证书。支持SAN证书。
  • 可以直接安装颁发的证书到Nginx或Apache服务器。
  • 默认每60天自动检查并自动续订证书。
  • 支持ECC加密算法颁发的证书。

上手非常简单,如果你的Linux命令熟练几分钟即可上手使用。
目前支持的CA机构和CA机构支持的功能:

CA 最大有效期(日) ECC加密 域名数量限制 泛域名 IPv4 IPv6 过期日 IDN
Let’s Encrypt 90 支持 100 支持 不支持 不支持 不支持 支持
ZeroSSL 90 支持 100 支持 不支持 不支持 支持 支持
Google 90 支持 100 支持 不支持 不支持 支持 不支持
Buypass 180 支持 5 付费 不支持 不支持 不支持 支持
SSL.com 90 支持 2 付费 不支持 不支持 不支持 支持

acme.sh 主要有部署SSL的方案非常多,不过我个人主要使用两种方案,也是我推荐的两种方案:

  • Web服务器验证: 使用Nginx验证网站的归属,实现证书签发验证。
  • DNS API验证: 使用DNS厂商的API,自动校验域名归属,实现证书签发和续签。

如何安装

如何安装acme.sh呢?一条命令即可:

1
curl https://get.acme.sh | sh -s email=my@example.com # 换成自己的邮箱(最好是ZeroSSL账号)

国内服务器如果无法访问,可以克隆仓库后手动安装:

1
2
3
4
5
6
# 克隆仓库
git clone https://github.com/acmesh-official/acme.sh.git
# 进入仓库内
cd ./acme.sh
# 运行脚本
./acme.sh --install -m my@example.com # 换成自己的邮箱(最好是ZeroSSL账号)

为什么最好使用ZeroSSL的账号邮箱呢?很早之前,ZeroSSL就买了acme.sh这个网站,所以,后来amce.sh切换默认的CA为ZeroSSL也是很正常的啦。而ZeroSSL申请SSL,需要预留邮箱。

安装成功:

安装成功

之后,我们使用acme.sh -v,就可以看到acme.sh的版本号:

查看版本号

如果acme.sh没有添加到环境变量内,可以进行手动添加:
我手动添加的环境变量

常用命令

在教程开始之前,我们看看acme.sh的基础命令:

1
acme.sh -h

查看帮助

而查看帮助内,比较常用的是:

1
acme.sh --list

查看acme.sh管理的域名

一些情况下,acme.sh可能会报错,这个时候,优先尝试升级acme.sh:

1
2
3
4
5
6
# 升级acme.sh
acme.sh --upgrade
# 开启acme.sh的自动升级
acme.sh --upgrade --auto-upgrade
# 关闭acme.sh的自动升级
acme.sh --upgrade --auto-upgrade 0

更新acme.sh

其次是默认使用ZeroSSL,如果你想切换默认的CA,可以:

1
2
# 切换默认的CA机构为letsencrypt
acme.sh --set-default-ca --server letsencrypt

切换默认CA

除了默认的letsencrypt(Let’s Encrypt)和zerossl(ZeroSSL),默认配置下,还支持的CA选项:buypass(Buypass)、ssl.com(SSL.com)和google(Google Public CA)。
同时,如果你申请了很多域名,但是其中有的域名已经不再使用,需要进行删除操作时,可以使用命令:

1
acme.sh --remove -d example.com

删除不需要的域名

接下来,我们看看如何使用acme.sh申请SSL证书。

支持创作

书写文章不易,如果热心的小伙伴,想支持创作,可以加入我们的「爱发电」电圈(还可以解锁远程协助、好友位😃):

当然,也欢迎在B站或YouTube上关注我们:

更多:

ZeroSSL

acme.sh在3.0开始,默认使用ZeroSSL:https://zerossl.com/

其实和原本的Let’s Encrypt差不多,ZeroSSL有一个可视化的界面,还是很不错的,可以直观查看SSL是否续期成功;但是有点尴尬的是,我绑定了多个通配域名后,ZeroSSL的控制台上,还是空空如也,可能ZeroSSL的控制台目前还不支持acme.sh的通配符展示(也可能是我部署的时候,ZeroSSL的服务器宕机了):

ZeroSSL的后台

Uptime检测ZeroSSL的服务器状态

不过,不影响我们的使用,你还是可以用acme.sh --list看看Linux服务器上SSL证书的续期情况。

如果你在使用acme.sh的时候,在已经有ZerorSSL的情况下,可以直接进行绑定:

1
acme.sh  --register-account -m 「ZeroSSL邮箱」 --server zerossl

acme.sh登录

当然,你也可以直接使用ZeroSSL的EAB:

1
2
3
acme.sh  --register-account  --server zerossl \
--eab-kid "你账号的kid" \
--eab-hmac-key "你账号的key"

使用EAB进行登录

其中的kidkey可以在ZeroSSL的这里进行获取:

获取EAB

如果你不想使用ZeroSSL,可以切换为letsencrypt

方式对比

acme.sh支持多种方法进行部署,不过常用的是两种:

  • HTTP验证: 通过在Web服务器上创建临时文件,让ACME服务器验证域名的控制权。acme.sh可以自动配置常见的Web服务器,如Apache和Nginx,以便进行HTTP验证。我们也可以使用acme.sh提供的命令来生成临时文件并在验证完成后进行清理。
  • DNS验证: 通过在DNS服务器上添加相应的DNS TXT记录来验证域名所有权。acme.sh官方提供了多个DNS服务提供商的支持,包括Cloudflare、GoDaddy、Aliyun等。其他第三方DNS也可以根据ACME协议进行对接,不过大部分的DNS服务厂商,其实acme.sh都已经支持了。

两个方法都是很快速的方法,从结果上出发,最的区别就是DNS验证可以签署通配域名,也就是签署顶级二级域名后,其顶级二级域名分割出的三级域名都可以使用这个证书。

举个例子: 你为顶级二级域名example.com签署通配域名*.example.com,那么a.example.comb.example.com都可以使用这个通配域名证书。但是,使用acme.sh签署通配域名证书,只能使用DNS验证的方式进行签署。

有点复杂

接下来,我们就来分别演示。

HTTP验证签署

acme.sh 其实可以自动HTTP验证。也就是你的Web服务器已经配置的情况下,你可以选择acme.sh自动修改Web配置并验证;也可以手动自动配置,并使用acme.sh进行配置的验证。
本次使用Nginx服务器为例,Apache其实也差不多。

自动配置签署

自动部署配置,其实就是自动配置Web服务器(Nginx、Apache)的配置,创建验证文件;在验证成功后,下载并配置好SSL证书,需要手动配置HTTPS

首先,我们需要确保Nginx已经配置到环境变量内:

1
2
# 查看Nginx的版本
nginx -v

已经安装了nginx,并且配置到环境变量

在配置之前,确保Nginx当前的配置是可用的状态:

1
2
3
$ nginx -t
nginx: the configuration file /usr/local/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/nginx.conf test is successful

按道理,就可以使用官方的命令进行安装:

1
acme.sh  --issue  -d example.com  --nginx

acme.sh自动配置

配置完成:

配置完成

配置的原理是怎么样呢?

首先,acme.sh会检测Nginx的配置文件地址。如果你的Nginx使用的是主config文件,引入子config(也就是:include /www/wwwConf/*.conf这样的形式)也是支持的;根据你指定的域名,找到具体的server配置片段所在的文件,将其备份。

之后,修改server配置片段,主要的修改内容就是后续我们手动的配置;修改配置后,acme.sh会使用Nginx的nginx -t检测配置的合法性,如果不合法,那么直接恢复备份终止脚本。

最后,在server修改完成,并和CA校验成功后;签署SSL证书并还原Nginx备份。之后如果是acme.sh的续签,也是重新运行此过程。

graph TD

A[临时修改Nginx配置] --> B[向CA机构申请证书]  
B --> C[生成证书]
C --> D[重置Nginx配置为原始版本] 
D --> E[证书生成完成]

E --> F[定期证书续期检查]
F --> |证书尚未到期| E
F --> |证书即将到期| A

那么如果你不想让acme.sh自动配置呢?我们可以自己配置。

手动配置签署

手动配置的灵活性更高;自动配置签署和手动配置签署二选一就可以了。

手动配置,就是在Nginx上配置一个目录,使其acme.sh在Linux的服务器内写入CA机构下发的验证文件,通过Http可以访问到。

首先,在Linux服务器上创建一个目录:

1
2
3
4
# 创建目录,用存放acme.sh验证文件
mkdir -p /www/acme/.well-known/acme-challenge
# 授权给nginx所在的用户
chown -R www:www /www/acme

并且在Nginx上进行配置:

1
2
3
location /.well-known/acme-challenge/ {
alias /www/acme/.well-known/acme-challenge/;
}

重载Nginx的配置,使用acme.sh即可完成配置部署:

1
2
3
4
# Nginx 重载配置
nginx -s reload
# 使用acme.sh验证配置
acme.sh --issue -d example.com -d www.example.com -w /www/acme

解释一下:

  • -d example.com: 指定主域名为example.com。
  • -d www.example.com: 指定附加域名为www.example.com,这将成为同一个证书的 Subject Alternative Name。
  • -w /www/acme: 指定校验域名所有权的网站根目录为/www/acme。acme.sh会在此目录下生成临时文件用来向CA验证域名所有权。

CA机构的验证API正常的情况下,签署的过程是很丝滑的:

签署过程很丝滑

需要注意的是,我们设置的/.well-known/acme-challenge/目录配置不要删除,否则可能影响后续的SSL续签。

部署SSL证书

acme.sh配置已经部署完成了:

部署完成的状态

我们需要把SSL证书安装到网站的SSL目录内,方便Nginx的配置内开启SSL,使用命令:

1
2
3
4
acme.sh --install-cert -d www.example.com \
--key-file /www/ssl/www.example.com.key \
--fullchain-file /www/ssl/www.example.com.pem \
--reloadcmd "systemctl reload nginx"

操作的效果:
操作效果

解释一下:
命令参数:

  • –install-cert:安装 SSL 证书。
  • -d www.example.com: 指定要安装证书的域名。
  • –key-file: 指定私钥文件的路径。
  • –fullchain-file: 指定包含证书链的 PEM 文件的路径。
  • –reloadcmd: 指定在安装/更新证书后重新加载Web服务器的命令。

之后就是设置Nginx的HTTPS了,这里不再赘述。

嘿嘿,很丝滑

DNS验证签署

acme.sh还可以使用DNS验证签署的方式,支持通配域名,配合DNS厂商的API,也可以实现自动续期:

sequenceDiagram
  participant U as 用户服务器
  participant A as acme.sh
  participant DP as DNS提供商
  participant CA as 证书机构

  U->A: 签发证书
  A->DP: 创建TXT记录
  DP->A: 证书签发完成
  A->CA: 请求签名
  CA->A: 返回签名
  A->U: TXT生效等待
  U-->A: 续订证书
  A->DP: 更新TXT记录 
  DP->A: TXT记录已更新
  A->CA: 请求续订签名
  CA->A: 返回续订签名
  A-->U: 更新证书

从流程图就可以看出来,我们需要DNS添加TXT验证。 如果是自己添加DNS的TXT的验证,岂不是和以往差不多么?没错,acme.sh就是可以通过DNS厂商的API,自动添加TXT验证,在验证成功后,自动删除添加的TXT验证;同理,续期也是一样的。

DNS API KEY

acme.sh 需要使用DNS厂商的API,添加TXT记录地址,帮助CA签发机构验证我们对域名的所有权。而acme.sh操作DNS厂商的API,就需要API密钥鉴权。

acme.sh已经适配了多家DNS厂商的API,比如: Cloudflare,、DNSPod(腾讯)、Cloudxns, Godadd等等。我们就以DNSpod为例,看看如何操作。

DNSpod在acme.sh的别名是:dns_dp,其他厂商的别名和支持,可以查看:

acme.sh官方支持列表

acme.sh官方对DNSpod的描述

如果你所使用的DNS,不在acme.sh的支持列表内,你可以尝试手动适配: https://github.com/acmesh-official/acme.sh/wiki/DNS-manual-mode

登录DNSpod的后台地址:https://console.dnspod.cn/

选择API密钥管理:

选择API密钥

之后,我们创建一个密钥:

创建一个密钥

创建好的ID和Token密钥:

创建好的ID和Token密钥

环境变量

acme.sh会从环境变量内,读取所需要用到的DNS API地址。你可以直接临时使用:

1
2
export DP_Id="<id>"
export DP_Key="<key>"

只要你在后续使用acme.sh的--dns dns_dp前,没有重载环境变量,那么acme.sh会把这个环境变量写入~/.acme.sh/account.conf内:

acme.sh保存的账户

当然,我会直接写到环境变量内,其实都一样:

添加到zsh的环境变量内

签署证书

我们使用acme.sh进行签署和验证:

1
acme.sh --dns dns_dp --issue -d "example.com" -d "*.example.com" --server zerossl

使用acme.sh进行SSL的签署

一点点小的报错“红字”是没有关系的;如果没什么问题,片刻的等待后,就可以得到证书内容:

得到证书内容

签署的过程是很丝滑的。

部署SSL证书

我们需要把SSL证书安装到网站的SSL目录内,方便Nginx的配置内开启SSL,使用命令:

1
2
3
4
acme.sh --install-cert -d www.example.com \
--key-file /www/ssl/www.example.com.key \
--fullchain-file /www/ssl/www.example.com.pem \
--reloadcmd "systemctl reload nginx"

操作的效果:

部署SSL到指定位置

之后就是设置Nginx的HTTPS了,这里不再赘述。最后网站的效果:
浏览器上查看网站的SSL证书有效性

END

好啦,本次的教程就到这里啦。其实acme.sh的原理是挺简单的,对DNS的API和CA接口进行操作;省去了用户自己造“轮子”的时间和精力。
其实一开始别人和我推荐acme.sh的时候,我其实是比较反感的,总觉得,脚本修改网站配置或者给出DNS的API Key,也不安全。但是可以直接用别人造好的“轮子”是真的方便,而且本身是开源项目,其实及时更新,一般也没什么问题。



使用acme.sh给Web网站自动安装免费的SSL证书并自动更新,轻松实现HTTPS
https://www.mintimate.cn/2024/05/17/acmeShAutoWebSite/
作者
Mintimate
发布于
2024年5月17日
许可协议