Description:
AWS Lambda is serverless compute service that runs your code in response to events and automatically manages the underlying compute resources for you. It is also used for extending other AWS services with custom logic or creating your back-end services that operate at AWS scale, performance, and security.
The code you run on AWS Lambda is called a Lambda function. These functions are stateless. After uploading your code to AWS Lambda, you can associate your function with a specific AWS resource.
Metadata of the Lambda function contains some small configuration that executes the function and it includes environment variables, These variables can be accessed by any entity and in these variables, it may contain your secrets (key, password, etc). Make sure that in runtime lambda environment passes without secrets in code or environment variables.
Rationale:
Make sure data sent has no personal info or sensitive information sent to or from the Lambda functions variables. For this function to work we need to make sure AWS Secrets Manager so that all the data can be properly traced.
Lambda functions contain secrets in their environment variables, These secrets can be keys, authorization tokens, passwords and it may that information which should be kept private. The lambda function is not encrypted so it is the risk if it contains any secret information.
If the Lambda function executes the first time, it reaches out to gather the secrets - otherwise, it uses the global variable. So, you need to use Secrets Manager to sore your secure values.
It is not a good practice to store the secrets in the code, if we need these secrets in your function you can use AWS Secrets Manager. This AWS Secrets Manager stores your secrets( API keys, Passwords, etc) securely. Through this AWS secrets manager, you can be retrieved by the Lambda function to complete the request.
Impact:
If We ensure there are no secrets in the Lambda function variable it helps to reduce the risk of exposing data to third parties.
Default Value:
By default in AWS Lambda Dashboard, there is no function created by cloud service providers.
When you create a lambda function by default it has basic hello codes in its environment it did not contain any secret values in the lambda function.
Pre-Requisites:
Before the implementation plan, you need to create the CloudFront distribution
Create an IAM role that allows your Lambda function
Remediation:
Test Plan:
Step 1: Log in to AWS Management Console and go to AWS Lambda dashboard at https://console.aws.amazon.com/lambda
Step 2: Click on Function in the left navigation pane
Step 3: Choose your lambda function to audit any secret values contain or not
Step 4: Scroll the page and go to Code Source and check your code source is it contain any secret value or not
Using AWS CLI:
To get the configuration of the Lambda function in environment variables
aws lambda get-function-configuration --region <REGION> --function-name <FUNCTION_NAME> --query Environment.Variables
Implementation Plan:
Creating a Secret in Secrets Manager
Step 1: Log in to AWS Management Console and go to AWS Secrets Manager dashboard at https://console.aws.amazon.com/secretsmanager
Step 2: Click on Secrets in the left navigation pane
Step 3: Click on Store a new secret button
On this page, you see the following screen
Here we choose Other types of secrets and this secret type is simple key-value pair. and give the name of the secret key and select the encryption key and click on the Next button.
In the part, you provide a name for the secret. and click on Next
If your organization policy want to rotate interval enable then enable it otherwise go to the next step which is the Review part after clicking on the Store button
2. Create IAM Policy for AWS Secrets Manager Access
=> Create a new IAM Policy that allows this role access to read a secret out of AWS Secrets Manager.
=> Copy down the ARN of the secret you created above you need to specify this in the Resource section of the policy.
=> JSON policy document that allows the Lambda role access to read the secret from AWS Secrets Manager:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "secretsmanager:GetSecretValue", "Resource": "arn:aws:secretsmanager:us-east-1:012345678910:secret:app/api/key-123xyz" } ] }
=> Name the policy LambdaSecretsManagerPolicy and attach it to the lambda role that you created as per the above steps
In your Lambda Function which you create to access (the secret value but not gather in the function ) AWS Secrets Manager to retrieve an API key. following the python code which you configure into your Lambda Function:
import sys import json import boto3 import base64 from botocore.vendored import requests from botocore.exceptions import ClientError # global variables for re-use of invocations session = boto3.session.Session() apiEndpoint = "https://myapiendpoint/api/v1" secretName = "app/api/key" secretRegion = "us-east-1" # global variable to cut down the number of calls to AWS Secrets Manager apiKey = "" def lambda_handler(event, context): """ lambda handler function for invocation. """ # pull the API key from AWS Secrets Manager # only reach out to grab key if it is an empty string if apiKey == "": apiKey = get_secret(secretName, secretRegion)['apikey'] else: print("apiKey is already set, moving on") # HTTP POST call to external service apiresponse = call_api_endpoint( endpoint=apiEndpoint, payload={"source": "lambda-edge"}, headers={"Authorization": apiKey} ) if apiresponse.return_code == '200': return { 'status': '200', 'statusDescription': 'Good' } else: return { 'status': apiresponse.return_code, 'statusDescription': 'Error' } def call_api_endpoint( endpoint: str, payload: dict, headers: dict ): """ Makes HTTP POST call to service endpoint. Parameters: endpoint (str) = the HTTP endpoint to call. payload (dict) = the payload to pass to the endpoint. headers (dict) = all headers to pass to the HTTP request. Returns: response = the HTTP response. """ try: response = requests.post( url=endpoint, data=payload, headers=headers ) return response except requests.exceptions.HTTPError: print("An exception occurred while calling the API endpoint!") sys.exit(1) def get_secret(secret_name: str, region_name: str): """ Gets the secret from AWS Secrets Manager. Parameters: secret_name (str) = the name of the secret to get. region_name (str) = the name of the AWS region where the secret is. Returns: secret_val (dict) = the key-value pair of the secret. """ client = session.client( service_name='secretsmanager', region_name=region_name ) try: get_secret_value_response = client.get_secret_value( SecretId=secret_name ) except ClientError as e: raise e else: # Decrypts secret using the associated KMS CMK. # Depending on whether the secret is a string or binary, one of these fields will be populated. if 'SecretString' in get_secret_value_response: secret = get_secret_value_response['SecretString'] return json.loads(secret) else: decoded_binary_secret = base64.b64decode(get_secret_value_response['SecretBinary']) return json.loads(decoded_binary_secret)
3. Creating and deploying the Lambda function
=> Create a new Lambda function and choose Python 3.7 for the runtime, choose the IAM role which you created for the Lambda function. After creating the new Lambda Function, paste the above python code in the function contents and click save
Finally, navigate to Actions and select Deploy to Lambda@Edge as shown below
After you click on Deploy to Lambda@Edge, You will be asked to choose or create a new CloudFront trigger. Select Configure new CloudFront trigger and select the distribution you created before with correct cache behavior. Be sure to configure the CloudFront event as a Viewer request. and deploy it
Backout Plan:
If you have any problem remove the secrets from Secret Manger.
Remove those Lambda functions.
Remove IAM policy and role created for Lambda function to access secret manager and not gather the Secrets from AWS.