#amazon-web-services #amazon-s3 #boto3
Вопрос:
У меня есть версионная корзина S3 с именем protected-bucket
, и я хочу программно удалить объекты или версии (иногда только некоторые версии). К ведру прилагается следующая политика, которая обеспечивает присутствие МИД при выполнении действий по удалению* :
{
"Sid": "RequireMFAForDelete",
"Effect": "Deny",
"Principal": {
"AWS": "*"
},
"Action": "s3:Delete*",
"Resource": "arn:aws:s3:::protected-bucket/*",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
Я также попытался использовать "Condition": { "Null": { "aws:MultiFactorAuthAge": true }}
политику «в корзине», как было предложено на https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html#example-bucket-policies-use-case-7 страница. У меня та же проблема снизу…
Вот минимальный код 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 работает — он может удалять объекты и версии.
Комментарии:
1. В документации говорится, что
TokenCode
это «Значение, предоставляемое устройством MFA, если для политики доверия предполагаемой роли требуется MFA». Требуется ли для политики доверия этой роли MFA? Возможно, что MFA для роли не преобразуется в MFA для фактических вызовов API. Возможно, вам потребуется позвонитьget_session_token()
, чтобы пройти аутентификацию на устройстве MFA.2. Я подозревал то же самое, но не знаю, как подтвердить свои подозрения… Мне нужно взять на себя роль, так как только эта роль может удалять объекты в корзине. Итак, вы предлагаете мне использовать get_session_token (), а затем взять на себя роль с возвращенными учетными данными? У меня всегда было впечатление, что assume_role() делает то же самое, с той лишь разницей, что у него несколько дополнительных параметров (роль, которую нужно взять на себя и т. Д.)…
3. Вам, вероятно, придется сделать обратное… Примите роль IAM с необходимыми разрешениями, затем GetSessionToken, который сохраняет тот же набор разрешений, но позволяет указывать токен MFA. Затем используйте полученные учетные данные для выполнения Удаления.
4.
GetSessionToken
не может быть вызван с учетными данными сеанса. Я считаю , что все наоборот: сначала получите сеанс MFA, затем передайте эти учетные данные новому клиенту STSAssumeRole
, а затем используйте эти новые учетные данные для удаления.
Ответ №1:
Оказалось, что я пропустил ключевую часть информации, приведенной в https://docs.amazonaws.cn/en_us/IAM/latest/UserGuide/id_credentials_mfa_configure-api-require.html документ:
Временные учетные данные, возвращаемые AssumeRole, не содержат сведений о MFA в контексте, поэтому вы не можете проверять отдельные операции API для MFA. Вот почему вы должны использовать GetSessionToken для ограничения доступа к ресурсам, защищенным политиками на основе ресурсов.
Короче говоря, если я просто предположу_роль () с помощью 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)