前言
前不久,阿里云通知笔者 CDN 的 SSL 证书快到期了,于是笔者就打算新申请一张 1 年的 SSL 证书。
对于免费领取的 SSL 证书,签发后的证书有效期统一变更为 3 个月
结果发现,现在免费的证书只有 3 个月了,跟 Let’s Encrypt 提供的 90 天免费证书一样了。
既然如此,为什么不直接使用 Let’s Encrypt 提供的免费证书呢?
在之前,由于 Let’s Encrypt 只提供最长 90 天的免费证书,为了省点事,选择了阿里云的 1 年期免费证书。
关于 Let’s Encrypt 为什么只提供 90 天的免费证书,详见:Why ninety-day lifetimes for certificates?
最重要的是 Let’s Encrypt 完全免费!
经过一通研究,笔者发现可以通过调用阿里云 OpenAPI 来实现自动化更新 SSL 证书,在此记录一下使用方式。
什么是阿里云 OpenAPI
OpenAPI 是指开放应用程序编程接口(Open Application Programming Interface)的简称。阿里云 OpenAPI 为开发者提供了一系列开放的应用程序接口,使您可以通过这些应用程序接口来方便的管理云上资源、数据和服务等内容。
详见:什么是 OpenAPI
总之,我们可以通过阿里云提供的 OpenAPI,来调用阿里云上的一系列接口,从而管理对应的资源和服务。
获取 AccessKey
在一切开始之前,需要先获取到 AccessKeyId 和 AccessKeySecret。
如果你现在点击 左上角的 AccessKey 管理的话,就会发现,阿里云提示你使用 子用户 AccessKey
在这里,出于安全起见,十分建议使用子用户 AccessKey!
可以前往 子用户管理 页面进行创建。
创建用户的时候记得选上 OpenAPI 调用访问。
详见:创建 RAM 用户
记得及时保存 AccessKey ID 和 AccessKey Secret,关掉之后就看不见了。
创建完子用户后还需要给子用户授权。
由于目前只需要管理 CDN,因此这里只给子用户授权 CDN 相关的权限即可。
因为还需要写入域名证书,故需要拿到写入权限,不要选择 只读 权限。
如何使用 OpenAPI 管理 CDN
在调用 OpenAPI 之前,显然需要先知道这个 API 是干什么的,有哪些入参,返回值又是什么样的。
查看 API 文档
所幸,阿里云提供了完善的 API 文档。
详见:阿里云 API 文档
经过一番搜索,就可以看到需要的 API 了。
DescribeDomainCertificateInfo - 查询域名证书信息和 SetCdnDomainSSLCertificate - 设置CDN域名证书。一个用于查看当前域名的证书信息,另一个用于设置证书。
查看 API 代码调用示例
点击右上方的 去调用 即可调试相关接口。
在输入输入参数后,即可点击左侧的 SDK 示例查看具体的代码。
由于笔者比较喜欢用 TypeScript,所以此处的例子是用 TypeScript 实现的。
读者朋友也可以使用自己喜欢的编程语言进行调用。
阿里云提供了包括 Java、TypeScript、Go、PHP、Python 等多种语言的 SDK,可以选一个喜欢的进行使用。
基于同样的原理,也可以获取到 SetCdnDomainSSLCertificate 的代码逻辑。
封装 API 调用
参考阿里云给出的例子后,稍加改动,就得到了以下代码。
import Cdn20180510, * as $Cdn20180510 from '@alicloud/cdn20180510'
import * as $OpenApi from '@alicloud/openapi-client'
import * as $Util from '@alicloud/tea-util'
export default class Client {
/**
* 使用AK&SK初始化账号Client
* @return Client
* @throws Exception
*/
static createClient(): Cdn20180510 {
// 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
// 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378664.html。
const config = new $OpenApi.Config({
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
accessKeyId: process.env['ALIBABA_CLOUD_ACCESS_KEY_ID'],
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
accessKeySecret: process.env['ALIBABA_CLOUD_ACCESS_KEY_SECRET'],
})
// Endpoint 请参考 https://api.aliyun.com/product/Cdn
config.endpoint = 'cdn.aliyuncs.com'
return new Cdn20180510(config)
}
// 查询域名的证书
static async describeDomainCertificateInfoWithOptions(domainName: string) {
const client = Client.createClient()
const describeDomainCertificateInfoRequest = new $Cdn20180510.DescribeDomainCertificateInfoRequest({
domainName,
})
const runtime = new $Util.RuntimeOptions({})
try {
// 复制代码运行请自行打印 API 的返回值
return (await client.describeDomainCertificateInfoWithOptions(describeDomainCertificateInfoRequest, runtime)).body
} catch (error) {
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
console.error(error?.message)
// 诊断地址
console.error(error?.data?.['Recommend'])
}
}
/**
* 设置域名证书
*
* @author CaoMeiYouRen
* @date 2024-05-22
* @static
* @param domainName 域名
* @param certName 证书名称
* @param SSLPub 证书内容
* @param SSLPri 私钥内容
*/
static async setCdnDomainSSLCertificateWithOptions(domainName: string, certName: string, SSLPub: string, SSLPri: string) {
const client = Client.createClient()
const setCdnDomainSSLCertificateRequest = new $Cdn20180510.SetCdnDomainSSLCertificateRequest({
domainName,
certName,
certType: 'upload',
SSLProtocol: 'on',
SSLPub,
SSLPri,
})
const runtime = new $Util.RuntimeOptions({})
try {
// 复制代码运行请自行打印 API 的返回值
return (await client.setCdnDomainSSLCertificateWithOptions(setCdnDomainSSLCertificateRequest, runtime)).body
} catch (error) {
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
console.error(error?.message)
// 诊断地址
console.error(error?.data?.['Recommend'])
}
}
}
这里通过设置 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET 两个环境变量来进行调用 API,相对安全。
如果需要更安全的方式,可以考虑通过 STS 方式,更多鉴权访问方式请参见:help.aliyun.com/document_de…
实现自动更新 SSL 证书逻辑
最后,再添加一些判断逻辑进去,就是实现了自动更新 SSL 证书,这里是在过期时间小于 31 天的时候就更新证书。
import path from 'path'
import dotenv from 'dotenv'
import dayjs from 'dayjs'
import fs from 'fs-extra'
import CdnClient from './apis/cdn' // './apis/cdn' 文件就是上面的示例。
dotenv.config({ path: ['.env.local', '.env'] })
// 更新 CDN 证书
async function updateCdnCertificate(domainName: string) {
// 域名证书位置
const CDN_SSL_DIR_PATH = process.env.CDN_SSL_DIR_PATH
const certificateInfo = await CdnClient.describeDomainCertificateInfoWithOptions(domainName)
const days = dayjs(certificateInfo.certInfos.certInfo[0].certExpireTime).diff(dayjs(), 'days')
console.log(`域名 ${domainName} 还有 ${days} 天过期`)
if (days < 31) { // 如果小于 31 天,更新证书
console.log(`域名 ${domainName} 的证书即将过期,正在更新中`)
const certName = `${domainName}-${dayjs().format('YYYY-MM-DD')}`
// 此处假定是 Let’s Encrypt 提供的域名证书,公钥是 fullchain.pem,私钥是 privkey.pem。其他类型的证书也类似
const SSLPub = await fs.readFile(path.join(CDN_SSL_DIR_PATH, 'fullchain.pem'), 'utf-8')
const SSLPri = await fs.readFile(path.join(CDN_SSL_DIR_PATH, 'privkey.pem'), 'utf-8')
const certificateResponseBody = await CdnClient.setCdnDomainSSLCertificateWithOptions(domainName, certName, SSLPub, SSLPri)
console.log(JSON.stringify(certificateResponseBody))
console.log
return
}
console.log(`域名 ${domainName} 的证书还未过期,跳过更新`)
}
updateDcdnCertificate('example.com')
最后,把这份代码设置为定时任务,每天执行一遍,即可实现自动更新 SSL 证书!
总结
除了更新 CDN 证书外,阿里云 OpenAPI 自然也可以更新 DCDN 域名证书、管理 DNS 解析、管理图床、管理 WAF 等。
总之,只要是阿里云上提供的服务,基本上都能通过阿里云 OpenAPI 来管理。对自动化来说,是极大的利好。
不过,需要注意的是,也要保管好 AccessKey ID 和 AccessKey Secret,以免泄露,从而造成损失。
本文作者:草梅友仁
本文地址:blog.cmyr.ltd/archives/'4…
版权声明:转载请注明出处!