如何将Lambda生成的密钥存储到KMS及Parameter Store供ECS Fargate使用
实现指引
整体流程
- 创建持有KMS写入、SSM Parameter Store写入权限的Lambda执行角色
- 改造现有TypeScript密钥生成代码,删除本地写文件逻辑,替换为调用AWS SDK写入KMS和SSM参数存储
- 用Terraform编排所有资源:KMS密钥、Lambda函数、SSM参数、ECS任务执行角色、相关IAM权限
- 为ECS Fargate任务执行角色配置KMS解密、SSM参数读取权限,业务容器可直接读取注入的密钥参数,无需额外适配
改造后的Lambda TypeScript代码
需提前安装依赖:@aws-sdk/client-kms、@aws-sdk/client-ssm、crypt-util
import { LocalCryptUtils } from 'crypt-util'; import { KMSClient, ImportKeyMaterialCommand } from "@aws-sdk/client-kms"; import { SSMClient, PutParameterCommand } from "@aws-sdk/client-ssm"; // Lambda执行角色自带AWS权限,无需手动配置AKSK const kmsClient = new KMSClient({ region: process.env.AWS_REGION }); const ssmClient = new SSMClient({ region: process.env.AWS_REGION }); const KMS_KEY_ID = process.env.KMS_KEY_ID; const SSM_PARAM_NAME = process.env.SSM_PARAM_NAME; export const handler = async () => { const cryptUtils = new LocalCryptUtils(); cryptUtils.createMasterPrivateKey(); const masterPrivateKey = cryptUtils.exportMasterPrivateKey(); const derivedPublicKey = cryptUtils.derivePublicKey(0, 0); const derivedPublicAddress = cryptUtils.deriveAddress(0, 0); // 1. 将主私钥作为密钥材料导入KMS const importKeyCmd = new ImportKeyMaterialCommand({ KeyId: KMS_KEY_ID, KeyMaterial: Buffer.from(masterPrivateKey, 'utf8'), ExpirationModel: 'KEY_MATERIAL_DOES_NOT_EXPIRE' }); await kmsClient.send(importKeyCmd); // 2. 将派生公钥信息加密存入SSM Parameter Store const putParamCmd = new PutParameterCommand({ Name: SSM_PARAM_NAME, Type: 'SecureString', Value: JSON.stringify({ public_key_derived: derivedPublicKey, public_address_derived: derivedPublicAddress }), Overwrite: true, KeyId: KMS_KEY_ID }); await ssmClient.send(putParamCmd); return { statusCode: 200, body: '密钥存储完成' }; };
注:如果不需要将密钥材料导入KMS原生密钥存储,也可直接将主私钥作为
SecureString类型存入SSM,权限逻辑完全一致,可根据安全等级要求选择。
Terraform配置示例
1. 基础Provider配置
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } provider "aws" { region = "cn-northwest-1" # 替换为实际部署区域 } data "aws_caller_identity" "current" {} variable "ssm_param_path" { default = "/business/key" # 替换为实际SSM参数存储路径 }
2. Lambda执行角色与权限配置
# Lambda执行角色 resource "aws_iam_role" "lambda_key_generator_role" { name = "lambda-key-generator-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } }] }) } # Lambda基础日志权限 resource "aws_iam_role_policy_attachment" "lambda_basic_execution" { role = aws_iam_role.lambda_key_generator_role.name policy_arn = "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" } # Lambda操作KMS、SSM的权限 resource "aws_iam_policy" "lambda_key_permission" { name = "lambda-key-permission" description = "允许Lambda导入KMS密钥材料、写入SSM参数" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = ["kms:ImportKeyMaterial","kms:DescribeKey"] Effect = "Allow" Resource = aws_kms_key.master_key.arn }, { Action = ["ssm:PutParameter"] Effect = "Allow" Resource = "arn:aws-cn:ssm:${var.region}:${data.aws_caller_identity.current.account_id}:parameter${var.ssm_param_path}/*" } ] }) } resource "aws_iam_role_policy_attachment" "lambda_key_perm_attach" { role = aws_iam_role.lambda_key_generator_role.name policy_arn = aws_iam_policy.lambda_key_permission.arn }
3. KMS密钥与Lambda函数部署
# 存储主密钥的KMS对称密钥 resource "aws_kms_key" "master_key" { description = "业务主私钥存储密钥" key_usage = "ENCRYPT_DECRYPT" customer_master_key_spec = "SYMMETRIC_DEFAULT" enable_key_rotation = false # 自定义导入密钥材料不支持自动轮换 deletion_window_in_days = 30 } # 密钥生成Lambda函数 resource "aws_lambda_function" "key_generator" { filename = "lambda_function.zip" # 替换为打包后的代码包路径 function_name = "key-generator" role = aws_iam_role.lambda_key_generator_role.arn handler = "index.handler" # 替换为实际代码入口 runtime = "nodejs20.x" environment { variables = { KMS_KEY_ID = aws_kms_key.master_key.key_id SSM_PARAM_NAME = "${var.ssm_param_path}/derived-key-info" } } }
4. ECS Fargate权限配置
# ECS任务执行角色 resource "aws_iam_role" "ecs_task_execution_role" { name = "ecs-task-execution-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ecs-tasks.amazonaws.com" } }] }) } # ECS基础执行权限 resource "aws_iam_role_policy_attachment" "ecs_task_execution_basic" { role = aws_iam_role.ecs_task_execution_role.name policy_arn = "arn:aws-cn:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" } # ECS读取密钥权限 resource "aws_iam_policy" "ecs_key_access_policy" { name = "ecs-key-access-policy" description = "允许ECS任务读取SSM加密参数、KMS解密" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = ["kms:Decrypt","kms:DescribeKey"] Effect = "Allow" Resource = aws_kms_key.master_key.arn }, { Action = ["ssm:GetParameter"] Effect = "Allow" Resource = "arn:aws-cn:ssm:${var.region}:${data.aws_caller_identity.current.account_id}:parameter${var.ssm_param_path}/derived-key-info" } ] }) } resource "aws_iam_role_policy_attachment" "ecs_key_access_attach" { role = aws_iam_role.ecs_task_execution_role.name policy_arn = aws_iam_policy.ecs_key_access_policy.arn }
ECS任务定义引用密钥示例
在ECS任务定义的secrets字段直接引用SSM参数,AWS会自动解密后注入容器环境变量:
"secrets": [ { "name": "DERIVED_KEY_INFO", "valueFrom": "arn:aws-cn:ssm:cn-northwest-1:123456789012:parameter/business/key/derived-key-info" } ]
内容的提问来源于stack exchange,提问作者Mert Alnuaimi
相关产品推荐
相关产品推荐

