在 Node.JS 中设置来自 AWS Secrets manager 的 Secrets
在顶级 await 成为一种东西之前,在启动时从 AWS Secrets Manager 异步加载密钥有点麻烦.我想知道是否有人有比我目前拥有的更好的解决方案.
Before top-level await becomes a thing, loading secrets asynchronously from AWS Secrets Manager upon startup is a bit of a pain. I'm wondering if anyone has a better solution than what I currently have.
启动我的 Node.JS 服务器后,我从 AWS Secrets manager 加载所有机密并将它们设置在配置文件中,其中我有硬编码变量和机密的混合.举个例子:
Upon starting up my Node.JS server I'm loading all secrets from AWS Secrets manager and setting them in config files where I have a mix of hardcoded variables and secrets. Here's an example:
在 aws.js 中
import AWS from 'aws-sdk';
const region = "eu-north-1";
AWS.config.setPromisesDependency();
const client = new AWS.SecretsManager({
region
});
export const getSecret = async(secretName) => {
const data = await client.getSecretValue({SecretId: secretName}).promise();
return data.SecretString;
}
然后在 sendgridConfig.js 中
Then in sendgridConfig.js
import { getSecret } from "./aws";
export default async() => {
const secret = JSON.parse(await getSecret("sendgridSecret"));
return {
APIKey: secret.sendgridKey,
fromEmail: "some@email.com",
toEmail: "some@email.com"
}
}
然后在使用配置的某个文件中:
Then in some file where the config is used:
import { sendgridConfig } from "./sendgridConfig";
const myFunc = () => {
const sendgridConf = await sendgridConfig();
... do stuff with config ...
}
这在异步函数中可以正常工作,但是如果我想在使用硬编码变量的非异步函数中使用相同的设置怎么办?然后秘籍还没取到,我也用不上.此外,我必须始终等待秘密.IMO 未来的一个好的解决方案可能是顶级等待,在启动服务器时,服务器将在继续之前等待来自 AWS 的秘密.我想我可以找到一种方法来阻止主线程并设置秘密,但这感觉有点麻烦.
This works okay in async functions, but what if I'd like to use the same setup in non-async functions where I use my hardcoded variables? Then the secrets haven't been fetched yet, and I can't use them. Also I have to always await the secrets. IMO a good solution in the future could be top level await, where upon booting the server, the server will await the secrets from AWS before proceeding. I guess I could find a way to block the main thread and set the secrets, but that feels kind of hacky.
有人有更好的解决方案吗?
Does anyone have a better solution?
所以我最终做了以下事情.首先,我在导出的对象文字中设置非异步配置变量.然后我将值分配给 sendgridConfigAsync IIFE 中的对象文字(不必是 IFEE).这样我就不必等待配置承诺.只要应用在启动时等待 IIFE,就会在访问之前分配密钥.
So I ended up doing the following. First I'm setting the non-async config variables in an exported object literal. Then I'm assigning values to the object literal in the sendgridConfigAsync IIFE (doesn't have to be an IFEE). That way I don't have to await the config promise. As long as the app awaits the IIFE on startup, the keys will be assigned before being accessed.
在 sendgridConfig.js 中
In sendgridConfig.js
import { getSecret } from "./aws";
export const sendgridConfig = {
emailFrom: process.env.sendgridFromEmail,
emailTo: process.env.sendgridToEmail
}
export const sendgridConfigAsync = (async() => {
const secret = JSON.parse(await getSecret("Sendgrid-dev"));
sendgridConfig.sendgridKey = secret.key;
})()
然后在主配置文件 _index.js 中导入所有配置文件.
Then in the main config file _index.js where I import all the config files.
import { sendgridConfigAsync } from "./sendgrid";
import { twilioConfigAsync } from "./twilio";
import { appConfigAsync } from "./app";
export const setAsyncConfig = async() => {
await Promise.all([
appConfigAsync,
sendgridConfigAsync,
twilioConfigAsync
]);
}
然后在主 index.js 文件中,我首先等待 setAsyncConfig 函数.我也确实重建了应用程序,以便控制所有函数调用并承诺按所需顺序解析.
Then in the main index.js file I'm awaiting the setAsyncConfig function first. I did also rebuild the app somewhat in order to control all function invocations and promise resolving in the desired order.
import { servicesConnect } from "../src/service/_index.js";
import { setAsyncConfig } from '$config';
import { asyncHandler } from "./middleware/async";
import { middleware } from "./middleware/_index";
import { initJobs } from "./jobs/_index"
import http from 'http';
async function startServer() {
await setAsyncConfig();
await servicesConnect();
await initJobs();
app.use(middleware);
app.server = http.createServer(app);
app.server.listen(appConfig.port);
console.log(`Started on port ${app.server.address().port}`);
}
asyncHandler(startServer());