Upload to S3 bucket using Pre-Signed URL – "The AWS Access Key Id you provided does not exist in our records."

huangapple go评论72阅读模式
英文:

Upload to S3 bucket using Pre-Signed URL - "The AWS Access Key Id you provided does not exist in our records."

问题

Context:
我有一个EC2实例和一个S3存储桶。
EC2实例正在运行一个提供通过GET请求生成预签名URL的Web服务器。
请求预签名URL的客户端需要将文件上传到S3存储桶。
根据在线教程,我创建了一个具有完整S3访问权限的IAM角色,并将其添加到了我的EC2实例上。使用AWS CLI,我可以列出存储桶。

问题:
外部客户端可以访问此REST端点并获取预签名URL及其相关字段,但无法上传文件。

以下是服务器端代码:

import requests
import json
import boto3
import pydantic
from botocore.exceptions import ClientError
from botocore.client import Config
import datetime

URL = "http://169.254.169.254/latest/meta-data/iam/security-credentials/my-iam-role-s3"
access_key = ""
secret_key = ""
bucket_name = "my-bucket-name-s3"

def get_aws_creds():
    global access_key
    global secret_key
    try:
        response = requests.get(URL)
        response.raise_for_status()
        json_response = json.loads(response.text)
        access_key = json_response["AccessKeyId"]
        secret_key = json_response["SecretAccessKey"]
    except requests.exceptions.HTTPError as err:
        print(err)
        raise SystemExit(err)
    return access_key, secret_key

def get_pre_signed_upload_URL(uuid :str):
    global access_key
    global secret_key

    if (access_key == "" or secret_key == ""):
        access_key, secret_key = get_aws_creds()
    s3_client = boto3.client(
        's3',
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        region_name="us-west-1",
        config=Config(signature_version='s3v4'))
    try:
        response = s3_client.generate_presigned_post(
            Bucket=bucket_name,
            Key=uuid,
            ExpiresIn=50
        )
    except ClientError as e:
        return None
    return response

调用EC2上的REST端点的客户端能够接收到以下内容(已编辑信息):

{
    'url': 'https://bucket-name.s3.amazonaws.com/',
    'fields': {
        'key': 'some-uuid',
        'x-amz-algorithm': 'AWS4-HMAC-SHA256',
        'x-amz-credential': 'ABCDEFGH/20230628/us-west-1/s3/aws4_request',
        'x-amz-date': '20230628T202210Z',
        'policy': 'POLICY-BLOB',
        'x-amz-signature': 'signature-hash'
    }
}

我使用以下命令在我的计算机上执行curl命令 - 我已经收到了URL和Fields中的信息,称为upload_object

curl_upload_command = f'curl -X POST {upload_object["url"]}'
for key, value in upload_object['fields'].items():
    curl_upload_command += f' -F "{key}={value}"'
curl_upload_command += f' -F "file=@{post_body["filename"]}"'

# 使用curl上传文件
print(curl_upload_command)
upload_result = subprocess.run(curl_upload_command, shell=True, capture_output=True, text=True)

print(upload_result.stdout)

我收到的响应如下:

<Error><Code>InvalidAccessKeyId</Code><Message>The AWS Access Key Id you provided does not exist in our records.</Message><AWSAccessKeyId>ASIA****************</AWSAccessKeyId><RequestId>W8GW0J0PE0BWK5RZ</RequestId><HostId>HOST-ID******</HostId></Error>

非常感谢您的任何指导。

注意:EC2实例具有IAM角色,用于生成预签名URL,而公共客户端正尝试使用它来进行文件上传。

英文:

Context:
I have an EC2, and and S3 bucket.
The EC2 is running a webserver that provides a Pre-Signed URL through a GET Request.
The client requesting the pre-signed URL form the Webserver is to upload the file to the S3 bucket.
Based on the tutorials online, I created an IAM role with full S3 access and added that to my EC2. With AWS CLI I am able to list the bucket.

Problem:
The external client can reach this REST endpoint and get the pre-signed URL and the fields associated with it. But not able to upload the file.

Here is the server side code:

import json
import boto3
import pydantic
from botocore.exceptions import ClientError
from botocore.client import Config
import datetime


URL = &quot;http://169.254.169.254/latest/meta-data/iam/security-credentials/my-iam-role-s3&quot;
access_key = &quot;&quot;
secret_key = &quot;&quot;
bucket_name = &quot;my-bucket-name-s3&quot;

def get_aws_creds():
    global access_key
    global secret_key
    try:
        response = requests.get(URL)
        response.raise_for_status()
        json_response = json.loads(response.text)
        access_key = json_response[&quot;AccessKeyId&quot;]
        secret_key = json_response[&quot;SecretAccessKey&quot;]
    except requests.exceptions.HTTPError as err:
        print(err)
        raise SystemExit(err)
    return access_key, secret_key


def get_pre_signed_upload_URL(uuid :str):
    global access_key
    global secret_key

    if (access_key == &quot;&quot; or secret_key == &quot;&quot;):
        access_key, secret_key = get_aws_creds()
    s3_client = boto3.client(
        &#39;s3&#39;,
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        region_name=&quot;us-west-1&quot;,
        config=Config(signature_version=&#39;s3v4&#39;))
    try:
        response = s3_client.generate_presigned_post(
            Bucket=bucket_name,
            Key=uuid,
            ExpiresIn=50
             )
    except ClientError as e:
        return None
    return response

The client calling the REST endpoint on EC2 is able to receive the following (information redacted):

{
	&#39;url&#39;: &#39;https://bucket-name.s3.amazonaws.com/&#39;,
	&#39;fields&#39;: {
		&#39;key&#39;: &#39;some-uuid&#39;,
		&#39;x-amz-algorithm&#39;: &#39;AWS4-HMAC-SHA256&#39;,
		&#39;x-amz-credential&#39;: &#39;ABCDEFGH/20230628/us-west-1/s3/aws4_request&#39;,
		&#39;x-amz-date&#39;: &#39;20230628T202210Z&#39;,
		&#39;policy&#39;: &#39;POLICY-BLOB&#39;,
		&#39;x-amz-signature&#39;: &#39;signature-hash&#39;
	}
}

I use the following to perform a curl command on my computer - I've already received the URL and Fields in upload_object:

curl_upload_command = f&#39;curl -X POST {upload_object[&quot;url&quot;]}&#39;
for key, value in upload_object[&#39;fields&#39;].items():
    curl_upload_command += f&#39; -F &quot;{key}={value}&quot;&#39;
curl_upload_command += f&#39; -F &quot;file=@{post_body[&quot;filename&quot;]}&quot;&#39;

# Upload the file using curl
print(curl_upload_command)
upload_result = subprocess.run(curl_upload_command, shell=True, capture_output=True, text=True)

print(upload_result.stdout)

The response I get is as follows:

&lt;Error&gt;&lt;Code&gt;InvalidAccessKeyId&lt;/Code&gt;&lt;Message&gt;The AWS Access Key Id you provided does not exist in our records.&lt;/Message&gt;&lt;AWSAccessKeyId&gt;ASIA****************&lt;/AWSAccessKeyId&gt;&lt;RequestId&gt;W8GW0J0PE0BWK5RZ&lt;/RequestId&gt;&lt;HostId&gt;HOST-ID******&lt;/HostId&gt;&lt;/Error&gt;

Any guidance here will greatly be appreciated.

Note: EC2 with IAM role is generating the pre-signed URL and a public client is trying to POST a file with it.

答案1

得分: 2

"An IAM Role does not have a permanent Access Key + Security Key,"所以你传递给该函数的任何内容都是无效的。

AWS Security Token Service的GetAccessKeyInfo“以ASIA开头的访问密钥ID是使用AWS STS操作创建的临时凭证。”

相反,你的程序应该调用AssumeRole(),同时传递IAM角色ARN。这将返回一个临时访问密钥、秘密密钥和安全令牌。然后可以使用这些值生成预签名URL。

请注意,预签名URL只在扮演角色有效的时间内才有效(默认为60分钟)。

此外,程序需要使用一组AWS凭据来调用AssumeRole()。这些可以是分配给EC2实例的IAM角色自动生成的自动凭据。

英文:

An IAM Role does not have a permanent Access Key + Security Key, so whatever you are passing to that function is invalid.

From GetAccessKeyInfo - AWS Security Token Service: "Access key IDs beginning with ASIA are temporary credentials that are created using AWS STS operations."

Instead, your program should call AssumeRole() while passing the IAM Role ARN. This will return a temporary Access Key, Secret Key and Security Token. These values can then be used to generate the pre-signed URL.

Note that the pre-signed URL will only be valid for the duration that the Assumed Role is valid (which defaults to 60 minutes).

Also, the program will need to use a set of AWS credentials to call AssumeRole(). These can be the automatic credentials generated by the IAM Role assigned to the EC2 instance.

huangapple
  • 本文由 发表于 2023年6月29日 05:17:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76576761.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定