摘要
如上所述,我想创建一个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("/")
],
}),
]
})
},
});