0
votes

I originally setup SES to receive emails and during the process I created a bucket policy which allowed the service to put emails in S3. I now have a lambda function that should be able to use STS to assume a role and access the same bucket. Unfortunately, I can't figure out the right policy to allow both the service and the IAM Role to access the same bucket, right now I get 'access denied'. I used the policy simulator to verify that everything else functioned as it should e.g I added a generic policy with more actions on the aim Role and if I tell the simulator to ignore the bucket policy then it says it CAN access the files.

I've tried having two statements, one with the principal set to 'services' and the other to the aim roles (added two just to see if either would work)

{
"Version": "2012-10-17",
"Id": "Policy1478013193612",
"Statement": [
    {
        "Sid": "Stmt1478013187203",
        "Effect": "Allow",
        "Principal": {
            "Service": "ses.amazonaws.com"
        },
        "Action": [
            "s3:PutObject",
            "s3:GetObject"
        ],
        "Resource": "arn:aws:s3:::email-01-bucket/*"
    },
    {
        "Sid": "Stmt55",
        "Effect": "Allow",
        "Principal": {
            "AWS": [
                "arn:aws:sts::[id-num-with-no-dashes]:assumed-role/[role-name]/[session-name]",
                "arn:aws:iam::[id-num-with-no-dashes]:role/[role-name]"
            ]
        },
        "Action": [
            "s3:PutObject",
            "s3:GetObject"
        ],
        "Resource": "arn:aws:s3:::email-01-bucket/*"
    }
]
}

I also tried putting it in 1 statement:

{
"Version": "2012-10-17",
"Id": "Policy1478013193612",
"Statement": [
    {
        "Sid": "Stmt1478013187203",
        "Effect": "Allow",
        "Principal": {
            "AWS": [
                "arn:aws:sts::[id-num-with-no-dashes]:assumed-role/[role-name]/[session-name]",
                "arn:aws:iam::[id-num-with-no-dashes]:role/[role-name]"
            ],
            "Service": "ses.amazonaws.com"
        },
        "Action": [
            "s3:PutObject",
            "s3:GetObject"
        ],
        "Resource": "arn:aws:s3:::email-01-bucket/*"
    }
]
}

BTW, the file path is : arn:aws:s3:::email-01-bucket/email/[file-id]

I couldn't put ListBucket since it would throw an error.

Any help is appreciated :D

Let me know if I should add anything else

[Update]

In an effort to make finding the right policy easier I will describe what I currently have setup in the IAM policy simulator, I'm assuming if we can get it working here then it should work on a lambda function.

Role: serviceDev

This role has two policies attached to it.

1) serviceFS:

{
"Version": "2012-10-17",
"Statement": [
    {
        "Sid": "VisualEditor0",
        "Effect": "Allow",
        "Action": [
            "s3:PutObject",
            "s3:GetObject",
            "s3:PutBucketLogging",
            "s3:ListBucket"
        ],
        "Resource": "arn:aws:s3:::email-01-bucket/*"
    }
]
}

2) AWSLambdaBasicExecutionRole

{
"Version": "2012-10-17",
"Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "logs:CreateLogGroup",
            "logs:CreateLogStream",
            "logs:PutLogEvents"
        ],
        "Resource": "*"
    }
]
}

In the Policy Simulator:

The dropdown Select Service is set to S3

The dropdown Select Action is set to GetObject

Under Resource > Object I type the path to an existing file arn:aws:s3:::email-01-bucket/email/u245hnt85uivpfkrrlhgo0jt86gfjgnca2fgocg1 (after double checking that the file is in fact there)

Once I do that there is now a Resource Policies section on the left which shows me the Bucket policy attached to my bucket with the following policy:

{
"Version": "2012-10-17",
"Id": "Policy1478013193612",
"Statement": [
    {
        "Sid": "Stmt1478013187203",
        "Effect": "Allow",
        "Principal": {
            "Service": "ses.amazonaws.com"
        },
        "Action": "s3:PutObject",
        "Resource": "arn:aws:s3:::email-01-bucket/*"
    }
]
}

Now here is where I get lost, going back to the Resource > Object area where I specify the path to an existing file there is a checkbox labeled Include Resource Policy, if I uncheck it then the Simulator says that the permission was allowed BUT if I check it then I get the following error: The simulation could not be performed! : Simulation failed!

1
The typical way to grant permissions to a Lambda function is for the policy associated with the Lambda function's IAM role to confer the permissions. You don't typically do this via an S3 bucket policy (unless you are specifically trying to deny all principals except that IAM role).jarmod
Like I mentioned in the beginning, the bucket policy was added for the SES service, I still need this service to work so I can't remove it. I also tried having the bucket policy with only the SES as the principal and then a separate policy on the IAM role but I was still getting 'access denied''.weirded
The bucket policy does need the SES permissions but it doesn’t need, and generally should not have, the Lambda permissions. Debug the Lambda permissions independently of the bucket policy.jarmod
@jarmod I did, when validating that everything else was working correctly, I created a IAM role policy with all the required S3 bucket related actions. In the policy simulator when toggling the 'Include Resource Policy' option 'off' it works, but not if I turn it 'on'. Seems the bucket policy is conflicting with the role permissions.weirded
The bucket policy won’t conflict with the IAM policy unless the bucket policy has Deny statements. Let’s better understand what API calls your Lambda is invoking and why it’s IAM role is insufficient. What is your Lambda doing other than GetObject and PutObject?jarmod

1 Answers

2
votes

As far as I understand your requirements, you need one S3 bucket policy (to allow SES to put objects into your S3 bucket) and you need one IAM role that your Lambda function will use to list that bucket and to put/get objets in that bucket. The IAM role also needs a trust relationship, allowing the AWS Lambda Service to assume the role on your behalf (so that you don't have to manually assume that role in your code).

Specifically:

  • don't add the Lambda permissions to the S3 bucket policy
  • don't use STS in Lambda to assume a role; simply configure the Lambda with that role
  • typically, you should not have to explicitly code anything in your Lambda function related to assuming roles or getting permissions (the Lambda service does it all for you)

S3 bucket policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowSESPuts",
            "Effect": "Allow",
            "Principal": {
                "Service": "ses.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::BUCKET-NAME/*",
            "Condition": {
                "StringEquals": {
                    "aws:Referer": "AWSACCOUNTID"
                }
            }
        }
    ]
}

IAM role:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "sidlist",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::BUCKET-NAME"
        },
        {
            "Sid": "sidputget",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::BUCKET-NAME/*"
        }
    ]
}

Trust relationship on the IAM role:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

I'm not sure why you included s3:PutBucketLogging in your example but if you genuinely need it then add it to the Lambda's IAM role against the arn:aws:s3:::BUCKET-NAME resource (not the arn:aws:s3:::BUCKET-NAME/* resource -- it's an action on buckets not on objects)