如何使用承担角色将来自另一个 AWS 帐户的 S3 对象放入您自己的帐户 S3 存储桶中?
How to PUT S3 objects from another AWS account into your own account S3 bucket using assume role?
我有一个非常典型的用例,我在 AWS 帐户(1234567890 - 不受我控制)中获得了一个角色,可以从他们的 S3 存储桶 ('remote_bucket') 中读取数据。我可以很好地从远程存储桶中读取数据,但我无法再将其转储到我自己的存储桶中,因为在另一个 AWS 账户中承担角色 "hides" 会授予我自己账户上的资源。最后一行因错误而失败。如何解决?
import boto3
# Create IAM client and local session
sts = boto3.client('sts')
local_sess = boto3.Session()
s3_local = local_sess.resource('s3')
role_to_assume_arn='arn:aws:iam::1234567890:role/s3_role'
role_session_name='test'
# Assume role in another account to access their S3 bucket
response = sts.assume_role(
RoleArn = role_to_assume_arn,
RoleSessionName = 'test',
ExternalId = 'ABCDEFG12345'
)
creds=response['Credentials']
# open session in another account:
assumed_sess = boto3.Session(
aws_access_key_id=creds['AccessKeyId'],
aws_secret_access_key=creds['SecretAccessKey'],
aws_session_token=creds['SessionToken'],
)
remote_bucket = 'remote_bucket'
s3_assumed = assumed_sess.resource('s3')
bk_assumed = s3_assumed.Bucket(remote_bucket)
for o in bk_assumed.objects.filter(Prefix="prefix/"):
print(o.key)
in_object = s3_assumed.Object(remote_bucket, o.key)
content = in_object.get()['Body'].read()
s3_local.Object('my_account_bucket', o.key).put(Body=content)
错误:
botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
(TL;DR 看到最后一个比使用角色更容易配置权限的方法!)
在存储桶之间移动数据的最简单方法是使用 copy_object()
命令。此命令发送到 目标存储桶 和来自源存储桶的 "pulls" 信息。
当涉及多个 AWS 账户时,这会稍微复杂一些,因为同一组凭据需要对源存储桶的 GetObject
权限 AND PutObject
目标存储桶的权限。
您的情况似乎是:
GetObject
已通过您可以承担的角色授予对源存储桶的权限
- 但是,该角色对目标存储桶没有
PutObject
权限
如果还为角色分配了写入目标存储桶的权限,这一点很重要。
为了测试这种情况,我做了以下操作:
- 在
Account-A
(源存储桶)中创建了 Bucket-A
并上传了一些测试文件
- 已在
Account-B
(目标存储桶)中创建 Bucket-B
- 在
Account-A
中创建Role-A
,指定它可以被Account-B
使用
- 已将此 IAM 策略分配给
Role-A
:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:List*",
"s3:Get*"
],
"Resource": [
"arn:aws:s3:::bucket-a",
"arn:aws:s3:::bucket-a/*"
]
},
{
"Effect": "Allow",
"Action": [
"s3:Put*"
],
"Resource": [
"arn:aws:s3:::bucket-b/*"
]
}
]
}
请注意,该角色也已获得写入 Bucket-B
的权限。这可能不会出现在您的特定情况下,但这是必要的,否则 Account-A
将不允许角色调用 Bucket-B
!
澄清一下:使用cross-account权限时,两个账户都需要授予权限。在这种情况下,Account-A
授予 Role-A
写入 Bucket-B
的权限,但 Account-B
也必须允许写入(请参阅下面的存储桶策略)。
- 在
Account-B
中创建 User-B
并授予在 Account-A
中的 Role-A
上调用 AssumeRole
的权限:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AssumeRoleA",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::111111111111:role/role-a"
}
]
}
- 在
Bucket-B
上创建了 存储桶策略 允许 Role-A
将文件放入存储桶。这很重要,因为 Account-A
无权访问 Account-B
中的资源,但此存储桶策略将允许 Role-A
使用该存储桶。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::bucket-b/*",
"Principal": {
"AWS": [
"arn:aws:iam::111111111111:role/role-a"
]
}
}
]
}
- 更新了我的凭据文件以使用
Role-A
:
[role-a]
role_arn = arn:aws:iam::906972072995:role/role-a
source_profile = user-b
- 已确认我可以访问
Bucket-A
:
aws s3 ls s3://bucket-a --profile role-a
- 确认我可以将对象从
Bucket-A
复制到 Bucket-B
:
aws s3 cp s3://bucket-a/foo.txt s3://stack-bucket-b/ --profile role-a
这成功了。
总结
上述过程可能看起来相当复杂,但它可以很容易地分为源和目标:
- 来源: 提供可以从
Bucket-A
读取的 Role-A
,也具有写入 Bucket-B
的权限
- 目标:
Bucket-B
上的存储桶策略允许 Role-A
写入它
如果Account-A
不愿意提供既可以读取Bucket-A
又可以写入Bucket-B
的角色权限,那么还有一个选择:
- 要求
Account-A
添加存储桶策略到Bucket-A
允许User-B
到GetObject
User-B
然后可以使用普通 (User-B
) 权限从 Bucket-A
读取内容,而无需 AssumeRole
- 这显然比担任角色要容易得多
我有一个非常典型的用例,我在 AWS 帐户(1234567890 - 不受我控制)中获得了一个角色,可以从他们的 S3 存储桶 ('remote_bucket') 中读取数据。我可以很好地从远程存储桶中读取数据,但我无法再将其转储到我自己的存储桶中,因为在另一个 AWS 账户中承担角色 "hides" 会授予我自己账户上的资源。最后一行因错误而失败。如何解决?
import boto3
# Create IAM client and local session
sts = boto3.client('sts')
local_sess = boto3.Session()
s3_local = local_sess.resource('s3')
role_to_assume_arn='arn:aws:iam::1234567890:role/s3_role'
role_session_name='test'
# Assume role in another account to access their S3 bucket
response = sts.assume_role(
RoleArn = role_to_assume_arn,
RoleSessionName = 'test',
ExternalId = 'ABCDEFG12345'
)
creds=response['Credentials']
# open session in another account:
assumed_sess = boto3.Session(
aws_access_key_id=creds['AccessKeyId'],
aws_secret_access_key=creds['SecretAccessKey'],
aws_session_token=creds['SessionToken'],
)
remote_bucket = 'remote_bucket'
s3_assumed = assumed_sess.resource('s3')
bk_assumed = s3_assumed.Bucket(remote_bucket)
for o in bk_assumed.objects.filter(Prefix="prefix/"):
print(o.key)
in_object = s3_assumed.Object(remote_bucket, o.key)
content = in_object.get()['Body'].read()
s3_local.Object('my_account_bucket', o.key).put(Body=content)
错误:
botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
(TL;DR 看到最后一个比使用角色更容易配置权限的方法!)
在存储桶之间移动数据的最简单方法是使用 copy_object()
命令。此命令发送到 目标存储桶 和来自源存储桶的 "pulls" 信息。
当涉及多个 AWS 账户时,这会稍微复杂一些,因为同一组凭据需要对源存储桶的 GetObject
权限 AND PutObject
目标存储桶的权限。
您的情况似乎是:
GetObject
已通过您可以承担的角色授予对源存储桶的权限- 但是,该角色对目标存储桶没有
PutObject
权限
如果还为角色分配了写入目标存储桶的权限,这一点很重要。
为了测试这种情况,我做了以下操作:
- 在
Account-A
(源存储桶)中创建了Bucket-A
并上传了一些测试文件 - 已在
Account-B
(目标存储桶)中创建Bucket-B
- 在
Account-A
中创建Role-A
,指定它可以被Account-B
使用
- 已将此 IAM 策略分配给
Role-A
:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:List*",
"s3:Get*"
],
"Resource": [
"arn:aws:s3:::bucket-a",
"arn:aws:s3:::bucket-a/*"
]
},
{
"Effect": "Allow",
"Action": [
"s3:Put*"
],
"Resource": [
"arn:aws:s3:::bucket-b/*"
]
}
]
}
请注意,该角色也已获得写入 Bucket-B
的权限。这可能不会出现在您的特定情况下,但这是必要的,否则 Account-A
将不允许角色调用 Bucket-B
!
澄清一下:使用cross-account权限时,两个账户都需要授予权限。在这种情况下,Account-A
授予 Role-A
写入 Bucket-B
的权限,但 Account-B
也必须允许写入(请参阅下面的存储桶策略)。
- 在
Account-B
中创建User-B
并授予在Account-A
中的Role-A
上调用AssumeRole
的权限:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AssumeRoleA",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::111111111111:role/role-a"
}
]
}
- 在
Bucket-B
上创建了 存储桶策略 允许Role-A
将文件放入存储桶。这很重要,因为Account-A
无权访问Account-B
中的资源,但此存储桶策略将允许Role-A
使用该存储桶。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::bucket-b/*",
"Principal": {
"AWS": [
"arn:aws:iam::111111111111:role/role-a"
]
}
}
]
}
- 更新了我的凭据文件以使用
Role-A
:
[role-a]
role_arn = arn:aws:iam::906972072995:role/role-a
source_profile = user-b
- 已确认我可以访问
Bucket-A
:
aws s3 ls s3://bucket-a --profile role-a
- 确认我可以将对象从
Bucket-A
复制到Bucket-B
:
aws s3 cp s3://bucket-a/foo.txt s3://stack-bucket-b/ --profile role-a
这成功了。
总结
上述过程可能看起来相当复杂,但它可以很容易地分为源和目标:
- 来源: 提供可以从
Bucket-A
读取的Role-A
,也具有写入Bucket-B
的权限
- 目标:
Bucket-B
上的存储桶策略允许Role-A
写入它
如果Account-A
不愿意提供既可以读取Bucket-A
又可以写入Bucket-B
的角色权限,那么还有一个选择:
- 要求
Account-A
添加存储桶策略到Bucket-A
允许User-B
到GetObject
User-B
然后可以使用普通 (User-B
) 权限从Bucket-A
读取内容,而无需 AssumeRole- 这显然比担任角色要容易得多