这是 AWS CDK v2 开发者指南。较旧的 CDK v1 于 2022 年 6 月 1 日进入维护阶段,并于 2023 年 6 月 1 日终止支持。
本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
权限和 AWS CDK
AWS 构造库使用一些广泛实现的常见习语来管理访问和权限。IAM 模块可为您提供使用这些习语所需的工具。
AWS CDK 使用 AWS CloudFormation 部署更改。每个部署都涉及启动 AWS CloudFormation 部署的角色(开发人员或自动化系统)。在此过程中,角色将担任一个或多个 IAM 身份(用户或角色),并可选择将角色传递给 AWS CloudFormation。
如果您使用 AWS IAM Identity Center 以用户身份进行身份验证,则单点登录提供商会提供短期会话凭证,授权您充当预定义的 IAM 角色。要了解 AWS CDK 如何通过 IAM Identity Center 身份验证获取 AWS 凭证,请参阅《AWS SDKs and Tools Reference Guide》中的 Understand IAM Identity Center authentication。
主体
IAM 主体是经过身份验证的 AWS 实体,代表可以调用 AWS API 的用户、服务或应用程序。AWS 构造库支持以多种灵活的方式指定主体,以向其授予对 AWS 资源的访问权限。
在安全上下文中,“主体”一词专门指经过身份验证的实体,例如用户。组和角色之类的对象并不代表用户(以及其他经过身份验证的实体),而是间接识别他们的身份以授予权限。
例如,如果您创建一个 IAM 组,则可以向该组(以及其成员)授予对 Amazon RDS 表的写入权限。但是,组本身不是主体,因为它不代表单个实体(而且,您无法登录组)。
在 CDK 的 IAM 库中,直接或间接识别主体身份的类实现了 IPrincipal
接口,从而允许这些对象在访问策略中互换使用。但是,从安全意义上讲,并非所有对象都是主体。这些对象包括:
授权
表示可以访问的资源(例如 Amazon S3 存储桶或 Amazon DynamoDB 表)的每个构造都有向其他实体授予访问权限的方法。所有这些方法的名称都以 grant 开头。
例如,Amazon S3 存储桶具有方法 grantRead
和 grantReadWrite
(Python:grant_read
和 grant_read_write
),用于分别启用实体对存储桶的读取和读/写访问权限。该实体不必确切知道执行这些操作需要哪些 Amazon S3 IAM 权限。
grant 方法的第一个参数始终为 IGrantable 类型。此接口表示可以被授予权限的实体。也就是说,它表示具有角色的资源,例如 IAM 对象 Role
、User
和 Group
。
也可以向其他实体授予权限。例如,在本主题的后面部分,我们将介绍如何向 CodeBuild 项目授予对 Amazon S3 存储桶的访问权限。通常,关联角色是通过被授予访问权限的实体的 role
属性获得的。
使用执行角色的资源(例如 lambda.Function
)也会实现 IGrantable
,因此您可以直接向其授予访问权限,而不必向其角色授予访问权限。例如,如果 bucket
是 Amazon S3 存储桶,并且 function
是 Lambda 函数,则以下代码会向函数授予对存储桶的读取访问权限。
- TypeScript
-
bucket.grantRead(function);
- JavaScript
-
bucket.grantRead(function);
- Python
-
bucket.grant_read(function)
- Java
-
bucket.grantRead(function);
- C#
-
bucket.GrantRead(function);
有时,必须在部署堆栈时应用权限。其中一种情况是您向 AWS CloudFormation 自定义资源授予对其他资源的访问权限时。该自定义资源将在部署期间被调用,因此它在部署时必须具有指定的权限。
另一种情况是当服务验证您传递给它的角色是否应用了正确的策略时。(许多 AWS 服务通过这种方式确保您不会忘记设置策略。) 在这种情况下,如果权限应用得太晚,部署可能会失败。
要在创建其他资源之前强制应用 grant 要授予的权限,您可以向 grant 本身添加依赖项,如下所示。尽管通常会丢弃 grant 方法的返回值,但实际上每个 grant 方法都会返回一个 iam.Grant
对象。
- TypeScript
-
const grant = bucket.grantRead(lambda);
const custom = new CustomResource(...);
custom.node.addDependency(grant);
- JavaScript
-
const grant = bucket.grantRead(lambda);
const custom = new CustomResource(...);
custom.node.addDependency(grant);
- Python
-
grant = bucket.grant_read(function)
custom = CustomResource(...)
custom.node.add_dependency(grant)
- Java
-
Grant grant = bucket.grantRead(function);
CustomResource custom = new CustomResource(...);
custom.node.addDependency(grant);
- C#
-
var grant = bucket.GrantRead(function);
var custom = new CustomResource(...);
custom.node.AddDependency(grant);
角色
IAM 程序包包含一个代表 IAM 角色的 Role
构造。以下代码会创建一个信任 Amazon EC2 服务的新角色。
- TypeScript
-
import * as iam from 'aws-cdk-lib/aws-iam';
const role = new iam.Role(this, 'Role', {
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), // required
});
- JavaScript
-
const iam = require('aws-cdk-lib/aws-iam');
const role = new iam.Role(this, 'Role', {
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com') // required
});
- Python
-
import aws_cdk.aws_iam as iam
role = iam.Role(self, "Role",
assumed_by=iam.ServicePrincipal("ec2.amazonaws.com")) # required
- Java
-
import software.amazon.awscdk.services.iam.Role;
import software.amazon.awscdk.services.iam.ServicePrincipal;
Role role = Role.Builder.create(this, "Role")
.assumedBy(new ServicePrincipal("ec2.amazonaws.com")).build();
- C#
-
using Amazon.CDK.AWS.IAM;
var role = new Role(this, "Role", new RoleProps
{
AssumedBy = new ServicePrincipal("ec2.amazonaws.com"), // required
});
您可以通过调用角色的 addToPolicy
的方法(Python:add_to_policy
),传入定义要添加的规则的 PolicyStatement
来为该角色添加权限。该语句将添加到角色的默认策略中;如果角色没有策略,则会创建一个。
在授权服务为 AWS CodeBuild 的条件下,以下示例会针对资源 bucket
和 otherRole
(Python:other_role
)上的 ec2:SomeAction
和 s3:AnotherAction
操作,向角色添加 Deny
策略语句。
- TypeScript
-
role.addToPolicy(new iam.PolicyStatement({
effect: iam.Effect.DENY,
resources: [bucket.bucketArn, otherRole.roleArn],
actions: ['ec2:SomeAction', 's3:AnotherAction'],
conditions: {StringEquals: {
'ec2:AuthorizedService': 'codebuild.amazonaws.com',
}}}));
- JavaScript
-
role.addToPolicy(new iam.PolicyStatement({
effect: iam.Effect.DENY,
resources: [bucket.bucketArn, otherRole.roleArn],
actions: ['ec2:SomeAction', 's3:AnotherAction'],
conditions: {StringEquals: {
'ec2:AuthorizedService': 'codebuild.amazonaws.com'
}}}));
- Python
-
role.add_to_policy(iam.PolicyStatement(
effect=iam.Effect.DENY,
resources=[bucket.bucket_arn, other_role.role_arn],
actions=["ec2:SomeAction", "s3:AnotherAction"],
conditions={"StringEquals": {
"ec2:AuthorizedService": "codebuild.amazonaws.com"}}
))
- Java
-
role.addToPolicy(PolicyStatement.Builder.create()
.effect(Effect.DENY)
.resources(Arrays.asList(bucket.getBucketArn(), otherRole.getRoleArn()))
.actions(Arrays.asList("ec2:SomeAction", "s3:AnotherAction"))
.conditions(java.util.Map.of( // Map.of requires Java 9 or later
"StringEquals", java.util.Map.of(
"ec2:AuthorizedService", "codebuild.amazonaws.com")))
.build());
- C#
-
role.AddToPolicy(new PolicyStatement(new PolicyStatementProps
{
Effect = Effect.DENY,
Resources = new string[] { bucket.BucketArn, otherRole.RoleArn },
Actions = new string[] { "ec2:SomeAction", "s3:AnotherAction" },
Conditions = new Dictionary<string, object>
{
["StringEquals"] = new Dictionary<string, string>
{
["ec2:AuthorizedService"] = "codebuild.amazonaws.com"
}
}
}));
在前面的示例中,我们使用 addToPolicy
(Python:add_to_policy
)调用创建了一个新的 PolicyStatement
内联策略。您也可以传入现有的或您修改过的策略语句。PolicyStatement 对象支持许多方法,可用于添加主体、资源、条件和操作。
如果要使用的构造需要角色才能正常运行,您可以执行以下操作之一:
- TypeScript
-
import * as codebuild from 'aws-cdk-lib/aws-codebuild';
// imagine roleOrUndefined is a function that might return a Role object
// under some conditions, and undefined under other conditions
const someRole: iam.IRole | undefined = roleOrUndefined();
const project = new codebuild.Project(this, 'Project', {
// if someRole is undefined, the Project creates a new default role,
// trusting the codebuild.amazonaws.com service principal
role: someRole,
});
- JavaScript
-
const codebuild = require('aws-cdk-lib/aws-codebuild');
// imagine roleOrUndefined is a function that might return a Role object
// under some conditions, and undefined under other conditions
const someRole = roleOrUndefined();
const project = new codebuild.Project(this, 'Project', {
// if someRole is undefined, the Project creates a new default role,
// trusting the codebuild.amazonaws.com service principal
role: someRole
});
- Python
-
import aws_cdk.aws_codebuild as codebuild
# imagine role_or_none is a function that might return a Role object
# under some conditions, and None under other conditions
some_role = role_or_none();
project = codebuild.Project(self, "Project",
# if role is None, the Project creates a new default role,
# trusting the codebuild.amazonaws.com service principal
role=some_role)
- Java
-
import software.amazon.awscdk.services.iam.Role;
import software.amazon.awscdk.services.codebuild.Project;
// imagine roleOrNull is a function that might return a Role object
// under some conditions, and null under other conditions
Role someRole = roleOrNull();
// if someRole is null, the Project creates a new default role,
// trusting the codebuild.amazonaws.com service principal
Project project = Project.Builder.create(this, "Project")
.role(someRole).build();
- C#
-
using Amazon.CDK.AWS.CodeBuild;
// imagine roleOrNull is a function that might return a Role object
// under some conditions, and null under other conditions
var someRole = roleOrNull();
// if someRole is null, the Project creates a new default role,
// trusting the codebuild.amazonaws.com service principal
var project = new Project(this, "Project", new ProjectProps
{
Role = someRole
});
创建对象后,角色(无论是传入的角色还是构造创建的默认角色)即可作为属性 role
使用。但是,此属性不可用于外部资源。因此,这些构造支持 addToRolePolicy
(Python:add_to_role_policy
)方法。
如果构造是外部资源,则该方法不执行任何操作,否则它会调用 role
属性的 addToPolicy
(Python:add_to_policy
)方法。这为您省去了显式处理未定义案例的麻烦。
以下示例说明了:
- TypeScript
-
// project is imported into the CDK application
const project = codebuild.Project.fromProjectName(this, 'Project', 'ProjectName');
// project is imported, so project.role is undefined, and this call has no effect
project.addToRolePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW, // ... and so on defining the policy
}));
- JavaScript
-
// project is imported into the CDK application
const project = codebuild.Project.fromProjectName(this, 'Project', 'ProjectName');
// project is imported, so project.role is undefined, and this call has no effect
project.addToRolePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW // ... and so on defining the policy
}));
- Python
-
# project is imported into the CDK application
project = codebuild.Project.from_project_name(self, 'Project', 'ProjectName')
# project is imported, so project.role is undefined, and this call has no effect
project.add_to_role_policy(iam.PolicyStatement(
effect=iam.Effect.ALLOW, # ... and so on defining the policy
)
- Java
-
// project is imported into the CDK application
Project project = Project.fromProjectName(this, "Project", "ProjectName");
// project is imported, so project.getRole() is null, and this call has no effect
project.addToRolePolicy(PolicyStatement.Builder.create()
.effect(Effect.ALLOW) // .. and so on defining the policy
.build();
- C#
-
// project is imported into the CDK application
var project = Project.FromProjectName(this, "Project", "ProjectName");
// project is imported, so project.role is null, and this call has no effect
project.AddToRolePolicy(new PolicyStatement(new PolicyStatementProps
{
Effect = Effect.ALLOW, // ... and so on defining the policy
}));
资源策略
AWS 中的一些资源(例如 Amazon S3 存储桶和 IAM 角色)也有资源策略。这些构造支持 addToResourcePolicy
方法(Python:add_to_resource_policy
),它将 PolicyStatement
作为其参数。添加到资源策略的每条策略语句都必须指定至少一个主体。
在以下示例中,Amazon S3 存储桶 bucket
会向自身授予包含 s3:SomeAction
权限的角色。
- TypeScript
-
bucket.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:SomeAction'],
resources: [bucket.bucketArn],
principals: [role]
}));
- JavaScript
-
bucket.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:SomeAction'],
resources: [bucket.bucketArn],
principals: [role]
}));
- Python
-
bucket.add_to_resource_policy(iam.PolicyStatement(
effect=iam.Effect.ALLOW,
actions=["s3:SomeAction"],
resources=[bucket.bucket_arn],
principals=role))
- Java
-
bucket.addToResourcePolicy(PolicyStatement.Builder.create()
.effect(Effect.ALLOW)
.actions(Arrays.asList("s3:SomeAction"))
.resources(Arrays.asList(bucket.getBucketArn()))
.principals(Arrays.asList(role))
.build());
- C#
-
bucket.AddToResourcePolicy(new PolicyStatement(new PolicyStatementProps
{
Effect = Effect.ALLOW,
Actions = new string[] { "s3:SomeAction" },
Resources = new string[] { bucket.BucketArn },
Principals = new IPrincipal[] { role }
}));
使用外部 IAM 对象
如果您在 AWS CDK 应用程序之外定义了 IAM 用户、主体、组或角色,则可以在 AWS CDK 应用程序中使用这些 IAM 对象。为此,请使用对象的 ARN 或名称创建对其的引用。(使用用户、组和角色的名称。) 然后,返回的引用可用于授予权限或构造策略语句,如前所述。
可以使用以下方法以类似方式使用策略(包括托管策略)。您可以在任何需要 IAM 策略的位置使用对这些对象的引用。
与对所有外部 AWS 资源的引用一样,您无法在 CDK 应用程序中修改外部 IAM 对象。