【问题标题】:How to delete S3 objects/versions on MFA protected bucket using boto3?如何使用 boto3 删除 MFA 保护存储桶上的 S3 对象/版本?
【发布时间】:2021-07-07 18:54:46
【问题描述】:

我有一个名为 protected-bucket 的版本化 S3 存储桶,我想以编程方式删除对象或版本(有时只是某些版本)。 Bucket 附加了以下策略,强制 MFA 在即将执行 Delete* 操作时出现:

{
    "Sid": "RequireMFAForDelete",
    "Effect": "Deny",
    "Principal": {
        "AWS": "*"
    },
    "Action": "s3:Delete*",
    "Resource": "arn:aws:s3:::protected-bucket/*",
    "Condition": {
        "Bool": {
            "aws:MultiFactorAuthPresent": "false"
        }
    }
}

我还尝试按照https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html#example-bucket-policies-use-case-7 页面上的建议使用存储桶策略中的"Condition": { "Null": { "aws:MultiFactorAuthAge": true }}。从下面遇到同样的问题...

这是一个最小的 Python3 代码,它应该删除我上面提到的存储桶中的对象版本:

#!/usr/bin/env python3
import boto3
from datetime import datetime


mfa_totp = input("Enter the MFA code: ")

session_name='my-test-session-' + str(int(datetime.utcnow().timestamp()))
client=boto3.client('sts', 'us-east-1')
ar_res = client.assume_role(
    RoleArn='arn:aws:iam::123456789102:role/test-role',
    RoleSessionName=session_name,
    DurationSeconds=900,
    SerialNumber='arn:aws:iam::987654321098:mfa/my_user_name',
    TokenCode=mfa_totp,
)
print(ar_res)
tmp_creds = ar_res["Credentials"]

s3_client = boto3.client("s3", "us-east-1",
                         aws_access_key_id=tmp_creds["AccessKeyId"],
                         aws_secret_access_key=tmp_creds["SecretAccessKey"],
                         aws_session_token=tmp_creds["SessionToken"])

s3_bucket = "protected-bucket"
s3_key = "test/test4.txt"
s3_version = "XYZXbHbi3lpCNlOM8peIim6gi.IZQJqM"

# If I put code here that lists objects in 
if s3_version:
    response = s3_client.delete_object(Bucket=s3_bucket,
                                       Key=s3_key,
                                       VersionId=s3_version)
else:
    response = s3_client.delete_object(Bucket=s3_bucket,Key=s3_key)

print(response)

我得到的错误如下:

Traceback (most recent call last):
  File "./del_test.py", line 37, in <module>
    response = s3_client.delete_object(Bucket=s3_bucket,
  File "/home/dejan/py/myproj/lib64/python3.8/site-packages/botocore/client.py", line 386, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/home/dejan/py/myproj/lib64/python3.8/site-packages/botocore/client.py", line 705, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the DeleteObject operation: Access Denied

注意事项:

  • 我假设的角色是不同的帐户(有些人可能已经注意到 Python 代码中的不同帐号。
  • 该角色在附加到该角色的策略中允许删除* 操作。当我删除存储桶策略中的 MFA 保护时,上面的 Python 3 代码可以工作 - 它可以删除对象和版本。

【问题讨论】:

  • 文档说TokenCode 是“MFA 设备提供的值,如果代入角色的信任策略需要 MFA。”该角色的信任策略是否需要 MFA?角色上的 MFA 可能不会转换为实际 API 调用的 MFA。您可能需要调用 get_session_token() 来对 MFA 设备进行身份验证。
  • 我也有同样的怀疑,但我不知道如何证实我的怀疑...我需要承担角色,因为只有该角色才能删除存储桶中的对象。那么您是否建议我使用 get_session_token(),然后承担返回凭据的角色?我一直觉得假设角色()做同样的事情,唯一的区别是几个额外的参数(假设的角色等)......
  • 您可能需要执行相反的操作...假设 IAM 角色具有必要的权限,然后 GetSessionToken 保留相同的权限集但允许指定 MFA 令牌。然后,使用生成的凭据执行删除。
  • GetSessionToken 不能使用会话凭据调用。我相信反过来:首先获取 MFA 会话,然后将这些凭据传递给新的 STS 客户端以执行 AssumeRole,然后使用这些新凭据进行删除。

标签: amazon-web-services amazon-s3 boto3


【解决方案1】:

原来我错过了https://docs.amazonaws.cn/en_us/IAM/latest/UserGuide/id_credentials_mfa_configure-api-require.html文档中给出的关键信息:

AssumeRole 返回的临时凭证在上下文中不包含 MFA 信息,因此您无法检查单个 API 操作的 MFA。这就是为什么您必须使用 GetSessionToken 来限制对受基于资源的策略保护的资源的访问。

简而言之,如果我只是假设_role(),with MFA,就像我在问题中提出的Python代码中所做的那样,MFA数据不会被传递下去,所以get_session_token()是一个必须...以下重构代码(在我的同事@Chadwick 的帮助下制作)按预期工作:

#!/usr/bin/env python3
import boto3
from datetime import datetime


mfa_serial = "arn:aws:iam::987654321098:mfa/my_user_name"
role_to_assume = "arn:aws:iam::123456789102:role/test-role"

mfa_totp = input("Enter the MFA code: ")
mfa_sts_client = boto3.client("sts", "us-east-1")
mfa_credentials = mfa_sts_client.get_session_token(
    SerialNumber=mfa_serial,
    TokenCode=mfa_totp,
)["Credentials"]

session_name='my-test-session-' + str(int(datetime.utcnow().timestamp()))
# We now create a client with credentials from the MFA enabled session we created above:
ar_sts_client=boto3.client("sts", "us-east-1",
                           aws_access_key_id=mfa_credentials["AccessKeyId"],
                           aws_secret_access_key=mfa_credentials["SecretAccessKey"],
                           aws_session_token=mfa_credentials["SessionToken"])
ar_res = ar_sts_client.assume_role(
    RoleArn=role_to_assume,
    RoleSessionName=session_name,
    DurationSeconds=900
)
print(ar_res)
tmp_creds = ar_res["Credentials"]

s3_client = boto3.client("s3", "us-east-1",
                         aws_access_key_id=tmp_creds["AccessKeyId"],
                         aws_secret_access_key=tmp_creds["SecretAccessKey"],
                         aws_session_token=tmp_creds["SessionToken"])


s3_bucket = "protected-bucket"
s3_key = "test/test4.txt"
s3_version = "YYFMqnLaVEosoZ1Zk3Xy8dVbNGQVEF35"
# s3_version = None

if s3_version:
    response = s3_client.delete_object(Bucket=s3_bucket,
                                       Key=s3_key,
                                       VersionId=s3_version)
else:
    response = s3_client.delete_object(Bucket=s3_bucket,Key=s3_key)

print(response)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-20
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 2015-06-30
    • 2020-07-07
    • 2018-03-29
    相关资源
    最近更新 更多