Let’s Encrypt 是一个免费、开放,自动化的证书颁发机构,由 ISRG(Internet Security Research Group)运作。
然而有一个问题,Let’s Encrypt 的证书只有三个月的有效期,虽说服务器会自动续签,但是续签之后的证书不能自动推送到阿里云 CDN,博主手上有几个站点都使用的 Let’s Encrypt,手动上传证书是在是太麻烦了。
前几个月,朋友 Hintay 写了一个 Python 脚本,利用了阿里云 OpenAPI 的 SDK,实现推送 Let’s Encrypt 证书到阿里云 CDN,脚本只能提交一个域名。前段时间一直很忙,就没有跟进这件事,今天有空把脚本改成批量推送了,并且在服务器上用 Crontab
开了定时,因为证书会至少提前一个月自动续期,所以设置成每周一检,就很舒服了。
下面上 Python
#!/usr/bin/python
# -*- coding:utf-8 -*-
#
# Copyright (c) 2017 Hintay <hintay@me.com> & 0xJacky <jacky-943572677@qq.com>
#
# !请先使用 pip install aliyun-python-sdk-cdn 安装 sdk!
from aliyunsdkcore import client
from aliyunsdkcdn.request.v20141111 import SetDomainServerCertificateRequest
import datetime
import os
import collections
import hashlib
import json
ABS_PATH = os.path.abspath('.')
JSON_PATH = os.path.join(ABS_PATH, 'data.json')
## 配置开始
# 访问 https://ak-console.aliyun.com/index#/accesskey 获取
AccessKeyId = ''
AccessKeySecret = ''
# 指定证书所属加速域名,需属于https加速类型
# !!! 修改域名元组后请将 data.json 删除
DomainName = ['jackyu.cn', 'beta.uozi.org']
Letsencrypt_path = os.path.join('/etc/letsencrypt')
live_cert = os.path.join(Letsencrypt_path, 'live')
## 配置结束
# 获取证书 md5 返回: 字典
def key_md5():
domain = collections.OrderedDict()
for d in DomainName:
privkey = os.path.join(live_cert, d, 'privkey.pem')
file = open(privkey, 'rb')
md5 = hashlib.md5(file.read()).hexdigest()
domain[d] = md5
return domain
# 写入数据
def write_data(domain):
with open(JSON_PATH, 'w') as json_file:
json_file.write(json.dumps(domain))
# 获取数据
def load_data():
with open(JSON_PATH) as json_file:
data = json.load(json_file)
return data
# 判断数据文件是否存在
if not os.path.exists(JSON_PATH):
# 将私钥的 md5 写入 Json
write_data(key_md5())
domain = key_md5()
data = load_data()
for d in DomainName:
if not data[d] == domain[d]:
try:
Client = client.AcsClient(AccessKeyId, AccessKeySecret, 'cn-hangzhou')
request = SetDomainServerCertificateRequest.SetDomainServerCertificateRequest()
request.set_accept_format('json')
CertName = d + '_' + datetime.datetime.now().strftime("%Y%m%d_%H%M%S") # 证书名称,默认域名+日期时间
ServerCertificate_path = os.path.join(live_cert, d, 'fullchain.pem') # 安全证书路径
PrivateKey_path = os.path.join(live_cert, d, 'privkey.pem') # 私钥路径
request.set_DomainName(d)
request.set_CertName(CertName)
request.set_ServerCertificateStatus('on')
ServerCertificate = open(ServerCertificate_path, 'r').read()
ServerCertificate = open(ServerCertificate_path, 'r').read()
PrivateKey = open(PrivateKey_path, 'r').read()
request.set_ServerCertificate(ServerCertificate)
request.set_PrivateKey(PrivateKey)
result = Client.do_action_with_exception(request)
print(result)
except ServerException as e:
print('Domain:'+d+'Error:'+e)
# 更新数据文件
os.remove(JSON_PATH)
write_data(key_md5())
文章最后修订于 2020年5月14日
为啥是if not data[d] == domain[d]:
不是 if data[d] == domain[d]:
@jilmy: md5值相等的话就不用重复推送了
@Jacky: 是的,我刚刚发现,感谢
@jilmy: 扩展项目是这个 https://github.com/0xJacky/cdn_cert 如果您发现代码有逻辑问题可以提个 pr
@Jacky: 好的,学习了
PrivateKey 需要为RSA格式啊,要先转一下吧
@Harry: 这个 issue 已经在 cdn_cert 项目中修正