能否通过 STS AssumeRole 角色链实现跨账号 S3 服务端分段复制?
结论
可以通过 STS AssumeRole 角色链取得最终角色的临时凭证,再执行跨账号 S3 服务端分段复制。用于签署复制请求的最终角色必须同时具备读取源对象和写入目标对象的有效权限。
upload_part_copy 没有接收 IAM 角色列表的参数,也不能在同一次复制请求中分别使用源端角色和目标端角色。角色链负责取得最终身份,先前角色的权限不会累加到最终角色上。AWS 角色权限说明
为什么现有两个客户端能下载再上传,却不能直接组合复制
现有服务通过两次独立请求完成操作:源端客户端读取对象,目标端客户端上传本地文件。两次请求可以分别使用不同角色的凭证。
upload_part_copy 是一条由某个身份签名的服务端复制请求。对于普通 S3 bucket,这个身份需要同时具有源对象的 s3:GetObject 和目标对象的 s3:PutObject 权限。对象数据由 S3 在服务端复制,无需经过应用磁盘。UploadPartCopy 权限要求
例如,Role A 只能读取源对象,Role B 只能写入目标对象。即使通过 Role A 承担 Role B,使用 Role B 凭证发出的复制请求也不会继承 Role A 的读取权限,还需要额外授权 Role B 读取源对象。
如何配置最终角色
一种做法是在目标账号创建 S3CopyRole,让它获得以下权限:
- 读取源 bucket 中指定前缀的对象。
- 写入目标 bucket 中指定前缀的对象。
- 中止目标端未完成的 multipart upload,以便失败时清理。
下面是附加到最终角色的身份策略示例。bucket 名称和前缀均为占位示例,需要替换:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::source-bucket/source-prefix/*"
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:AbortMultipartUpload"
],
"Resource": "arn:aws:s3:::destination-bucket/destination-prefix/*"
}
]
}
由于源 bucket 位于另一个账号,还需要在源 bucket policy 中允许最终角色 ARN 执行相应的 s3:GetObject。只给最终角色添加身份策略,通常不足以完成跨账号授权。资源端和身份端都必须允许相应访问。S3 跨账号授权说明
角色的 trust policy 负责允许调用方承担角色。它与 S3 数据访问策略用途不同:能够成功执行 AssumeRole,不代表已经获得 bucket 的读写权限。
如果调用方可以直接承担 S3CopyRole,就可以省去中间角色。只有现有身份路径要求经过中间角色时,才需要角色链。
Boto3 角色链与分段复制示例
可以通过 AWS profile 配置角色链,让 Boto3 调用 STS。以下示例假设已经存在可用的 base profile,并已配置每一跳所需的 sts:AssumeRole 权限和目标角色信任关系。
在 ~/.aws/config 中配置:
[profile bridge]
role_arn = arn:aws:iam::111111111111:role/BridgeRole
source_profile = base
region = us-east-1
[profile s3-copy]
role_arn = arn:aws:iam::222222222222:role/S3CopyRole
source_profile = bridge
duration_seconds = 3600
region = us-east-1
这里的账号 ID、角色名和 Region 都是示例值。S3CopyRole 必须已经具备前面所述的源端读取和目标端写入权限。
然后使用最终 profile 创建客户端:
import boto3
from boto3.s3.transfer import TransferConfig
session = boto3.Session(profile_name="s3-copy")
s3 = session.client("s3")
config = TransferConfig(
multipart_threshold=64 * 1024 * 1024,
multipart_chunksize=64 * 1024 * 1024,
)
s3.copy(
CopySource={
"Bucket": "source-bucket",
"Key": "source-prefix/large-file.bin",
},
Bucket="destination-bucket",
Key="destination-prefix/large-file.bin",
Config=config,
)
client.copy() 是 Boto3 的托管复制方法,会根据对象大小和传输配置在需要时执行 multipart copy。示例中的 64 MiB 是配置示例,可以按实际对象规模调整。Boto3 copy 文档
如果希望保留已有的 upload_part_copy 实现,也可以使用上述最终角色客户端执行 create_multipart_upload、各次 upload_part_copy 和 complete_multipart_upload,并在失败时调用 abort_multipart_upload。不必改用托管方法,但最终身份仍需具备所需权限。
SourceClient 能否分别提供源端和目标端凭证
client.copy() 的 SourceClient 可用于源对象相关的辅助操作,例如通过 head_object 查询对象大小,但不能解决复制请求的双端授权问题。执行复制的当前客户端仍然需要同时访问两个 bucket。即使 SourceClient 能成功读取源对象元数据,也不代表当前客户端能够执行服务端复制。SourceClient 参数说明
还需要注意什么
角色链会话最长为一小时
通过一个角色承担另一个角色时,角色链会话最长为 3600 秒,即使最终角色的最大会话时长设置得更长也一样。这个限制针对单次会话,并不意味着整个复制任务只能运行一小时。STS AssumeRole 文档
上面的 profile 方式由 Boto3 管理临时凭证,并在需要时刷新,前提是上游身份仍然有效、角色链仍然允许重新承担。如果手动调用 assume_role,并将返回的三个凭证字段直接传给客户端,则需要自行处理凭证更新。Boto3 凭证管理说明
SSE-KMS 需要额外授权
如果源对象使用 SSE-KMS,最终角色还需要源 KMS key 的解密权限;如果目标使用 SSE-KMS,分段上传还涉及目标 key 的 kms:GenerateDataKey 和 kms:Decrypt 权限。跨账号使用 KMS key 时,也要检查 key policy,不能只检查 S3 策略。分段复制的 KMS 权限要求
两个独立角色的权限无法自动合并
如果权限边界明确禁止任何一个身份同时读取源对象并写入目标对象,那么 AssumeRole 角色链无法绕过这个限制。此时需要调整授权设计,或继续使用分别认证的下载、上传流程。
备注:内容仅供参考。
