使用 CDK 定义 L2 构造的权限 AWS - AWS Cloud Development Kit (AWS CDK) v2

这是 AWS CDK v2 开发者指南。旧版 CDK v1 于 2022 年 6 月 1 日进入维护阶段,并于 2023 年 6 月 1 日终止支持。

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用 CDK 定义 L2 构造的权限 AWS

使用 Cloud Development Ki AWS t (CDK) 时,为 L2 结构定义 AWS 身份和访问管理 (IAM) 角色和策略。AWS

使用授予方法定义权限

使用构造库中的 L2 构 AWS 造定义基础设施时,您可以使用提供的授权方法来指定您的资源所需的权限。 AWS CDK 将自动为所有需要这些角色的 AWS 资源创建所需的 IAM 角色。

以下示例定义了 AWS Lambda 函数和亚马逊简单存储服务 (Amazon S3) 存储桶之间的权限。在本例中,存储桶 L2 构造的 grants.read 方法将用于定义以下权限:

TypeScript
import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as s3 from 'aws-cdk-lib/aws-s3'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as kms from 'aws-cdk-lib/aws-kms'; export class CdkDemoStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const key = new kms.Key(this, 'BucketKey'); const bucket = new s3.Bucket(this, 'Bucket', { encryptionKey: key, }); const handler = new lambda.Function(this, 'Handler', { runtime: lambda.Runtime.NODEJS_20_X, handler: 'index.handler', code: lambda.Code.fromAsset('lambda'), }); // Define permissions between function and S3 bucket using the read method from BucketGrants bucket.grants.read(handler); } }
JavaScript
const { Stack, Duration } = require('aws-cdk-lib'); const s3 = require('aws-cdk-lib/aws-s3'); const lambda = require('aws-cdk-lib/aws-lambda'); const kms = require('aws-cdk-lib/aws-kms'); class CdkDemoStack extends Stack { constructor(scope, id, props) { super(scope, id, props); const key = new kms.Key(this, 'BucketKey'); const bucket = new s3.Bucket(this, 'Bucket', { encryptionKey: key, }); const handler = new lambda.Function(this, 'Handler', { runtime: lambda.Runtime.NODEJS_20_X, handler: 'index.handler', code: lambda.Code.fromAsset('lambda'), }); // Define permissions between function and S3 bucket using read method from BucketGrants bucket.grants.read(handler); } } // ...
Python
from aws_cdk import ( Stack, aws_s3 as s3, aws_lambda as _lambda, aws_kms as kms, ) from constructs import Construct class CdkDemoStack(Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) key = kms.Key(self, 'BucketKey') bucket = s3.Bucket(self, 'Bucket') handler = _lambda.Function( self, 'Handler', runtime = _lambda.Runtime.NODEJS_20_X, handler = 'index.handler', code = _lambda.Code.from_asset('lambda'), ) # Define permissions between function and S3 bucket using the read method from BucketGrants bucket.grants.read(handler)
Java
package com.myorg; import software.amazon.awscdk.core.App; import software.amazon.awscdk.core.Stack; import software.amazon.awscdk.core.StackProps; import software.amazon.awscdk.services.kms.Key; import software.amazon.awscdk.services.kms.KeyProps; import software.amazon.awscdk.services.s3.Bucket; import software.amazon.awscdk.services.s3.BucketProps; import software.amazon.awscdk.services.lambda.Function; import software.amazon.awscdk.services.lambda.FunctionProps; import software.amazon.awscdk.services.lambda.Runtime; import software.amazon.awscdk.services.lambda.Code; import software.constructs.Construct; public class CdkDemoStack extends Stack { public CdkDemoStack(final Construct scope, final String id) { this(scope, id, null); } public CdkDemoStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); Key key = new Key(this, "BucketKey", KeyProps.builder().build()); Bucket bucket = new Bucket(this, "Bucket", BucketProps.builder() .encryptionKey(key) .build()); Function handler = new Function(this, "Handler", FunctionProps.builder() .runtime(Runtime.NODEJS_20_X) .handler("index.handler") .code(Code.fromAsset("lambda")) .build()); // Define permissions between function and S3 bucket using the read method from BucketGrants bucket.getGrants().read(handler); } public static void main(final String[] args) { App app = new App(); new CdkDemoStack(app, "CdkDemoStack"); app.synth(); } }
C#
using Amazon.CDK; using Amazon.CDK.AWS.KMS; using Amazon.CDK.AWS.S3; using Amazon.CDK.AWS.Lambda; namespace CdkDemo { public class CdkDemoStack : Stack { internal CdkDemoStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { var key = new Key(this, "BucketKey"); var bucket = new Bucket(this, "Bucket", new BucketProps { EncryptionKey = key }); var handler = new Function(this, "Handler", new FunctionProps { Runtime = Runtime.NODEJS_20_X, Handler = "index.handler", Code = Code.FromAsset("lambda") }); // Define permissions between function and S3 bucket using the Read method from BucketGrants bucket.Grants.Read(handler); } } }
Go
package main import ( "github.com/aws/aws-cdk-go/awscdk/v2" "github.com/aws/aws-cdk-go/awscdk/v2/awskms" "github.com/aws/aws-cdk-go/awscdk/v2/awss3" "github.com/aws/aws-cdk-go/awscdk/v2/awslambda" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) // ... func NewCdkDemoStack(scope constructs.Construct, id string, props *CdkDemoStackProps) awscdk.Stack { stack := awscdk.NewStack(scope, &id, &props.StackProps) key := awskms.NewKey(stack, jsii.String("BucketKey"), nil) bucket := awss3.NewBucket(stack, jsii.String("Bucket"), &awss3.BucketProps{ EncryptionKey: key, }) handler := awslambda.NewFunction(stack, jsii.String("Handler"), &awslambda.FunctionProps{ Runtime: awslambda.Runtime_NODEJS_20_X(), Handler: jsii.String("index.handler"), Code: awslambda.Code_FromAsset(jsii.String("lambda"), &awss3assets.AssetOptions{}), }) bucket.Grants().Read(handler) return stack } // ...

当您使用 L2 结构的授予方法来定义资源之间的权限时, AWS CDK 将根据您指定的方法创建具有最低权限策略的角色。作为一项安全最佳实践,我们建议您使用仅应用您所需权限的方法。例如,如果您只需要授予 Lambda 函数从 Amazon S3 存储桶读取数据的权限,请使用 grants.read 方法而不是 grants.readWrite

对于您使用的每种方法,CDK 都会为指定资源创建唯一的 IAM 角色。如有必要,您还可以直接修改将附加到该角色的策略。以下是示例:

TypeScript
import { aws_iam as iam } from 'aws-cdk-lib'; handler.addToRolePolicy(new iam.PolicyStatement({ actions: ['s3:GetObject', 's3:List*'], resources: [ bucket.bucketArn, bucket.arnForObjects('*'), ] }));
JavaScript
const iam = require('aws-cdk-lib/aws-iam'); handler.addToRolePolicy(new iam.PolicyStatement({ actions: ['s3:GetObject', 's3:List*'], resources: [ bucket.bucketArn, bucket.arnForObjects('*'), ] }));
Python
from aws_cdk import aws_iam as iam handler.add_to_role_policy(iam.PolicyStatement( actions=['s3:GetObject', 's3:List*'], resources=[ bucket.bucket_arn, bucket.arn_for_objects('*'), ] ))
Java
import software.amazon.awscdk.services.iam.PolicyStatement; import software.amazon.awscdk.services.iam.PolicyStatementProps; handler.addToRolePolicy(PolicyStatement.Builder.create() .actions(Arrays.asList("s3:GetObject", "s3:List*")) .resources(Arrays.asList( bucket.getBucketArn(), bucket.arnForObjects("*") )) .build());
C#
using Amazon.CDK.AWS.IAM; using Amazon.CDK.AWS.S3; using Amazon.CDK.AWS.Lambda; handler.AddToRolePolicy(new PolicyStatement(new PolicyStatementProps { Actions = new[] { "s3:GetObject", "s3:List*" }, Resources = new[] { bucket.BucketArn, bucket.ArnForObjects("*") } }));
Go
package main import ( // ... "github.com/aws/aws-cdk-go/awscdk/v2/awsiam" // ... ) // ... func NewCdkDemoStack(scope constructs.Construct, id string, props *CdkDemoStackProps) awscdk.Stack { // ... handler.AddToRolePolicy(awsiam.NewPolicyStatement(&awsiam.PolicyStatementProps{ Actions: jsii.Strings("s3:GetObject", "s3:List*"), Resources: jsii.Strings(bucket.BucketArn(), bucket.ArnForObjects("*")), })) // ... }

但是,我们建议您在可用时使用该grants属性中的方法。

手动创建和使用 IAM 角色

如果您不想使用 CDK grant 方法创建和管理权限,则必须手动创建和配置权限。您可以使用 AWS 管理控制台、 AWS CLI 或创建 IAM 角色 AWS SDKs。然后,您可以手动将它们传入 CDK 应用程序中,也可以使用角色自定义功能。

手动引用和管理所有角色

需要角色的构造有一个可选 role 属性,可用于传入角色对象。

手动引用角色
  1. 使用 Role.fromRoleName() 引用已有色。以下是示例:

    const existingRole = Role.fromRoleName(stack, 'Role', 'my-pre-existing-role', { mutable: false // Prevent CDK from attempting to add policies to this role })
  2. 在定义资源时传递已有角色。以下是示例:

    const handler = new lambda.Function(stack, 'Handler', { runtime: lambda.Runtime.NODEJS_20_XZ, handler: 'index.handler', code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')), // Pass in pre-existing role role: existingRole, });

使用角色自定义功能

AWS CDK 角色自定义功能会生成 CDK 应用程序中角色和策略的报告。您可以使用此功能生成报告。然后您可以用预先创建的角色代替它们。

使用角色自定义功能
  1. 在 CDK 应用程序顶部某处添加 Role.customizeRoles()。以下是示例:

    const stack = new Stack(app, 'LambdaStack'); // Add this to use the role customization feature iam.Role.customizeRoles(stack); // Define your resources using L2 constructs const key = new kms.Key(stack, 'BucketKey'); const bucket = new s3.Bucket(stack, 'Bucket', { encryptionKey: key, }); const handler = new lambda.Function(stack, 'Handler', { runtime: lambda.Runtime.NODEJS_16_X, handler: 'index.handler', code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')), }); // The grants.read() is still important. Even though it actually doesn't mutate // any policies, it indicates the need for them. bucket.grants.read(handler);
  2. 合成应用程序时,CDK 会抛出一个错误,表示您需要向 Role.customizeRoles() 提供预先创建的角色名。以下是生成的报告的示例:

    <missing role> (LambdaStack/Handler/ServiceRole)
    
    AssumeRole Policy:
    [
      {
        "Action": "sts:AssumeRole",
        "Effect": "Allow",
        "Principal": {
          "Service": "lambda.amazonaws.com"
        }
      }
    ]
    
    Managed Policy ARNs:
    [
      "arn:(PARTITION):iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
    ]
    
    Managed Policies Statements:
    NONE
    
    Identity Policy Statements:
    [
      {
        "Action": [
          "s3:GetObject*",
          "s3:GetBucket*",
          "s3:List*"
        ],
        "Effect": "Allow",
        "Resource": [
          "(LambdaStack/Bucket/Resource.Arn)",
          "(LambdaStack/Bucket/Resource.Arn)/*"
        ]
      }
    ]
  3. 创建角色后,您可以将其传入应用程序中以应用于资源。例如,如果为 LambdaStack/Handler/ServiceRole 创建的角色名称为 lambda-service-role,则应按如下方式更新 CDK 应用程序:

    const stack = new Stack(app, 'LambdaStack'); // Add this to pass in the role iam.Role.customizeRoles(stack, { usePrecreatedRoles: { 'LambdaStack/Handler/ServiceRole': 'lambda-service-role', }, });

    现在,CDK 将在 CDK 应用程序中引用角色的任何位置使用预先创建的角色名称。它还将继续生成报告,以便可以参考未来的任何策略更改。

    您会注意到,报告中对 Amazon S3 存储桶 ARN 的引用显示为(LambdaStack/Bucket/Resource.Arn),而不是存储桶的实际 ARN。这是因为存储桶 ARN 是一个部署时间值,在合成时(存储桶尚未创建)未知。这是我们为何建议允许 CDK 使用提供的 grant 方法管理 IAM 角色和权限的又一个示例。要使用初始策略创建角色,管理员必须创建具有更广泛权限的策略(例如,arn:aws:s3:::*)。