Pass secrets to Lambda Function with AWS Secrets Manager

Retrieve sensitive information in a Lambda Function from AWS Secrets Manager.

The ACK service controller for Amazon Lambda lets you manage Lambda functions directly from Kubernetes. This guide shows you how to create a Lambda function that can retrieve sensitive data from AWS Secrets Manager.

Setup

Although it is not necessary to use Amazon Elastic Kubernetes Service (Amazon EKS) or Amazon Elastic Container Registry (Amazon ECR) with ACK, this guide assumes that you have access to an Amazon EKS cluster. If this is your first time creating an Amazon EKS cluster, see Amazon EKS Setup. For automated cluster creation using eksctl, see Getting started with Amazon EKS - eksctl and create your cluster with Amazon EC2 Linux managed nodes.

Prerequisites

This guide assumes that you have:

  • Created an EKS cluster with Kubernetes version 1.16 or higher.
  • AWS IAM permissions to create roles and attach policies to roles.
  • Installed the following tools on the client machine used to access your Kubernetes cluster:
    • AWS CLI - A command line tool for interacting with AWS services.
    • kubectl - A command line tool for working with Kubernetes clusters.
    • eksctl - A command line tool for working with EKS clusters.
    • Helm 3.8+ - A tool for installing and managing Kubernetes applications.
    • jq

Install the Lambda ACK service controller

Log into the Helm registry that stores the ACK charts:

aws ecr-public get-login-password --region us-east-1 | helm registry login --username AWS --password-stdin public.ecr.aws

Deploy the ACK service controller for Amazon Lambda using the lambda-chart Helm chart. This example creates resources in the us-west-2 region, but you can use any other region supported in AWS.

SERVICE=lambda
RELEASE_VERSION=$(curl -sL https://api.github.com/repos/aws-controllers-k8s/${SERVICE}-controller/releases/latest | jq -r '.tag_name | ltrimstr("v")')
helm install --create-namespace -n ack-system oci://public.ecr.aws/aws-controllers-k8s/lambda-chart "--version=${RELEASE_VERSION}" --generate-name --set=aws.region=us-west-2

For a full list of available values to the Helm chart, please review the values.yaml file.

Configure IAM permissions

Once the service controller is deployed configure the IAM permissions for the controller to invoke the Lambda API. For full details, please review the AWS Controllers for Kubernetes documentation for how to configure the IAM permissions. If you follow the examples in the documentation, use the value of lambda for SERVICE.

Create Secret in Secrets Manger

To test our Lambda function’s integration with AWS Secrets Manager we’ll need to create a sample secret value. We can create a new secret with the aws cli.

aws secretsmanager create-secret --name test-secret --secret-string "secret value"

The ACK Secrets Manager service controller can also be used to create and manage secrets directly from Kubernetes. See, Create a Secret with AWS Secrets Manager

Create Lambda function handler

The Lambda function handler is the method in your function code that processes events. When your function is invoked, Lambda runs the handler method.

cat > index.mjs << 'EOF'
import http from 'http';

export const handler = async (event) => {
    try {
        const secretName = process.env.TEST_SECRET_ARN;
        const options = {
            hostname: 'localhost',
            port: 2773,
            path: `/secretsmanager/get?secretId=${secretName}`,
            headers: {
                'X-Aws-Parameters-Secrets-Token': process.env.AWS_SESSION_TOKEN
            }
        };

        const response = await new Promise((resolve, reject) => {
            http.get(options, (res) => {
                let data = '';
                res.on('data', (chunk) => { data += chunk; });
                res.on('end', () => {
                    resolve({ 
                        statusCode: res.statusCode, 
                        body: data 
                    });
                });
            }).on('error', reject);
        });

        const secret = JSON.parse(response.body).SecretString;
        console.log('Retrieved secret:', secret);

        return {
            statusCode: response.statusCode,
            body: JSON.stringify({
                message: 'Successfully retrieved secret',
                secretRetrieved: true
            })
        };
    } catch (error) {
        console.error('Error:', error);
        return {
            statusCode: 500,
            body: JSON.stringify({
                message: 'Error retrieving secret',
                error: error.message
            })
        };
    }
};
EOF

To package the function handler we then need to add it to a zip file.

zip -r function.zip index.mjs

Create an IAM Execution Role for the Lambda function

Our Lambda function will need use an execution role that can access the secret in AWS Secrets Manager.

Create the IAM role:

read -r -d '' TRUST_RELATIONSHIP <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
EOF
echo "${TRUST_RELATIONSHIP}" > trust.json

ACK_LAMBDA_IAM_ROLE="ack-lambda-function"
ACK_LAMBDA_IAM_ROLE_DESCRIPTION="Role for ACK managed Lamdba function"
aws iam create-role --role-name "${ACK_LAMBDA_IAM_ROLE}" --assume-role-policy-document file://trust.json --description "${ACK_LAMBDA_IAM_ROLE_DESCRIPTION}"
ACK_LAMBDA_IAM_ROLE_ARN=$(aws iam get-role --role-name=$ACK_LAMBDA_IAM_ROLE --query Role.Arn --output text)

And then attach an IAM Policy that grants read access to our secret.

SECRET_ARN=$(aws secretsmanager describe-secret --secret-id test-secret | jq ".ARN")
POLICY_NAME=ack-lambda-policy
read -r -d '' POLICY <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "secretsmanager:GetSecretValue",
            "Resource": $SECRET_ARN
        }
    ]
}
EOF
echo "${POLICY}" > policy.json

POLICY_ARN=$(aws iam create-policy --policy-name $POLICY_NAME --policy-document file://policy.json | jq ".Policy.Arn" | tr -d '"')



aws iam attach-role-policy \
        --role-name $ACK_LAMBDA_IAM_ROLE \
        --policy-arn $POLICY_ARN

Deploy the Lambda Function using the ACK Lambda Controller

The following example creates a manifest that contains the Lambda function with the necessary environment variable and IAM role to read the secret from AWS Secrets Manager. In order to limit the number of calls made to AWS Secrets Manager the AWS Parameter and Secrets Lambda extension layer is applied.

BASE64_ZIP=$(cat function.zip | base64)
TEST_SECRET_ARN=$(aws secretsmanager describe-secret --secret-id test-secret | jq ".ARN")

read -r -d '' LAMBDA_MANIFEST <<EOF
apiVersion: lambda.services.k8s.aws/v1alpha1
kind: Function
metadata:
  name: sample-lambda
  annotations:
    services.k8s.aws/region: us-west-2
spec:
 name: sample-lambda
 environment:
   variables:
     TEST_SECRET_ARN: $TEST_SECRET_ARN
 packageType: Zip
 runtime: nodejs18.x
 handler: index.handler
 code:
    zipFile: $BASE64_ZIP

 role: $ACK_LAMBDA_IAM_ROLE_ARN
 description: Sample function for retrieving secrets from AWS Secrets Manager
 layers:
  - "arn:aws:lambda:us-west-2:345057560386:layer:AWS-Parameters-and-Secrets-Lambda-Extension:17"
EOF

echo "${LAMBDA_MANIFEST}" > function.yaml
kubectl create -f function.yaml

Invoke the Lambda Function

After the Lambda function has finished deploying, you can invoke the function through the AWS CLI.

aws lambda invoke --function-name sample-lambda --region us-west-2 /dev/stdout | jq

You will get the output as below:

{"statusCode":200,"body":"\"Successfully retrieved secret!\""} 

Cleanup

You can delete you Lambda function using the kubectl delete command:

kubectl delete -f function.yaml

The IAM role and policy can removed with the AWS CLI

aws iam detach-role-policy --role-name $ACK_LAMBDA_IAM_ROLE --policy-arn $POLICY_ARN
aws iam delete-role --role-name $ACK_LAMBDA_IAM_ROLE
aws iam delete-policy --policy-arn $POLICY_ARN

We can also delete our secret from AWS Secrets Manager with the AWS CLI

aws secretsmanager delete-secret --secret-id test-secret

To remove the Lambda ACK service controller, related CRDs, and namespaces, see ACK Cleanup.

To delete your EKS clusters, see Amazon EKS - Deleting a cluster.

Edit this page on GitHub