Search code examples
amazon-web-servicesamazon-s3aws-lambdaamazon-cloudfrontaws-lambda-edge

Getting "AccessDenied: Access Denied" inside lambda when getting an object from s3


Update 2

I've tried changing our custom policy to allow access to the particular bucket that's giving the AccessDenied error, without any luck. Imagine that bucket1 is a bucket that the lambda normally accesses, and bucket2 is the bucket that's throwing AccessDenied when the lambda accesses it. I've changed the Resource block from

{
        ...
        {
            "Action": [
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::us-east-1-bucket1/*",
            "Effect": "Allow"
        }
        ...

to

{
        ...
        {
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::us-east-1-bucket1/*",
                "arn:aws:s3:::us-east-1-bucket2/*"
            ],
            "Effect": "Allow"
        }
        ...

I'm still getting the AccessDenied error.


Update

Per @Caldazar's comment about IAM users vs roles, the lambda's execution role has 3 policies:

  • the AWS managed arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess, specifically:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "xray:PutTraceSegments",
                "xray:PutTelemetryRecords",
                "xray:GetSamplingRules",
                "xray:GetSamplingTargets",
                "xray:GetSamplingStatisticSummaries"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
  • the AWS managed arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole, specifically:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}
  • our custom policy, specifically:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "logs:*"
            ],
            "Resource": "arn:aws:logs:us-east-1:*:log-group:/aws/lambda/*:*:*",
            "Effect": "Allow"
        },
        {
            "Action": [
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::us-east-1-bucket1/*",
            "Effect": "Allow"
        }
    ]
}

In lambda@edge I have an origin-response response lambda that is trying to pull an object out of s3 to be used as the response. However I'm getting this AccessDenied error when I try to list the contents of the bucket using a particular prefix. This is my code:

const getVidS3 = (s3, bucketName, fileName) =>
    new Promise((res, rej) => {
        const start = Date.now();
        s3.listObjects(
            {
                Bucket: bucketName,
                Delimiter: '/',
                Prefix: `${fileName}/`,
                MaxKeys: 1,
            },
            function (err, data) {
                console.log(
                    '================ milliseconds to list objects:',
                    Date.now() - start
                );
                if (err) return rej(err);
                if (!Array.isArray(data.Contents) || data.Contents.length < 1) {
                    return rej('original raw video not found');
                }
                console.log('============= s3 objects:', data);
                const rawVidFileKey = data.Contents[0].Key;
                s3.getObject(
                    {
                        Bucket: bucketName,
                        Key: rawVidFileKey,
                    },
                    (err, data) => {
                        console.log(
                            '================ milliseconds to get video object:',
                            Date.now() - start
                        );
                        if (err) {
                            return rej(err);
                        }

                        const contentType = data.ContentType;
                        const video = data.Body;
                        console.log('=============== S3 video data', data);
                        return res({ video, contentType });
                    }
                );
            }
        );
    });

The error is from listObjects. I've gone over this set of suggestions but I'm not sure I took the right steps because I used the IAM user that I use to access the dashboard, which may have higher permissions than the one the lambda uses. More so, I recreated this getVidS3 function locally using my admin credentials and it works, so I think I need help determining how to give the lambda the appropriate permissions. I didn't create the system and I'm not very proficient with aws, so bear with me if this feels incomplete. What/where else can I check to see why this is happening? Thanks in advance.


Solution

  • The problem is in your policy. The policy allows GET of the objects, but not List. You're missing the ListBucket action on the bucket itself.

    You need to change this:

    {
            "Action": [
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::us-east-1-bucket1/*",
            "Effect": "Allow"
        }
    

    To this:

    {
            "Action": [
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                 "arn:aws:s3:::us-east-1-bucket1/*",
                 "arn:aws:s3:::us-east-1-bucket1"
             ]
            "Effect": "Allow"
        }