电脑上按Ctrl + D,下次访问更方便
服务分类

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或以上。

发表回复

登录后才能评论
联系我们

联系我们

微信专属客服:

fuwu360微信客服

工作时间:周一至周五,9:30-18:30,节假日休息

返回顶部