You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.10 14:15:02