为 AWS S3 中的权限指定 IAM 角色
Specifying IAM roles for permissions in AWS S3
我试图将所有 AWS Cognito 用户限制在我的 S3 存储桶中他们自己的子目录中。
我不希望他们在我的大存储桶中列出、读取或写入其他人的 subdirectories/files,我只希望他们在自己的目录中读取和写入对象。
我正在汲取灵感from this AWS documentation snippet。
这是我的政策:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-bucket"
],
"Condition": {
"StringLike": {
"s3:prefix": [
"subfolder/"
]
}
}
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::my-bucket/subfolder/${cognito-identity.amazonaws.com:sub}",
"arn:aws:s3:::my-bucket/subfolder/${cognito-identity.amazonaws.com:sub}/*"
]
}
]
}
我的代码使用 user_id = test@test.com
检索某个用户的文件,但实际上允许我检索受限文件:
import boto
# These keys are *not* hardcoded, I'm just leaving out
# the auth flow to get them from Cognito/STS as described
# here: https://mobile.awsblog.com/post/Tx2FL1QAPDE0UAH/Understanding-Amazon-Cognito-Authentication-Part-2-Developer-Authenticated-Ident
conn = boto.s3.connect_to_region('us-east-1',
aws_access_key_id=ACCESS_KEY_FROM_COGNITO,
aws_secret_access_key=SECRET_KEY_FROM_COGNITO,
security_token=SECURITY_KEY_FROM_COGNITO)
# get the bucket
b = conn.get_bucket('my-bucket', validate=False)
# try to get an object we SHOULD be able to get
k = Key(b)
k.key = 'subfolder/us-east-1:xxxx-xxxx-xxxx-xxxxxxxxx/foobar'
print "Contents:", k.get_contents_as_string() # success!
# try to get and object we SHOUDN'T be able to get
k2 = Key(b)
k2.key = 'subfolder/BLAH_BLAH/restricted'
print "Contents:", k2.get_contents_as_string() # should fail, but doesn't
不幸的是,我可以访问和阅读这两个文件的内容,但我在 AWS 博客文档 post 中遵循完全相同的模式。我也不确定为什么在 boto 连接中需要 validate=False
,但它似乎工作得很好。
我错过了什么?
编辑:作为对以下答案的回应,我已尝试将我的角色更新为以下内容,但这并没有什么不同:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-bucket"
],
"Condition": {
"StringLike": {
"s3:prefix": [
"subfolder/${cognito-identity.amazonaws.com:sub}/*"
]
}
}
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::my-bucket/subfolder/${cognito-identity.amazonaws.com:sub}/*"
]
}
]
}
我还确认我正在使用的访问凭据来自 Cognito,方法是使用从 STS 检索的 access/secret/security 令牌三元组,使用 Cognito 令牌创建 boto
IAMConnection
对象并查询与我的身份池的授权认知用户相对应的我的角色名称。这样做时,我在尝试读取此角色时遇到以下异常(这正是我未授予访问权限后应该发生的情况):
BotoServerError: BotoServerError: 403 Forbidden
<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
<Error>
<Type>Sender</Type>
<Code>AccessDenied</Code>
<Message>User: arn:aws:sts::MY_AWS_ACCT_ID:assumed-role/my_policy_role_name/session_name_here is not authorized to perform: iam:GetRole on resource: role awsNameFor_Role_Given_123012313</Message>
</Error>
<RequestId>xxx-xxxx-xxxx-xxxx-xxxxx</RequestId>
</ErrorResponse>
所以仍然不清楚为什么这不起作用。
5 件事:
- 确保您使用的是 Amazon Cognito 身份服务颁发的凭证,否则 ${cognito-identity.amazonaws.com:sub} 将为空并授予您访问所有内容的权限
- 确保您使用的 Amazon Cognito 身份凭证是在您更新策略后颁发的,该策略嵌入在凭证的会话部分,因此如果您使用的是旧凭证,它们可能没有附加当前策略.
- 您不能使用用户的用户名,必须使用 Amazon Cognito 身份 ID。因此,而不是 test@test.com 它将是身份 ID:us-east-1:beef-beef-beef-xxxxxxxxx
- 您的池有 2 个与之关联的角色,一个未经身份验证的角色和一个经过身份验证的角色。确保您在正确的角色上设置策略,在这种情况下,您似乎正在使用经过开发人员身份验证的身份,应该修改经过身份验证的角色的策略。
- 检查您的 S3 存储桶策略,如果您允许匿名访问您的存储桶,Cognito 角色策略将不会覆盖它。如果是这种情况,请关闭匿名访问。 http://docs.aws.amazon.com/AmazonS3/latest/dev/example-bucket-policies.html#example-bucket-policies-use-case-2
看起来您的代码使用的是硬编码访问密钥和秘密密钥,而不是使用 Cognito 检索凭据。要利用 Cognito,您无需为所有用户嵌入相同的访问密钥和秘密密钥,而是需要遵循 Authentication Flow and use GetId (boto reference, AWS reference) to get an identity ID and then GetCredentialsForIdentity (boto reference AWS reference) 来获取 Cognito 为当前 ID 颁发的凭据。然后将这些凭据与 boto S3 连接一起使用。
另外请务必缓存每个用户的 ID,并在对 Cognito 进行额外调用时重用它。
答案很傻。显然 S3 中的存储桶本身有自己的策略(它们被隐藏起来),但我有指令:
Principal: "*"
这导致存储桶是世界可读的。
第二个启示是,如果您使用 Condition
限制带有 s3:ListBucket
的存储桶,这并不意味着如果您列出该存储桶,您将只能获得那些结果 - 您必须直呼其名。例如 boto
:
wrong = bucket.list() # will simply 403
right = bucket.list(prefix="base/subdir/<cognito-id>/") # will succeed
换句话说,S3 的设计使得您必须知道所需文件夹的前缀键,无论如何这是一个很好的做法。
我不得不说,AWS 的人们在这里和他们的论坛上帮助诊断这个问题给我留下了深刻的印象。不管怎样,现在对 S3 有了更好的理解。
我试图将所有 AWS Cognito 用户限制在我的 S3 存储桶中他们自己的子目录中。
我不希望他们在我的大存储桶中列出、读取或写入其他人的 subdirectories/files,我只希望他们在自己的目录中读取和写入对象。
我正在汲取灵感from this AWS documentation snippet。
这是我的政策:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-bucket"
],
"Condition": {
"StringLike": {
"s3:prefix": [
"subfolder/"
]
}
}
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::my-bucket/subfolder/${cognito-identity.amazonaws.com:sub}",
"arn:aws:s3:::my-bucket/subfolder/${cognito-identity.amazonaws.com:sub}/*"
]
}
]
}
我的代码使用 user_id = test@test.com
检索某个用户的文件,但实际上允许我检索受限文件:
import boto
# These keys are *not* hardcoded, I'm just leaving out
# the auth flow to get them from Cognito/STS as described
# here: https://mobile.awsblog.com/post/Tx2FL1QAPDE0UAH/Understanding-Amazon-Cognito-Authentication-Part-2-Developer-Authenticated-Ident
conn = boto.s3.connect_to_region('us-east-1',
aws_access_key_id=ACCESS_KEY_FROM_COGNITO,
aws_secret_access_key=SECRET_KEY_FROM_COGNITO,
security_token=SECURITY_KEY_FROM_COGNITO)
# get the bucket
b = conn.get_bucket('my-bucket', validate=False)
# try to get an object we SHOULD be able to get
k = Key(b)
k.key = 'subfolder/us-east-1:xxxx-xxxx-xxxx-xxxxxxxxx/foobar'
print "Contents:", k.get_contents_as_string() # success!
# try to get and object we SHOUDN'T be able to get
k2 = Key(b)
k2.key = 'subfolder/BLAH_BLAH/restricted'
print "Contents:", k2.get_contents_as_string() # should fail, but doesn't
不幸的是,我可以访问和阅读这两个文件的内容,但我在 AWS 博客文档 post 中遵循完全相同的模式。我也不确定为什么在 boto 连接中需要 validate=False
,但它似乎工作得很好。
我错过了什么?
编辑:作为对以下答案的回应,我已尝试将我的角色更新为以下内容,但这并没有什么不同:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-bucket"
],
"Condition": {
"StringLike": {
"s3:prefix": [
"subfolder/${cognito-identity.amazonaws.com:sub}/*"
]
}
}
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::my-bucket/subfolder/${cognito-identity.amazonaws.com:sub}/*"
]
}
]
}
我还确认我正在使用的访问凭据来自 Cognito,方法是使用从 STS 检索的 access/secret/security 令牌三元组,使用 Cognito 令牌创建 boto
IAMConnection
对象并查询与我的身份池的授权认知用户相对应的我的角色名称。这样做时,我在尝试读取此角色时遇到以下异常(这正是我未授予访问权限后应该发生的情况):
BotoServerError: BotoServerError: 403 Forbidden
<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
<Error>
<Type>Sender</Type>
<Code>AccessDenied</Code>
<Message>User: arn:aws:sts::MY_AWS_ACCT_ID:assumed-role/my_policy_role_name/session_name_here is not authorized to perform: iam:GetRole on resource: role awsNameFor_Role_Given_123012313</Message>
</Error>
<RequestId>xxx-xxxx-xxxx-xxxx-xxxxx</RequestId>
</ErrorResponse>
所以仍然不清楚为什么这不起作用。
5 件事:
- 确保您使用的是 Amazon Cognito 身份服务颁发的凭证,否则 ${cognito-identity.amazonaws.com:sub} 将为空并授予您访问所有内容的权限
- 确保您使用的 Amazon Cognito 身份凭证是在您更新策略后颁发的,该策略嵌入在凭证的会话部分,因此如果您使用的是旧凭证,它们可能没有附加当前策略.
- 您不能使用用户的用户名,必须使用 Amazon Cognito 身份 ID。因此,而不是 test@test.com 它将是身份 ID:us-east-1:beef-beef-beef-xxxxxxxxx
- 您的池有 2 个与之关联的角色,一个未经身份验证的角色和一个经过身份验证的角色。确保您在正确的角色上设置策略,在这种情况下,您似乎正在使用经过开发人员身份验证的身份,应该修改经过身份验证的角色的策略。
- 检查您的 S3 存储桶策略,如果您允许匿名访问您的存储桶,Cognito 角色策略将不会覆盖它。如果是这种情况,请关闭匿名访问。 http://docs.aws.amazon.com/AmazonS3/latest/dev/example-bucket-policies.html#example-bucket-policies-use-case-2
看起来您的代码使用的是硬编码访问密钥和秘密密钥,而不是使用 Cognito 检索凭据。要利用 Cognito,您无需为所有用户嵌入相同的访问密钥和秘密密钥,而是需要遵循 Authentication Flow and use GetId (boto reference, AWS reference) to get an identity ID and then GetCredentialsForIdentity (boto reference AWS reference) 来获取 Cognito 为当前 ID 颁发的凭据。然后将这些凭据与 boto S3 连接一起使用。
另外请务必缓存每个用户的 ID,并在对 Cognito 进行额外调用时重用它。
答案很傻。显然 S3 中的存储桶本身有自己的策略(它们被隐藏起来),但我有指令:
Principal: "*"
这导致存储桶是世界可读的。
第二个启示是,如果您使用 Condition
限制带有 s3:ListBucket
的存储桶,这并不意味着如果您列出该存储桶,您将只能获得那些结果 - 您必须直呼其名。例如 boto
:
wrong = bucket.list() # will simply 403
right = bucket.list(prefix="base/subdir/<cognito-id>/") # will succeed
换句话说,S3 的设计使得您必须知道所需文件夹的前缀键,无论如何这是一个很好的做法。
我不得不说,AWS 的人们在这里和他们的论坛上帮助诊断这个问题给我留下了深刻的印象。不管怎样,现在对 S3 有了更好的理解。