Skip to content

Having trouble with POST request for uploading S3 objects

0

I have a Lambda written in python3 that generates two URLs: one presigned post URL, and another presigned URL for getting the S3 HTML hosted in the same bucket from that post URL response. Here's the form content that I generate an HTML file for the presigned POST URL:

<html>
          <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
          </head>
          <body>

          <form action="{upload_form_url['url']}" method="post" enctype="multipart/form-data">
            Key to upload:
            <input type="input"  name="key" value="{upload_form_url['fields']['key']}" /><br />
            <input type="hidden" name="acl" value="public-read" />
            <input type="hidden" name="success_action_redirect" value="{upload_form_url['url']}/successful_upload.html" />
            Content-Type:
            <input type="input"  name="Content-Type" value="application/json" /><br />
            <input type="hidden" name="x-amz-meta-uuid" value="14365123651274" />
            <input type="hidden" name="x-amz-server-side-encryption" value="AES256" />
            <input type="text"   name="X-Amz-Credential" value="{upload_form_url['fields']['AWSAccessKeyId']}/20151229/us-east-1/s3/aws4_request" />
            <input type="text"   name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" />
            <input type="text"   name="X-Amz-Date" value="20151229T000000Z" />

            Tags for File:
            <input type="input"  name="x-amz-meta-tag" value="" /><br />
            <input type="hidden" name="Policy" value='{upload_form_url['fields']['policy']}' />
            <input type="hidden" name="X-Amz-Signature" value="{upload_form_url['fields']['signature']}" />
            File:
            <input type="file"   name="file" /> <br />
            <!-- The elements after this will be ignored -->
            <input type="submit" name="submit" value="Upload to Amazon S3" />
          </form>

        </html>

where upload_form_url is the response from a generate_presigned_post.

I think it's probably not related to the form itself, but I still wanted to show it. The error I get when uploading the file on this form is:

<Error>
<Code>InvalidAccessKeyId</Code>
<Message>The AWS Access Key Id you provided does not exist in our records.</Message>
<AWSAccessKeyId>xxxxxxxxxxx</AWSAccessKeyId>
<RequestId>.....</RequestId>
<HostId>......</HostId>
</Error>

which is probably more related to how I'm generating credentials. I get this regardless of whether I'm executing this on the cloud or locally.
But for the life of me, I cannot figure out what I'm doing wrong. I'm tried assuming a role, I've tried setting up different session, but I keep getting this same key ID error. This is how I'm setting up the boto3 client, as of my latest attempt:


session = boto3.Session(
    aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
    aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
    aws_session_token=os.environ["AWS_SESSION_TOKEN"]
)

s3_client=session.client('s3')

I also read online that the access key ID must start with AKIA, instead of ASIA, but all access key IDs start with ASIA. Maybe that's where I'm going wrong? I'm really not sure, and I'd appreciate any help. Thanks in advances

1 Answer
1

Hello.

Access keys starting with "ASIA" are temporary access keys issued in STS events.
https://docs.aws.amazon.com/STS/latest/APIReference/API_GetAccessKeyInfo.html

If you are issuing a signed URL with Lambda, an IAM role will be used, so there is no need to set an access key in the code.

session = boto3.Session(
    aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
    aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
    aws_session_token=os.environ["AWS_SESSION_TOKEN"]
)

By the way, is the request successful with a "curl" command using a URL created with Lambda instead of an HTML form?
In my environment, I have confirmed that I can create a URL using the Lambda below and upload it using the curl command.

import boto3

s3_client = boto3.client('s3',region_name="ap-northeast-1",endpoint_url="https://s3.ap-northeast-1.amazonaws.com")

def lambda_handler(event, context):
    url = s3_client.generate_presigned_post(
        Bucket="bucket-name",
        Key="test.txt",
        Conditions=[
            {
                "acl":"public-read"
            }
        ],
        ExpiresIn=3600
    )
    print(url)
    return url

The response looks like this:

{
  "url": "https://s3.ap-northeast-1.amazonaws.com/bucket-name",
  "fields": {
    "key": "test",
    "AWSAccessKeyId": "ASIAxxxxxxxxx",
    "x-amz-security-token": "xxxxxxxxxxxxxx",
    "policy": "xxxxxxxxxxxxxxx",
    "signature": "xxxxxxxxxxxx"
  }
}

The curl command looks like this:

curl -X POST https://s3.ap-northeast-1.amazonaws.com/bucket-name \
-F 'key=test' \
-F 'acl=public-read' \
-F 'AWSAccessKeyId=ASIAxxxxxx' \
-F 'policy=xxxxxxxx' \
-F 'x-amz-security-token=xxxxxx' \
-F 'signature=xxxxxxxxx=' \
-F file=@kobayashi.txt
EXPERT
answered 2 years ago
EXPERT
reviewed 2 years ago
EXPERT
reviewed 2 years ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.