Search code examples
amazon-web-servicesaws-lambdaserverless-framework

Lambda / Pre-signed url access denied


I wrote a lambda function who return a pre-signed url for documents in S3 Buckets.

The code is really simple :

            const url = s3.getSignedUrl('getObject', {
                Bucket: BUCKET_NAME,
                Key: myFile.Key,
                Expires: 20
            })

            const response = {
                statusCode: 200,
                headers: {
                    "Access-Control-Allow-Origin": "*"
                },
                body: JSON.stringify({
                    "url": url
                }),
            };

The funny thing is when I call this function locally (with serverless framework) like this :

sls invoke local -f getEconomyFile -d '{ "queryStringParameters": { "key": "myfile.pdf" } }'

It's working ! I have a url which give me the file.

But when I deploy to AWS Lambda, the function return a URL which always says "access denied" on the file :

<Error>
    <Code>AccessDenied</Code>
    <Message>Access Denied</Message>
    <RequestId>93778EA364B3506B</RequestId>
    <HostId>
        yqnPC0SeIVE3/Pl7/d+xHDJ78=
    </HostId>
</Error>

Why is it working locally and not deployed ?

Thank you !


Solution

  • Here's a list of things to check when pre-signed URLs do not work:

    1. Check that the IAM policy of the signing credentials (the Lambda function IAM role in this case) permits access to download the S3 object in question (via s3:GetObject permission on the relevant object ARN such as arn:aws:s3:::BUCKET-NAME/*).
    2. Make sure that your IAM policy actually includes the appropriate object-level ARN such as arn:aws:s3:::BUCKET-NAME/* or arn:aws:s3:::BUCKET-NAME/images/*. It is a common mistake to indicate a bucket-level ARN such as arn:aws:s3:::BUCKET-NAME but that will not work.
    3. Check that the URL hasn't expired.
    4. Check that the credentials used to sign the URL haven't expired. This is a common problem when using temporary STS credentials to pre-sign URLs where the credentials expire before the pre-signed URL expires.
    5. Check that the client is time-synced.
    6. Check that the URL hasn't been mangled in transit or encoded in some way.

    Note on IAM credentials

    Even if your IAM credentials do not permit access to the S3 object(s), it is possible to use those credentials to create a pre-signed URL (a purely local computation**) but that URL will not actually allow you to access the object because the credentials underpinning the pre-signed URL do not have access to the object.

    ** you can tell this is a local computation and does not involve any calls into AWS by pre-signing an object such as s3://notmybucket/cat.png. That will work and generate a pre-signed URL, but it will not actually be usable to retrieve that object.