Context:为了实现完整的"基础设施即代码",我想编码使用certbot
请求SSL证书、使用DNSTXT
记录验证域、将证书上传到亚马逊证书管理器(ACM),最后将证书ACM ARN附加到我的Cloudfront分发版的过程。这一切都应该通过无服务器框架来完成。
我看到了两个可行的方案。
选项1:使用异步javascript文件变量
即,在serverless.yml
中,我会定义如下条目:
custom:
domains:
prod: tommedema.tk
ssl:
prod:
dnsTxtRoot: ${{file(scripts/request-cert.js):cert.dnsTxtRoot}}
dnsTxtWww: ${{file(scripts/request-cert.js):cert.dnsTxtWww}}
certArn: ${{file(scripts/request-cert.js):cert.certArn}}
资源将使用这些变量,如下所示:
- Type: TXT
Name: _acme-challenge.www.${{self:custom.domains.${{self:provider.stage}}, ''}}
TTL: '86400'
ResourceRecords:
- ${{self:custom.ssl.${{self:provider.stage}}.dnsTxtWww}}
scripts/request-cert.js
的样子:
module.exports.cert = () => {
console.log('running async logic')
// TODO: run certbot, get DNS records, upload to ACM
return Promise.resolve({
dnsTxtRoot: '"LnaKMkgqlIkXXXXXXXX-7PkKvqb_wqwVnC4q0"',
dnsTxtWww: '"c43VS-XXXXXXXXXWVBRPCXXcA"',
certArn: 'arn:aws:acm:us-east-1:XXXX95:certificate/XXXXXX'
})
}
这里的问题是,似乎不可能向request-cert.js
发送参数,也不可能让这个脚本知道serverless
或options
插件参数(因为它不是一个插件,而是一个没有上下文的简单脚本)。这意味着脚本无法知道部署所针对的阶段和域等,因此它缺少请求证书所需的变量。
因此,选项1似乎是不可能的。
选项2:创建一个插件
当然,我可以创建一个插件,它将具有所有必需的变量,因为它可以访问serverless
和options
对象。现在的问题是,我必须访问serverless.yml
中插件的输出,到目前为止,我还没有看到如何做到这一点。也就是说,我希望能够做这样的事情:
custom:
domains:
prod: tommedema.tk
ssl:
prod:
dnsTxtRoot: ${{myPlugin:cert.dnsTxtRoot}}
dnsTxtWww: ${{myPlugin:cert.dnsTxtWww}}
certArn: ${{myPlugin:cert.certArn}}
但这似乎是不可能的。是这样吗?
如果这也是不可能的,我如何在没有任何手动步骤的情况下以编程方式(即遵循基础设施作为代码原则)部署具有自定义SSL证书的服务?即
- 从certbot请求证书
- 从certbot接收用于验证的DNS txt记录
- 将DNS txt记录附加到路由53记录集
- 部署DNS记录并验证证书
- 从certbot下载证书并将其上传到ACM
- 从ACM接收ARN证书
- 云形成模板内云锋分布内的证书ARN参考
- 随附ARN证书重新部署
您可以在部署时执行此操作,但证书已过期,因此最好将其作为重复操作。
当我遇到这个问题时,我创建了一个Lambda来追加SSL证书并安装它。它需要的密钥可以作为安全的环境变量给出。
然后,我使用serverless-warmup-plugin
设置一个每日触发器,以检查证书是否要刷新。该插件也可配置为在部署时预热相关的Lambda,这使我能够在每次部署时检查是否有过期或丢失的SSL证书。
也许你可以做一些类似的事情。
对于这个特定的用例,使用Step函数是最好的选择。由于您有3个不同的步骤,它们将是三个独立的Lambda函数,Step函数可以在它们之间传递输入/输出,并包括等待时间和重试。