如何通过Typescript CDK自定义Lambda实现Secrets Manager密钥每小时轮转
基于Typescript CDK实现IAM用户密钥每小时自动轮转方案
1. 前置依赖安装
先安装所需的CDK依赖包:
npm install @aws-cdk/aws-secretsmanager @aws-cdk/aws-lambda-nodejs @aws-cdk/aws-iam @aws-cdk/core
2. 编写自定义轮转Lambda代码
在项目根目录新建lambda/rotate-iam-user-password.ts文件,代码遵循Secrets Manager官方约定的4步轮转接口规范:
import { SecretsManager, IAM } from 'aws-sdk'; const secretsManager = new SecretsManager(); const iam = new IAM(); interface RotationEvent { SecretId: string; ClientRequestToken: string; Step: 'createSecret' | 'setSecret' | 'testSecret' | 'finishSecret'; } export const handler = async (event: RotationEvent) => { const { SecretId, ClientRequestToken, Step } = event; switch (Step) { case 'createSecret': // 生成新密码并存入待生效版本 await secretsManager.putSecretValue({ SecretId, ClientRequestToken, SecretString: JSON.stringify({ username: 'user', password: generateRandomPassword() }), VersionStages: ['AWSPENDING'] }).promise(); break; case 'setSecret': // 获取待生效密码,同步更新IAM用户密码 const pendingSecret = await secretsManager.getSecretValue({ SecretId, VersionStage: 'AWSPENDING', VersionId: ClientRequestToken }).promise(); const { password } = JSON.parse(pendingSecret.SecretString as string); await iam.updateLoginProfile({ UserName: 'user', Password: password, PasswordResetRequired: false }).promise(); break; case 'testSecret': // 可自行补充新密码有效性验证逻辑 break; case 'finishSecret': // 将新密码版本标记为当前生效版本 const secretDesc = await secretsManager.describeSecret({ SecretId }).promise(); const currentVersionId = Object.entries(secretDesc.VersionIdsToStages || {}) .find(([_, stages]) => stages.includes('AWSCURRENT'))?.[0]; await secretsManager.updateSecretVersionStage({ SecretId, VersionStage: 'AWSCURRENT', MoveToVersionId: ClientRequestToken, RemoveFromVersionId: currentVersionId }).promise(); break; } }; // 生成符合IAM密码策略的随机密码,可根据需求调整规则 function generateRandomPassword(): string { const length = 16; const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-='; let password = ''; for (let i = 0; i < length; i++) { password += charset.charAt(Math.floor(Math.random() * charset.length)); } return password; }
3. 更新CDK栈配置
补充你现有代码的轮转相关配置:
import * as cdk from '@aws-cdk/core'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda-nodejs'; import { Duration } from '@aws-cdk/core'; export class YourStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const templatedSecret = new secretsmanager.Secret(this, 'TemplatedSecret', { generateSecretString: { secretStringTemplate: JSON.stringify({ username: 'user' }), generateStringKey: 'password', }, }); const otherUser = new iam.User(this, 'OtherUser', { userName: templatedSecret.secretValueFromJson('username').unsafeUnwrap(), password: templatedSecret.secretValueFromJson('password'), }); // 部署轮转Lambda函数 const rotationLambda = new lambda.NodejsFunction(this, 'RotationLambda', { entry: 'lambda/rotate-iam-user-password.ts', handler: 'handler', runtime: lambda.Runtime.NODEJS_18_X, timeout: Duration.seconds(30) }); // 授予Lambda必要的操作权限 templatedSecret.grantRead(rotationLambda); templatedSecret.grantWrite(rotationLambda); otherUser.grant(rotationLambda, 'iam:UpdateLoginProfile'); // 配置每小时自动轮转规则 templatedSecret.addRotationSchedule('RotationSchedule', { rotationLambda, automaticallyAfter: Duration.hours(1) }); } }
注意事项
- Lambda代码中的用户名
user需和实际创建的IAM用户名保持一致,避免密码同步失败 - 可根据企业IAM密码策略调整
generateRandomPassword函数的密码生成规则 - 生产环境建议在
testSecret步骤补充实际的密码有效性验证逻辑,确认新密码可正常使用后再标记为生效 - 不要在CDK代码中对敏感值使用
toString(),建议使用unsafeUnwrap()获取动态引用,避免敏感值泄露到部署配置文件中
内容的提问来源于stack exchange,提问作者Gulshan
相关产品推荐
相关产品推荐

