Reputation: 9628
Have a Lambda function access (with Read and Write permissions) a specific Bucket I own.
Just that one Bucket though, it doesn't need to access anything else.
orca-resources
lamba-s3-orca-resources
. It is set up to be used by the Lambda service.lamba-s3-orca-resources
IAM RoleTesting the function actually yields the contents of the Bucket I wish to be unavailable. Also, s3:listObjects
ain't even in my Allow
ed Actions.
What am I overlooking?
Its code:
'use strict';
const aws = require('aws-sdk');
const s3 = new aws.S3();
exports.handler = (event, context, callback) => {
s3.listObjects({Bucket: 'orca-exe'}, callback);
};
Its Execution Role:
{
"roleName": "lamba-s3-orca-resources",
"policies": [
{
"document": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:GetObjectTagging",
"s3:ListBucket",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::orca-resources/*",
"arn:aws:s3:::orca-resources"
]
}
]
},
"name": "orca-resources-only",
"type": "inline"
}
]
}
Upvotes: 3
Views: 2213
Reputation: 179194
“AWS Lambda function seems to ignore its specified Execution Role”
In one sense, this is true and by design, because the details of the execution role aren't something that the Lambda service or your function are actually aware of. The Lambda function and the Lambda infrastructure aren't actually looking at or aware of the permissions associated with the execution role. Lambda isn't responsible for enforcing this policy.
There's a bit of black magic going on behind the scenes that stitches all of this together, and understanding how that works may help explain why the behavior you're seeing is expected and correct.
First things first, the Lambda Execution Role is an IAM Role.
Q: What is an IAM role?
An IAM role is an IAM entity that defines a set of permissions for making AWS service requests. IAM roles are not associated with a specific user or group. Instead, trusted entities assume roles, such as IAM users, applications, or AWS services such as EC2.
When you assume a role, you're issued a set of temporary credentials -- an access key and secret (similar to IAM user credentials) and a security or session token that conveys the associated privileges, all which must accompany these credentials when they are used for signing requests. In Lambda functions, this is all done automatically.
When your Lambda function is invoked, the Lambda service itself calls AssumeRole
in Session Token Service.
AssumeRole
Returns a set of temporary security credentials (consisting of an access key ID, a secret access key, and a security token) that you can use to access AWS resources that you might not normally have access to.
— https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html
This action -- which is allowed by your role's trust policy -- returns a set of credentials that are dropped into the environment of your Lambda container as environment variables with names like AWS_ACCESS_KEY_ID
, AWS_SECRET_ACCESS_KEY
, and AWS_SESSION_TOKEN
(with the specific names dependent on the environment).
The AWS SDK then automatically uses these environment variables to sign all of the requests you generate within your Lambda function.
Nothing in Lambda screens these requests against the role policy, for a couple of different reasons: that would be a much more complicated and vulnerability-prone solution, compared to simply allowing the normal authentication and authorization mechanisms of the destination service to authenticate and authorize the request... but perhaps more importantly, the Lambda service can't actually tell what actions your code is attempting via the AWS SDK because those requests are transmitted to the service endpoint over HTTPS by default, so it isn't possible for them to be inspected.
The service you're making a request against then authenticates the credentials and authorizes the request with help from IAM and STS -- determining whether the request signature is valid, the accompanying token is valid, and whether the attempted action against the specified resource is allowed.
That last bit is where your assumptions are blurry.
The question that then must be answered by the service handling the request is two-fold:
These are two questions, but when the principal (the role) and the resource (the bucket) are owned by the same account, they can coalesce into a single question that is answered by combining the results from multiple places.
Access policy describes who has access to what. You can associate an access policy with a resource (bucket and object) or a user. Accordingly, you can categorize the available Amazon S3 access policies as follows:
Resource-based policies – Bucket policies and access control lists (ACLs) are resource-based because you attach them to your Amazon S3 resources.
User policies – You can use IAM to manage access to your Amazon S3 resources. You can create IAM users, groups, and roles in your account and attach access policies to them granting them access to AWS resources, including Amazon S3.
When Amazon S3 receives a request, it must evaluate all the access policies to determine whether to authorize or deny the request. For more information about how Amazon S3 evaluates these policies, see How Amazon S3 Authorizes a Request.
— https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-overview.html
This explains the apparently contradictory behavior, as you noted in comments:
“Also, the documentation here seems to pretty clearly indeed deny what's not explicitly allowed.”
The documentation is correct, and you're correct, except that you've failed to consider that what's explicitly allowed includes what the resource owner (the account owner for the bucket) has already explicitly allowed.
If the bucket owner has allowed everyone to perform a particular action... well, the execution role of your Lambda function is part of everyone. Therefore, a grant of access in the Lambda execution role policy would be redundant and unnecessary, because the account owner has already given that permission to everyone. The lack of an explicit Deny
in the role policy means the explicit Allow
in the bucket policy allows the proposed action to occur.
“Plus, the Policy Simulator does give me exactly the behaviour I expect.”
The policy simulator doesn't make real calls to real services, so it's necessary -- when a resource like a bucket has its own policies -- to explicitly include the policy for the resource itself in the simulation.
To use a resource-based policy in the simulator, you must include the resource in the simulation and select the check box to include that resource's policy in the simulation.
https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_testing-policies.html
Otherwise, you're only testing the role policy in isolation.
Note that there is some confusion in the documentation about the S3 policy for listing the objects in a bucket. The action for allowing this in IAM policies is s3:ListBucket
in spite of the fact that the Node.JS SDK method call is listObjects()
. There is an action in the policy simulator called ListObjects
that is either part of planned/future functionality or simply an error, because at last check that didn't correspond to a valid IAM policy action for S3. In the S3 section of the IAM User Guide, s3:ListBucket
is correctly hyperlinked to the List Objects action in the S3 API Reference, but s3:ListObjects
is a circular hyperlink right back to the same page in the IAM User Guide (a link to nowhere). I've tried, so far without success, to find someone at AWS to explain or correct this discrepancy.
Upvotes: 6