IAM角色,只允许向S3存储桶中的特定子文件夹写入对象/从中读取对象



摘要

如上所述,我想创建一个IAM角色,该角色只能将对象读取/写入S3存储桶中的特定子文件夹。

然而,尽管我在这里和这里举了的例子,我还是无法做到

政策

{
"Version": "2012-10-17",
"Statement": [
{
"Condition": {
"StringEquals": {
"s3:prefix": "levelOne=A/levelTwo=B/levelThree=C"
}
},
"Action": [
"s3:Get*",
"s3:List*",
"S3:Put*"
],
"Resource": "arn:aws:s3:::insert_bucket_name",
"Effect": "Allow"
},
{
"Condition": {
"StringNotEquals": {
"s3:prefix": "levelOne=A/levelTwo=B/levelThree=C"
}
},
"Action": [
"s3:Get*",
"s3:List*",
"S3:Put*"
],
"Resource": "arn:aws:s3:::insert_bucket_name",
"Effect": "Deny"
}
]
}

测试设置

AWS配置文件

[profile mybucketrole]
role_arn = arn:aws:iam::insert_account_id:role/insert_role_name
source_profile = default

测试命令

aws s3api put-object 
--bucket insert_bucket_name
--key levelOne=A/levelTwo=B/levelThree=C/123456789.jsonl
--profile mybucketrole

我希望上面的命令能成功,因为我正在向levelOne=A/levelTwo=B/levelThree=C前缀写入一个对象,但我一直得到一个

An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

注意:我为S3存储桶设置了CloudTrail,以验证测试命令是否正在使用预期的角色(事实上是)

我尝试过的其他事情

  • 根据将StringEquals+StringNotEquals(分别)替换为StringLike+StringNotLike

  • 将资源更改为

    "Resource": [
    "arn:aws:s3:::insert_bucket_name",
    "arn:aws:s3:::insert_bucket_name/*"
    ]
    

我错过了什么!?

Amazon S3存储桶默认情况下是私有的。因此,无需使用Deny,除非您希望覆盖其他IAM策略授予的权限(例如,阻止有权访问所有S3存储桶的管理员访问HR机密存储桶)。

因此,您可以使用这样的策略:

{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:Get*",
"s3:List*",
"S3:Put*"
],
"Resource": "arn:aws:s3:::insert_bucket_name/levelOne=A/levelTwo=B/levelThree=C/*",
"Effect": "Allow"
}
]
}

我能够通过以下权限策略和CDK代码获得我想要的行为。

注:

  • 以下所有都是基于角色的策略
  • 存储桶策略保持为空

写入策略

{
"Version": "2012-10-17",
"Statement": [
{
"Action": "s3:Put*",
"Resource": "arn:aws:s3:::insert_bucket_name/level_one=A/level_two=B/level_three=C/*",
"Effect": "Allow"
}
]
}

READ策略

{
"Version": "2012-10-17",
"Statement": [
{
"Action": "s3:Get*",
"Resource": "arn:aws:s3:::insert_bucket_name/level_one=A/level_two=B/level_three=C/*",
"Effect": "Allow"
},
// NOTE: 
// I had to be permissive here because otherwise, S3 will return an AccessDenied error instead of NoSuchKey when requesting nonexistent S3 objects. 
// See the CDK code comments for details
{
"Action": "s3:List*",
"Resource": "arn:aws:s3:::insert_bucket_name",
"Effect": "Allow"
}
]
}

CDK代码

return new Role(this, roleName, {
roleName: roleName,
assumedBy: new CompositePrincipal(new AccountPrincipal(props.stageProps.awsAccountId)),
maxSessionDuration: Duration.hours(12),
inlinePolicies: {
"READ": new PolicyDocument({
statements: [
new PolicyStatement({
effect: Effect.ALLOW,
actions: ["s3:Get*"],
resources: [
new Array(
this.[insert_bucket_variable].bucketArn,
"levelOne=A",
"levelTwo=B",
"levelThree=C",
"*",
).join("/")
],
}),
new PolicyStatement({
effect: Effect.ALLOW,
actions: ["s3:List*"],
resources: [
// First off, our resource here has no wildcards because the ListBucket API action (i.e. the API behind our favorite ListObjectsV2 boto3 call)
// acts on the bucket resource itself vs. object resources (like the (Get|Put)Object API actions). 
// However, we don't restrict visibility here because if we do, we'll see AccessDenied errors
// if/when we make a GetObject API call against the bucket for a nonexistent object 
// (see https://stackoverflow.com/a/56027548/16409315). That would be SUPER confusing to 
// debug so we want to avoid it -- even if it means that this IAM role can 
// list collections of converted labels from other labeling sources. That being said, this role still 
// wouldn't be able to actually download any of those collections because of the s3:Get* permissions so it's not a huge concession.
this.[insert_bucket_variable].bucketArn,
],
}),
]
}),
"WRITE": new PolicyDocument({
statements: [
new PolicyStatement({
effect: Effect.ALLOW,
actions: ["s3:Put*"],
resources: [
new Array(
this.[insert_bucket_variable].bucketArn,
"levelOne=A",
"levelTwo=B",
"levelThree=C",
"*",
).join("/")
],
}),
]
})
},
});

最新更新