背景
现在网站部署,普遍都是用https的模式了。但是SSL证书的购买、更新,还是一个比较困扰普通网站主的事情。要想方便,就花钱买,动不动一年几千块钱的证书费用;不想花钱呢,就得麻烦点儿,因为免费的证书,通常有效时间不超过3个月,所以就得自己定期更新证书。
如果选择“不花钱”,acme.sh是一个很不错的方案,网上也有不少简单的介绍。不过实际用起来,才会发现,它的强大不仅仅在于更新域名证书,与各类DNS、服务器环境的结合,让域名证书的自动更新变得更加方便。
acme.sh
直接在浏览器地址栏里面输入acme.sh,就可以直接定向到工具的官网(实际是github仓库地址),这也很方便大家找到它。
在Wiki页面,可以找到它的详细使用教程。
不过写这篇文章,其实不仅仅是为了介绍这个工具,更多地,是想说一下我自己使用它的方案。
我的部署
我的代码部署主要是在百度云的BCH(虚拟主机)上,也有一些试验性质,或者工具性质的内容部署在其他的VPS服务器上,这部分可以不用管。此外,服务中有的涉及图片存储的,一般是放到七牛云上。因此,实际上我最终要部署SSL证书,支持https访问的,主要就是百度云BCH和七牛云CDN。
使用acme.sh,我认为主要需要解决3个问题:
证书的生成
证书的部署
即将过期时的自动替换和生效
因此,我们可以分开看这几个问题如何解决。
证书生成
证书的生成,是最简单的一环,实际上acme.sh也是调用各种证书厂商的接口来实现的。它支持的厂商可以参见wiki的列表:https://github.com/acmesh-official/acme.sh/wiki/CA。
其实证书有效期到底是90天还是180天,意义不是太大,只要能够自动更新就行。对我来说,支持的域名数量反而更重要一些。因为为了备案,以及各种不同的使用场景,再加上七牛云的图片域名证书,起码有十几个域名需要部署SSL证书。因此,Let's Encrypt、ZeroSSL这些支持域名数量较多的,对我来说更加适用。
证书生成时,需要进行域名所有权的验证。acme.sh支持直接接入各种DNS厂商的API,支持的列表可以参见:
https://github.com/acmesh-official/acme.sh/wiki/dnsapi。
我用的是DNSPod,因此照着说明,指定调用的Key,就可以使用了。
证书部署
如果服务本身是部署在vps上的话,其实就可以直接用acme.sh自己提供的功能来实现了。但对我来说,这就需要自己来处理了。
七牛云
查了acme.sh的文档之后,发现七牛云已经提供支持了。
https://github.com/acmesh-official/acme.sh/wiki/deployhooks
这一点其实还比较出乎我意料。不过既然已经有这个功能,那用起来就太方便了,证书生成之后,直接可以发布更新,完全不需要我操心。
百度云
但到了百度云时,事儿就稍微麻烦了一些。我们在acme.sh的文档里面也好,通过搜索引擎去找也好,可以确认工具并不支持与百度云的对接。
这个时候,我们就需要自己实现了。
OK,我们翻翻百度云的文档,发现百度云其实提供了API和SDK,帮助我们实现证书自动上传的功能:https://cloud.baidu.com/doc/Reference/s/pkbqd9yp2 。既然这样,照着写就可以了。如果用Java、Python实现,可以直接使用提供的SDK,如果用其他语言,就需要参照API,自己实现一套。
因为在证书上传之前,我还会调用证书生成之类的命令,因此我琢磨着用Python比较方便,所以就用Python写了这套工具(下文会放出代码仓库地址供参考)。不过这里有一个稍微坑一点的地方,就是证书能够正常上传,但上传之后并不能直接生效,而是需要到BCH的管理面板中进行刷新操作(参见:https://cloud.baidu.com/doc/BCH/s/Qk5gje9fs )。
即将过期时的自动替换和生效
acme.sh其实也提供了自动更新的能力,原理很简单,就是在Crontab中增加一个每天定时执行的任务,例如:
脚本每天执行,不过默认配置情况下,满60天之后才会触发更新。
同样的问题来了,本机自动更新很容易,但是涉及百度云这样需要手工再进行一次操作的,就稍微麻烦一点。因此,我又重新设计了一下我的流程
每天执行脚本
满N天后触发更新(其中七牛云可以直接自动更新,百度云生成后自动上传)
发出提醒邮件,提示自己需要到百度云上操作,更新证书
按照这个流程,我用了Jenkins平台(平时一些稳定性要求不高、低频的定时任务,以及自动部署等工具,原本也是放在Jenkins里面)来实现。
首先,我写了个shell脚本,用来每天执行。这个脚本唯一的目的是去判断距离上一次执行(其实就是上一次证书生成的时间)过去了多长时间,如果超出N,就正常退出(exit 0),见下图。
如果不需要更新呢,就exit 1。
Jenkins中设置一个任务,执行这个脚本,就实现了“需要更新”和“不需要更新”两种情况下,任务的成功/失败状态的区分。任务成功,表示“需要更新”,失败则表示“不需要更新”。
比如看上图,Jenkins中的任务可以通过插件来设置前后依赖,即在某个任务执行之后触发后续任务(我触发了bch、七牛更新这两个任务)。同时,在触发条件里面还可以设置为“成功后触发”,即这个Stable。
而在bch更新的这个任务中,我通过脚本又设置了一个后续动作,触发邮件发送,提醒自己去点击刷新按钮。
这样,就实现了一个完整的通路:
每天执行,判断是否需要进行证书更新
以下情况:
不需要更新,任务退出
需要更新,自动触发七牛和百度云两个任务
以下情况
七牛更新,所有步骤自动完成
百度云更新,发送邮件提醒自己到平台进行手工刷新操作
代码参考
https://github.com/poisonbian/cert_update
其实代码本身不算复杂,主要就是顺着上面的思路,一步步捋下来的,仅供参考。
本文链接:https://www.poisonbian.com/post/5047.html 转载需授权!