The AWS IAM model is one of the most granular and powerful permission models you will find among cloud providers. However, as the saying goes, "with great power, comes great responsibility".
When you create IAM policies (on AWS, and in general), you should always follow the standard security concept of granting the least required privileges - i.e. granting only the permissions required to perform a task successfully.
In the case of AWS Lambda functions, the role you assign to a function will dictate the permissions the function will have while it executes. Under certain conditions, this permissions model, might be the thing that will save your sensitive data.
With an over-permissive IAM role assigned to a function, an attacker my leverage an application layer vulnerability in your function to perform lateral movement into other resources in your AWS account.
Let's take a look at a simple AWS Lambda python function, which is triggered by an SNS event, and stores the event's message field inside a text file, in an S3 bucket:
In order to deploy our function, we will use the Serverless framework, using the following (insecure) serverless.yml configuration file:
In the configuration file above, the developer made 2 critical mistakes:
Mistake #1: in line #12, the developer granted all possible S3 actions, while in reality the only relevant action should be 's3:PutObject'
Mistake #2: in line #13, the developer applied the the permissions to the entire set of S3 buckets that exist in the same account as the function.
In essence, if an attacker finds a way to manipulate the function's logic to perform actions on his/her behalf, for example, through an application layer vulnerability, he/she will be able to perform any type of S3 action, against all of the buckets in the same account, regardless if they are being used by the function or not. What can an attacker achieve? here are a few examples:
- List all S3 buckets in the account
- Create/Delete/Modify files inside S3 buckets
- Modify bucket(s) configuration
And all of this is caused by sheer laziness (or lack of education) of the developer. However, as we stated earlier, the AWS IAM model is extremely granular and powerful, and mastering all of its intricacies can be perplexing. There are so many different possible actions, and knowing which ones are the only ones required could be challenging.
Auto-Generate Least Privileged Roles with 'serverless-puresec-cli'
In order help you save previous time, PureSec released an open source tool, which you can also use as a Serverless framework plugin, which automatically generates AWS IAM roles with the least privileges required by your functions.
- Saves you time - magically creates IAM roles for you
- Reduces the attack surface of your AWS Lambda functions
- Helps create least privileged roles with the minimum required permissions
- Currently supported runtimes: Node.js, Python (more runtimes coming soon...)
- Currently supported services: DynamoDB, Kinesis, KMS, Lambda, S3, SES, SNS & Step Functions
- Works with the Serverless Framework
Let's add the tool as a part of our deployment, and see it in action. Installation is quick and effortless, just install the plugin in the following manner
npm install --save-dev serverless-puresec-cli
And then modify your serverless.yml file to include the plugin (see lines #18, #19 below):
Now, let's run it:
serverless puresec gen-roles --function demo-main
The tool will run its analysis, and will generate the least-privileged security policy as follows:
The tool statically analyzed all of our code, located the relevant AWS SDK APIs usage, and the relevant resources, and generated a policy that only allows us to perform the 's3:PutObject' action, on the specific bucket specified in our project. Simple, powerful, and most importantly a huge timesaver.
Here's the serverless.yml, after we incorporate the tool's suggested configuration:
What are you waiting for? Go download it (npm install --save-dev serverless-puresec-cli ), and take it out for a spin... be careful, it's highly addictive :-)