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:

  1. Before the implementation plan, you need to create the CloudFront distribution

  2. 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:

  1. 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.





References:

  1. What is AWS Lambda? - AWS Lambda 

  2. https://aws.amazon.com/lambda/features/

  3. https://aws.amazon.com/blogs/networking-and-content-delivery/securing-and-accessing-secrets-from-lambdaedge-using-aws-secrets-manager/