Python定期自动更新SSL证书到阿里云CDN
网站管理 •
网站如果不用SSL证书,用http协议访问,浏览器地址栏直接就出现三角符号警示,并文字提示“不安全”,所以哪怕是微不足道的小网站,也不得不整个SSL证书挂上,然而如今的SSL证书有效期越来越短,免费的Let’s Encrypt只有90天的有效期了,听说以后还要缩短到47天!如果服务器使用宝塔这类控制面板,也倒是可以自动续期证书,但如果使用了CDN,那又有新的麻烦:CDN的SSL证书每隔几十天就要更新一次!
如果你不想花钱买有效期更长的SSL证书,但又要定期更新CDN的证书,避免网站证书失效被浏览器警告,怎么办呢?
办法还是有的。首先,免费的Let’s Encrypt申请的时候用DNS验证,需要配置DNS接口,不要直接用文件验证的方式,原因是使用了CDN之后,文件验证的方式就不能自动续期了。
然后利用Python代码,在Linux服务器上设置定时运行,检测到服务器本地的SSL证书与CDN的证书有效期不一致,相差天数大于1,则说明服务器本地的SSL证书续期过了,需要上传更新。
代码如下:
# !/usr/bin/python3
# -*- coding:utf-8 -*-
import datetime
import json
import os
from aliyunsdkcore.client import AcsClient
from aliyunsdkcdn.request.v20180510 import SetCdnDomainSSLCertificateRequest
from aliyunsdkcdn.request.v20180510 import DescribeCdnHttpsDomainListRequest
from OpenSSL import crypto
def check_cert_expiration(cert_path, cert_expire_time) -> bool:
# 手动处理格式,去掉 Z 并解析为 UTC 时间
dt_utc = datetime.datetime.strptime(cert_expire_time, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=datetime.timezone.utc)
now_utc = datetime.datetime.now(datetime.timezone.utc)
delta = dt_utc - now_utc
# 本地验证证书文件
cert2 = crypto.load_certificate(crypto.FILETYPE_PEM, open(cert_path).read())
cert2.get_version()
subject = cert2.get_subject()
subject.get_components()
# 获取到的时间戳格式是ans.1的,需要转换
notafter2 = datetime.datetime.strptime(cert2.get_notAfter().decode()[0:-1], '%Y%m%d%H%M%S')
# 用证书到期时间减去当前时间
remain_days2 = notafter2 - datetime.datetime.now()
print(f'本地证书{subject.CN} 过期时间{remain_days2.days}天,CDN上的证书过期时间{delta.days}天', )
# 两个天数相差超过1天,说明本地证书续签了,需要重新上传
return delta.days <= 0 or remain_days2.days <= 0 or abs(remain_days2.days - delta.days) > 1
def upload_certificate(client, cdn_domain, cert_path, key_path, cert_expire_time):
if not os.path.isfile(cert_path):
raise FileNotFoundError(f"{cert_path} is missing")
if not os.path.isfile(key_path):
raise FileNotFoundError(f"{key_path} is missing")
if not os.path.getsize(cert_path):
raise FileNotFoundError(f"{cert_path} is empty")
if not os.path.getsize(key_path):
raise FileNotFoundError(f"{key_path} is empty")
if not check_cert_expiration(cert_path, cert_expire_time):
print(f"{cdn_domain} 还没有续签,跳过")
return
with open(cert_path, 'r') as f:
cert = f.read()
with open(key_path, 'r') as f:
key = f.read()
request = SetCdnDomainSSLCertificateRequest.SetCdnDomainSSLCertificateRequest()
# CDN加速域名
request.set_DomainName(cdn_domain)
# 证书名称
request.set_CertName(cdn_domain + datetime.datetime.now().strftime("%Y%m%d"))
request.set_CertType('upload')
request.set_SSLProtocol('on')
request.set_SSLPub(cert)
request.set_SSLPri(key)
request.set_CertRegion('cn-hangzhou')
response = client.do_action_with_exception(request)
print(str(response, encoding='utf-8'))
# 查询证书列表
def get_cert_infos(client):
request = DescribeCdnHttpsDomainListRequest.DescribeCdnHttpsDomainListRequest()
response = client.do_action_with_exception(request)
return str(response, encoding='utf-8')
def main():
# ALIYUN_ACCESS_KEY_ID
access_key_id = 'LTAI5tNGZQDXqc4zK*******'
access_key_secret = '3FaxIkypq9iwaNBpVQkgEuw*******'
client = AcsClient(access_key_id, access_key_secret, 'cn-hangzhou')
cert_infos = get_cert_infos(client)
cert_infos = json.loads(cert_infos)
CertInfos = cert_infos['CertInfos']['CertInfo']
for cert_info in CertInfos:
cdn_domain = cert_info['DomainName']
domain = cdn_domain.replace('www.', '')
cert_expire_time = cert_info['CertExpireTime']
print(f'域名:{cdn_domain}')
cert_path = f'/www/server/panel/vhost/cert/{domain}/fullchain.pem'
key_path = f'/www/server/panel/vhost/cert/{domain}/privkey.pem'
upload_certificate(client, cdn_domain, cert_path, key_path, cert_expire_time)
if __name__ == "__main__":
main()
以上代码,请自行修改access_key_id和access_key_secret的值,去阿里云申请。如果你的网站SSL本地证书的路径特殊,请修改cert_path和key_path路径。
在宝塔面板上设置个定时任务:

python代码这里是命名为upload_certs_to_aliyun.py放在/opt目录,路径自行修改即可。运行命令为:
python3 upload_certs_to_aliyun.py
需要安装的Python库,命令:
pip3 install aliyun-python-sdk-core aliyun-python-sdk-cdn OpenSSL
注意服务器上的Python版本需要3.6或以上。