在云服务器上实现免费SSL证书自动续期

免费SSL证书有效期从一年减少到了90天,终于把证书部署自动化提上了日程

1. 缘起

如今,网站都需要支持https,而https所需要的ssl证书如果使用付费证书也需要一笔不少的费用,所以免费证书便成了很多人的选择。在2024年以前我们一般使用的免费ssl证书都支持一年的有效期,所以我一般也是手动申请证书然后部署到服务器,毕竟一个域名一年也只需要弄一次,所以也并没有觉得麻烦。然后从2024年的大概4月份以后,免费的证书有效期最长就只有90天,这让免费证书的手动续期变成了我的烦恼。于是才想要找个自动续期证书的脚本。

于是acme.sh就走进了我的视野,这是一个github上很流行的开源脚本。


https://github.com/acmesh-official/acme.sh

2. 安装

像这样的开源项目文档还是相对比较完善的,完整的安装和使用的话请自行查看项目的readme和wiki。

这里我就只是简单的介绍一下我自己的安装使用流程,我的云服务器安装的是ubuntu。

脚本的安装有两种方式, 第一种是官方脚本自动安装,这里我用的是第二种git方式安装


git clone -b master --depth=1 https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh
./acme.sh --install -m 'my@example.com'
source ~/.bashrc

这里我只拉取了master分支的最后一次commit,这样减少了包大小,clone后进入项目目录,运行--install命令时替换 -m 后的参数为自己的邮箱地址就行。

安装脚本运行后还需要重新引入以下.bashrc让脚本路径生效,这样才能直接在shell中调用acme.sh。

做完以上 步骤之后在shell中运行 acme.sh -h 能看到脚本的帮助信息说明安装成功了。


3. 证书申请


在开始证书申请之前根据不同的使用场景需要准备几个不同的东西。比如我的使用场景是在网站部署的云服务器上实现自动化申请、部署、续期。然后我选择的域名认证方式是通过网站根目录.well-known的方式,所以我需要使用对网站根目录的.well-known目录有读写权限的用户来安装acme.sh。并且通过 域名+/.well-known 需要能访问到.well-known。

比如我的网站是通过nginx部署,并且我添加了对点文件的禁止访问规则,那么就需要在nginx的站点server配置中添加以下配置:


location /.well-known {
allow all;
}

当然一般如果没有添加其他规则禁止此目录的访问,也就不需要添加这条规则。

确保了当前用户对.well-known的读写权限和网站路径能正常访问到.well-known,acme就能自动验证域名了。

然后是nginx ssl相关的两个配置文件地址需要对当前用户可写,也就是ssl_certificate 和 ssl_certificate_key 这两个文件路径。只有这两个文件当前用户可写才能自动部署ssl证书到nginx。

那么我们就可以开始正式申请证书了。


acme.sh --issue \
    --domain '16o.xyz' \
    --server letsencrypt \
    --webroot '/www/16o.xyz' \
    --fullchain-file '/nginx/ssl_certificate/file/path' \
    --key-file '/nginx/ssl_certificate_key/file/path'


以上命令中,--issue 是表示申请证书,--domain是申请证书的域名, --server是证书颁发机构,这里我使用的是letsencrypt,--webroot指定的是域名站点的根目录,用于定位.well-known并写入域名验证文件,--fullchain-file 和 --key-file 分别是nginx的 ssl_certificate 和 ssl_certificate_key 的路径,指定这两个参数之后脚本就会自动将证书部署过去。

当然如果想手动部署,那就不需要这两个参数。acme生成成功的证书都会存放在 ~/.acme.sh/域名_ecc 目录下,想要手动部署的话就在这里获取生成成功的证书。


如果是不想通过.well-known的方式进行域名验证,那么还有DNS解析验证的方式,同样也支持自动化。但是这就需要有DNS提供商的api权限了,以腾讯云的DNSPod为例,需要先到DNSPod的账户中心去获取DNSPod的token密钥,然后在运行 issue 命令之前在环境中声明两个变量


export DP_Id=''
export DP_Key=''
acme.sh --issue \
    --domain 'jk.16o.xyz' \
    --server letsencrypt \
    --dns dns_dp \
    --fullchain-file '/nginx/ssl_certificate/file/path' \
    --key-file '/nginx/ssl_certificate_key/file/path'

这种方式就不需要网站的.well-know权限,同时也就不局限于一定要在云服务器上运行命令,当然如果不是在云服务器上,那么也就不能指定证书路径相关的参数来进行自动部署。当然也可以在本地申请完成之后再通过自己的方式将证书自动部署到服务器上。这就是看个人需求和灵活运用了。

然后我之所以选择.well-know的方式是因为这种方式申请证书的流程更快,因为DNS验证的方式需要等待DNS解析记录生效。而我本来就是要在云服务器上实现自动化,所以就选择了.well-known文件的方式。


4. 自动化


当acme.sh安装成功的时候就会自动创建crontab计划任务,每天运行一次并检查当前通过acme申请的证书是否需要续期。acme默认是在证书申请成功后60天进行续期操作,其实也就是重新申请并部署。


这里需要注意的一点是,比如我用的nginx部署,证书申请后会自动替换ssl证书,但是nginx并不会自动重载配置,也就是说虽然证书文件已经替换了,但是还没有生效。那么就需要我们自己来重启nginx或者重新加载nginx配置。


sudo nginx -s reload


以上就是 nginx 重载配置的命令,那么既然是自动化,那么当然我们就需要让ngxin配置重载也通过crontab实现自动化了。所以我们编辑root的crontab任务,添加一个自动重载ngxin配置的任务整个证书自动续期功能就算是完全实现了。


# 打开并编辑root的crontab
sudo crontab -e

# 然后在末尾添加

0 5 * * 1 /usr/sbin/nginx -s reload > /dev/null


至于计划任务的执行周期可以根据自己需求灵活调整。

至此,证书的自动化申请、部署、续期都已经实现。再也不用担心忘记更新证书导致网站不能访问了。