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

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

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

使用 AWS Cloud Development Kit (AWS CDK) 时,为 L2 构造定义 AWS Identity and Access Management(IAM)角色和策略。

使用授予方法定义权限

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

以下是在 AWS Lambda 函数和 Amazon Simple Storage Service(Amazon S3)存储桶之间定义权限的示例。在本例中,存储桶 L2 构造的 grantRead 方法将用于定义以下权限:

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 grantRead method bucket.grantRead(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 grantRead method bucket.grantRead(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 grantRead method bucket.grantRead(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 grantRead method bucket.grantRead(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 grantRead method bucket.GrantRead(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.GrantRead(handler) return stack } // ...

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

对于您使用的每种方法,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("*")), })) // ...

但是,我们建议您使用 grant 方法(如可用)。

手动创建和使用 IAM 角色

如果您不想使用 CDK grant 方法创建和管理权限,则必须手动创建和配置权限。您可以使用 AWS Management Console、AWS CLI 或 AWS SDK 创建 IAM 角色。然后,您可以手动将它们传入 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 grantRead() is still important. Even though it actually doesn't mutate // any policies, it indicates the need for them. bucket.grantRead(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:::*)。