繁体   English   中英

使用 django-storages 和 boto3 在 Django 中调用 CreateMultipartUpload 操作时出现 AccessDenied

[英]AccessDenied when calling the CreateMultipartUpload operation in Django using django-storages and boto3

我想使用django-storages将我的模型文件存储在 Amazon S3 中,但出现Access Denied错误。 我已授予用户几乎所有资源的 S3 权限 PutObject、ListBucketMultipartUploads、ListMultipartUploadParts、AbortMultipartUpload 权限等,但这并没有解决问题。

设置.py

...
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_S3_REGION_NAME = 'eu-west-1'
AWS_S3_CUSTOM_DOMAIN = 'www.xyz.com'
AWS_DEFAULT_ACL = None
AWS_STORAGE_BUCKET_NAME = 'www.xyz.com'
...

使用 Django shell,我尝试使用如下所示的存储系统。

Python 3.6.6 (default, Sep 12 2018, 18:26:19)
[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> import os
>>> AWS_ACCESS_KEY_ID = os.environ.get( 'AWS_ACCESS_KEY_ID', 'anything' )
>>> AWS_SECRET_ACCESS_KEY = os.environ.get( 'AWS_SECRET_ACCESS_KEY', 'anything' )
>>> AWS_DEFAULT_ACL = 'public-read'
>>> from django.core.files.storage import default_storage
>>> file = default_storage.open('test', 'w')
...
>>> file.write('storage contents')
2018-09-27 16:41:42,596 botocore.hooks [DEBUG] Event before-parameter-build.s3.CreateMultipartUpload: calling handler <function validate_ascii_metadata at 0x7fdb5e848d08>
2018-09-27 16:41:42,596 botocore.hooks [DEBUG] Event before-parameter-build.s3.CreateMultipartUpload: calling handler <function sse_md5 at 0x7fdb5e848158>
2018-09-27 16:41:42,597 botocore.hooks [DEBUG] Event before-parameter-build.s3.CreateMultipartUpload: calling handler <function validate_bucket_name at 0x7fdb5e8480d0>
2018-09-27 16:41:42,597 botocore.hooks [DEBUG] Event before-parameter-build.s3.CreateMultipartUpload: calling handler <bound method S3RegionRedirector.redirect_from_cache of <botocore.utils.S3RegionRedirector object at 0x7fdb5c5d1128>>
2018-09-27 16:41:42,597 botocore.hooks [DEBUG] Event before-parameter-build.s3.CreateMultipartUpload: calling handler <function generate_idempotent_uuid at 0x7fdb5e846c80>
2018-09-27 16:41:42,598 botocore.hooks [DEBUG] Event before-call.s3.CreateMultipartUpload: calling handler <function add_expect_header at 0x7fdb5e848598>
2018-09-27 16:41:42,598 botocore.hooks [DEBUG] Event before-call.s3.CreateMultipartUpload: calling handler <bound method S3RegionRedirector.set_request_url of <botocore.utils.S3RegionRedirector object at 0x7fdb5c5d1128>>
2018-09-27 16:41:42,598 botocore.endpoint [DEBUG] Making request for OperationModel(name=CreateMultipartUpload) with params: {'url_path': '/www.xyz.com/test?uploads', 'query_string': {}, 'method': 'POST', 'headers': {'Content-Type': 'application/octet-stream', 'User-Agent': 'Boto3/1.7.80 Python/3.6.6 Linux/4.14.67-66.56.amzn1.x86_64 Botocore/1.11.1 Resource'}, 'body': b'', 'url': 'https://s3.eu-west-1.amazonaws.com/www.xyz.com/test?uploads', 'context': {'client_region': 'eu-west-1', 'client_config': <botocore.config.Config object at 0x7fdb5c8e80b8>, 'has_streaming_input': False, 'auth_type': None, 'signing': {'bucket': 'www.xyz.com'}}}
2018-09-27 16:41:42,599 botocore.hooks [DEBUG] Event request-created.s3.CreateMultipartUpload: calling handler <bound method RequestSigner.handler of <botocore.signers.RequestSigner object at 0x7fdb5c8db780>>
2018-09-27 16:41:42,599 botocore.hooks [DEBUG] Event choose-signer.s3.CreateMultipartUpload: calling handler <bound method ClientCreator._default_s3_presign_to_sigv2 of <botocore.client.ClientCreator object at 0x7fdb5cabff98>>
2018-09-27 16:41:42,599 botocore.hooks [DEBUG] Event choose-signer.s3.CreateMultipartUpload: calling handler <function set_operation_specific_signer at 0x7fdb5e846b70>
2018-09-27 16:41:42,599 botocore.hooks [DEBUG] Event before-sign.s3.CreateMultipartUpload: calling handler <function fix_s3_host at 0x7fdb5e983048>
2018-09-27 16:41:42,600 botocore.utils [DEBUG] Checking for DNS compatible bucket for: https://s3.eu-west-1.amazonaws.com/www.xyz.com/test?uploads
2018-09-27 16:41:42,600 botocore.utils [DEBUG] Not changing URI, bucket is not DNS compatible: www.xyz.com
2018-09-27 16:41:42,601 botocore.auth [DEBUG] Calculating signature using v4 auth.
2018-09-27 16:41:42,601 botocore.auth [DEBUG] CanonicalRequest:
POST
/www.xyz.com/test
uploads=
content-type:application/octet-stream
host:s3.eu-west-1.amazonaws.com
x-amz-content-sha256:e3b0c44298fc1c149afbf343ddd27ae41e4649b934ca495991b7852b855
x-amz-date:20180927T164142Z

content-type;host;x-amz-content-sha256;x-amz-date
e3b0c44298fc1c149afb65gdfg33441e4649b934ca495991b7852b855
2018-09-27 16:41:42,601 botocore.auth [DEBUG] StringToSign:
AWS4-HMAC-SHA256
20180927T164142Z
20180927/eu-west-1/s3/aws4_request
8649ef591fb64412e923359a4sfvvffdd6d00915b9756d1611b38e346ae
2018-09-27 16:41:42,602 botocore.auth [DEBUG] Signature:
61db9afe5f87730a75692af5a95ggffdssd6f4e8e712d85c414edb14f
2018-09-27 16:41:42,602 botocore.endpoint [DEBUG] Sending http request: <AWSPreparedRequest stream_output=False, method=POST, url=https://s3.eu-west-1.amazonaws.com/www.xyz.com/test?uploads, headers={'Content-Type': b'application/octet-stream', 'User-Agent': b'Boto3/1.7.80 Python/3.6.6 Linux/4.14.67-66.56.amzn1.x86_64 Botocore/1.11.1 Resource', 'X-Amz-Date': b'20180927T164142Z', 'X-Amz-Content-SHA256': b'e3b0c44298fc1c149afbf4c8996fbdsdsffdss649b934ca495991b7852b855', 'Authorization': b'AWS4-HMAC-SHA256 Credential=X1234567890/20180927/eu-west-1/s3/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date, Signature=61db9afe5f87730a7sdfsdfs20b7137cf5d6f4e8e712d85c414edb14f', 'Content-Length': '0'}>
2018-09-27 16:41:42,638 botocore.parsers [DEBUG] Response headers: {'x-amz-request-id': '9E879E78E4883471', 'x-amz-id-2': 'ZkCfOMwLoD08Yy4Nzfxsdfdsdfds3y9wLxzqFw+o3175I+QEdtdtAi8vIEH1vi9iq9VGUC98GqlE=', 'Content-Type': 'application/xml', 'Transfer-Encoding': 'chunked', 'Date': 'Thu, 27 Sep 2018 16:41:42 GMT', 'Server': 'AmazonS3'}
2018-09-27 16:41:42,639 botocore.parsers [DEBUG] Response body:
b'<?xml version="1.0" encoding="UTF-8"?>\n<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>9E879E78E4883471</RequestId><HostId>ZkCfOMwLoD08Yy4Nzfxo8RpzsdfsdfsxzqFw+o3175I+QEdtdtAi8vIEH1vi9iq9VGUC98GqlE=</HostId></Error>'
2018-09-27 16:41:42,639 botocore.hooks [DEBUG] Event needs-retry.s3.CreateMultipartUpload: calling handler <botocore.retryhandler.RetryHandler object at 0x7fdb5c618ac8>
2018-09-27 16:41:42,640 botocore.retryhandler [DEBUG] No retry needed.
2018-09-27 16:41:42,640 botocore.hooks [DEBUG] Event needs-retry.s3.CreateMultipartUpload: calling handler <bound method S3RegionRedirector.redirect_from_error of <botocore.utils.S3RegionRedirector object at 0x7fdb5c5d1128>>
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python3.6/dist-packages/storages/backends/s3boto3.py", line 127, in write
    self._multipart = self.obj.initiate_multipart_upload(**parameters)
  File "/usr/local/lib/python3.6/dist-packages/boto3/resources/factory.py", line 520, in do_action
    response = action(self, *args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/boto3/resources/action.py", line 83, in __call__
    response = getattr(parent.meta.client, operation_name)(**params)
  File "/usr/local/lib/python3.6/dist-packages/botocore/client.py", line 314, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/usr/local/lib/python3.6/dist-packages/botocore/client.py", line 612, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the CreateMultipartUpload operation: Access Denied

这些是我正在使用的版本。

boto3==1.7.80
botocore==1.11.1
Django==2.1
s3transfer==0.1.13
django-storages==1.7.1

为什么会引发异常?

事实证明,我必须指定一个策略来添加使用存储桶下任何对象/*权限。

...
"Resource": [
            "arn:aws:s3:::www.xyz.com"
            ]
...

...
"Resource": [
            "arn:aws:s3:::www.xyz.com/*"
            ]
...

我也遇到了这个错误,但我犯了一个不同的错误。 django-storages 函数正在创建具有“公共读取”ACL 的对象。 这是默认设置,这对于 Web 框架来说是有意义的,而且确实是我想要的,但是我没有在我的 IAM 策略中包含与 ACL 相关的权限。

  • 放置对象Acl
  • PutObjectVersionAcl

这个政策对我有用(它基于这个):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucketMultipartUploads",
                "s3:AbortMultipartUpload",
                "s3:PutObjectVersionAcl",
                "s3:DeleteObject",
                "s3:PutObjectAcl",
                "s3:ListMultipartUploadParts"
            ],
            "Resource": [
                "arn:aws:s3:::bucketname/*",
                "arn:aws:s3:::bucketname"
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetBucketLocation"
            ],
            "Resource": "arn:aws:s3:::bucketname"
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": "s3:ListAllMyBuckets",
            "Resource": "*"
        }
    ]
}

另一个可能的原因是您的存储桶已打开加密。 您需要添加kms:GenerateDataKeykms:Decrypt的第二个语句。 这是我的声明:

        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
               "kms:Decrypt",
               "kms:GenerateDataKey"
            ],
            "Resource": "*"
        }

请注意,我使用的是内置密钥,而不是 CMK。 有关更多信息,请参阅此处的 AWS 文档

仅供参考,另一个原因是您的目标存储桶没有正确的策略定义。

对于我的用例,我试图将 S3 文件从 AWS 账户 A 中的一个存储桶复制到 AWS 账户 B 中的另一个存储桶。我创建了一个启用此功能的角色和策略,但我没有添加允许外部 AWS 的存储桶策略写入它的角色。 我能够通过以下 AWS 文档站点解决此问题: https://aws.amazon.com/premiumsupport/knowledge-center/copy-s3-objects-account/

(如果上面的链接有效,请忽略)

如果上述链接中断,该网站会提到:

重要提示:Amazon S3 中的对象不再自动归上传它的 AWS 账户所有。 默认情况下,任何新创建的存储桶现在都启用了存储桶所有者强制设置。 在更改 Object 所有权时,使用存储桶所有者强制设置也是最佳实践。 但是,请注意,此选项会禁用所有存储桶 ACL 和存储桶中任何对象的 ACL。

通过 S3 Object 所有权中的存储桶所有者强制设置,Amazon S3 存储桶中的所有对象都自动归存储桶所有者所有。 存储桶所有者强制功能还禁用所有访问控制列表 (ACL),这简化了对存储在 S3 中的数据的访问管理。 但是,对于现有存储桶,Amazon S3 object 仍归上传它的 AWS 账户所有,除非您明确禁用 ACL。 要更改现有存储桶中对象的 object 所有权,请参阅如何更改 S3 存储桶中公有对象的所有权?

如果您现有的共享对象方法依赖于使用 ACL,则确定使用 ACL 访问对象的主体。 有关如何在禁用任何 ACL 之前查看权限的更多信息,请参阅禁用 ACL 的先决条件。

如果您无法禁用 ACL,请按照以下步骤获取对象的所有权,直到您可以调整存储桶策略:

  1. 在源账户中,创建授予 IAM 身份(用户或角色)适当权限的 AWS Identity and Access Management (IAM) 客户托管策略。 IAM 用户必须有权从源存储桶检索对象并将对象放回目标存储桶。 您可以使用类似于以下内容的 IAM 策略:

{“版本”:“2012-10-17”,“声明”:[{“效果”:“允许”,“操作”:[“s3:ListBucket”,“s3:GetObject”],“资源”:[ "arn:aws:s3:::source-DOC-EXAMPLE-BUCKET", "arn:aws:s3:::source-DOC-EXAMPLE-BUCKET/ " ] }, { "Effect": "Allow", "Action ": [ "s3:ListBucket", "s3:PutObject", "s3:PutObjectAcl" ], "资源": [ "arn:aws:s3:::destination-DOC-EXAMPLE-BUCKET", "arn:aws: s3:::destination-DOC-EXAMPLE-BUCKET/ " ] } ] } 注意:此示例 IAM 策略仅包括列出对象和跨不同账户的存储桶复制对象所需的最低权限。 您必须根据您的用例自定义允许的 S3 操作。 例如,如果用户必须复制具有 object 标记的对象,那么您还必须授予 s3:GetObjectTagging 的权限。 如果您遇到错误,请尝试以管理员用户身份执行这些步骤。

  1. 在源账户中,将客户托管策略附加到您要用于将对象复制到目标存储桶的 IAM 身份。

  2. 在目标账户中,将目标存储桶上的 S3 Object 所有权设置为首选存储桶所有者。 设置 S3 Object 所有权后,使用设置为 bucket-owner-full-control 的访问控制列表 (ACL) 上传的新对象将自动归存储桶的账户所有。

  3. 在目的账户中,修改目的桶的桶策略,授予源账户上传对象的权限。 此外,在存储桶策略中包含一个条件,要求上传 object 以将 ACL 设置为存储桶所有者完全控制。 您可以使用类似于以下的语句:

注意:将 destination-DOC-EXAMPLE-BUCKET 替换为目标存储桶的名称。 然后,将 arn:aws:iam::222222222222:user/Jane 替换为来自源账户的 IAM 身份的 Amazon 资源名称 (ARN)。

{“版本”:“2012-10-17”,“Id”:“Policy1611277539797”,“声明”:[{“Sid”:“Stmt1611277535086”,“效果”:“允许”,“主体”:{“AWS ": "arn:aws:iam::222222222222:user/Jane" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::destination-DOC-EXAMPLE-BUCKET/* ", "条件": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } } }, { "Sid": "Stmt1611277877767", "Effect": "允许", "Principal": { "AWS": "arn:aws:iam::222222222222:user/Jane" }, "Action": "s3:ListBucket", "Resource": "arn:aws:s3::: destination-DOC-EXAMPLE-BUCKET" } ] } 注意:此示例存储桶策略仅包括上传具有所需 ACL 的 object 所需的最低权限。 您必须根据您的用例自定义允许的 S3 操作。 例如,如果用户必须复制具有 object 标签的对象,您还必须授予 s3:GetObjectTagging 的权限

  1. 配置 IAM 策略和存储桶策略后,源账户中的 IAM 身份必须将对象上传到目标存储桶。 确保 ACL 设置为 bucket-owner-full-control。 例如,源 IAM 身份必须运行带有 --acl 选项的 cp AWS CLI 命令:

aws s3 cp s3://source-DOC-EXAMPLE-BUCKET/object.txt s3://destination-DOC-EXAMPLE-BUCKET/object.txt --acl bucket-owner-full-control

就我而言,使用 Github 操作上传到 s3 失败并引发类似错误 - An error occurred (AccessDenied) when calling the CreateMultipartUpload operation: Access Denied

使用 IAM 用户和 S3 存储桶对策略进行后验证,这很好,并且类似的设置适用于不同的 IAM 用户和 S3 存储桶。

由于 Github 不允许查看添加的机密,因此为 IAM 用户旋转security_credentials并在 Github 使用。 这有帮助。

我使用以下 Python 脚本收到同样的错误( An error occurred (AccessDenied) when calling the CreateMultipartUpload operation: Access Denied ):

import logging
import boto3
import datetime

def create_boto3_client(s3_id, s3_secret_key):
    try:
        logging.info(f'####### Creating boto3Client... #######')
        s3_client = boto3.resource(
                's3',
                aws_access_key_id = s3_id,
                aws_secret_access_key = s3_secret_key,
        )
        logging.info(f'####### Successfully created boto3Client #######')
    except:
        logging.error(f'####### Failed to create boto3Client  #######')
    return s3_client


def upload_file_to_s3(s3_client, s3_bucket, aws_path, blob):
    try:
        ul_start = datetime.datetime.now()
        logging.info(f'####### Starting file upload at {str(ul_start)} #######')
        config = boto3.s3.transfer.TransferConfig(multipart_threshold=1024*25, max_concurrency=10, multipart_chunksize=1024*25, use_threads=True)
        s3_client.Bucket(s3_bucket).upload_fileobj(blob, Key = aws_path, Config = config)
        ul_end = datetime.datetime.now()
        logging.info(f'####### File uploaded to AWS S3 bucket at {str(ul_end) } #######')
        ul_duration = str(ul_end - ul_start)
        logging.info(f'####### Upload duration:{str(ul_duration)} #######')
    except Exception as e:
        logging.error(f'####### Failed to upload file to AWS S3: {e}  #######')
    return ul_start, ul_end, ul_duration

就我而言, aws_path ( .upload_file(Key) ) 不正确。 指向s3_client无权访问的路径。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM