16

I have a lambda that needs to communicate 'locally' with an EC2 instance in a private VPC. The API key is being stored in Secrets Manager.

Using the default code provided by Secrets Manager and the necessary IAM roles I am able to read the API key from Secrets Manager in my Lambda:

# Use this code snippet in your app.
# If you need more information about configurations or implementing the sample code, visit the AWS docs:   
# https://aws.amazon.com/developers/getting-started/python/

import boto3
import base64
from botocore.exceptions import ClientError

def get_secret():

    secret_name = "MYSECRET"
    region_name = "ap-southeast-2"

    # Create a Secrets Manager client
    session = boto3.session.Session()
    client = session.client(
        service_name='secretsmanager',
        region_name=region_name
    )

    # In this sample we only handle the specific exceptions for the 'GetSecretValue' API.
    # See https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
    # We rethrow the exception by default.

    try:
        get_secret_value_response = client.get_secret_value(
            SecretId=secret_name
        )
    except ClientError as e:
 ... # Default error handling..
    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 secret
        else:
            decoded_binary_secret = base64.b64decode(get_secret_value_response['SecretBinary'])
            return decoded_binary_secret
 
def lambda_handler(event, context):
    secrt = get_secret()

    return {
        'statusCode': 200,
        "headers": {
            'Access-Control-Allow-Origin': '*',
            'Content-Type': 'application/json'
        },
        'body': secrt 
    }             

This Lambda is able to successfully retrieve and print the API key from Secrets Manager.

To communicate with the EC2 instance I have a Lambda with a helper layer and some simple test code:

import apihelper
import json

def lambda_handler(event, context):
  conn = apihelper.getConnection('API KEY')
  return {
    'statusCode': 200,
    "headers": {
      "Access-Control-Allow-Origin": "*"
    },
    'body': json.dumps(conn.listProducts())
  }

This lambda is in the VPC, subnet and has the necessary security group rules to communicate with the EC2 instance. Hard coding the API KEY it successfully returns the expected data from the EC2 instance.

When I try to combine them so that the API key is not hard-coded the Lambda no longer works. There is no error message it just times out.

I have tried:

  • Increasing the timeout to over a minute
  • Placing allow all inbound and outbound rules on the security group
  • Configuring a VPC endpoint for Secrets Manager

I think I have narrowed it down to the VPC. The first Lambda that just prints out the secret works perfectly until I place it in the VPC. But I don't know where to look or how to configure it to allow the Lambda to talk to both the EC2 inside the VPC as well as Secrets Manager.

MatthewMartin
  • 32,326
  • 33
  • 105
  • 164
react-dev
  • 348
  • 1
  • 3
  • 10

1 Answers1

31

There is no error message it just times out.

Unfortunately, a lambda function in a VPC does not have internet access nor public IP. From docs:

Connecting a function to a public subnet does not give it internet access or a public IP address.

Therefore, when you use boto3:

    client = session.client(
        service_name='secretsmanager',
        region_name=region_name
    )

to connect to the Secrets Manager it times out, because boto3 can't connect to the Secrets Manager manager from the VPC.

There are two ways to rectify this:

  1. Place your function in a private subnet and use NAT gateway/instance with correctly configured route tables to provide the internet access, and consequently, to the Secrets Manager.

  2. Setup VPC interface endpoint for Secrets Manager in a private subnet. This way your lambda function will be able to connect to the Secrets Manager using the endpoint, without needing the internet.

Marcin
  • 215,873
  • 14
  • 235
  • 294
  • I am using boto in the Lambda to get the secret (not in the helper) but thanks for the info and references. I'll do some research and hopefully I can call the VPC endpoint instead of whatever boto calls by default. – react-dev Jun 09 '20 at 03:24
  • @react-dev Even it its `boto3` internet connection or VPC endpoint are required. I will amend the answer to include boto3. – Marcin Jun 09 '20 at 03:25
  • Thanks. I am researching it all now. We are hoping to avoid route tables, so looking into the VPC endpoints. Even to call that directly through the python code and not use boto. – react-dev Jun 09 '20 at 03:31
  • @react-dev If your vpc has DNSsupport and DNShostnames enabled, you shouldn't need to change your code. boto3 will automatically use the correct endpoint. – Marcin Jun 09 '20 at 03:33
  • You will certainly want to continue using boto3. The problem is in connecting the Lambda function (in the VPC) to Secrets Manager, either via a NAT Gateway or via the VPC Endpoint. – John Rotenstein Jun 09 '20 at 03:33
  • 1
    Thanks. Got it to work with a VPC endpoint, and security group rules. As you mentioned, I can still use native boto to read the secret. – react-dev Jun 09 '20 at 04:51
  • 6
    Hi @react-dev, can you share how you configured the security group? I have the same setup (lambda in a VPC with an endpoint connected to secretmanager) but it still times out. thanks – thinkOfaNumber Jul 11 '20 at 13:37
  • @thinkOfaNumber the security group attached to the VPC endpoint should allow inbound rule for port: 443, protocol: TCP, destination: your subnet CIDRs where your lambda is running. The security group attached to your lambda should allow outbound rule for port 443 - TCP - destination: add the security group created for secrets manager (least privilege) or the CIDR block of your VPC or subnets. – Ribeiro Aug 15 '21 at 08:58
  • thanks @Ribeiro, I can't remember my exact steps but I got it working by changing the way I was assigning permission to the lambda function in my cdk script. Instead of `handler.role = ...` I changed it to `handler.addToRolePolicy(...)`. Combined with the VPC having a service endpoint for the secret manager. Thanks! – thinkOfaNumber Aug 17 '21 at 06:35
  • Thanks for the tip. In my case, I went with the NAT Gateway setup and I placed it in a private subnet by mistake. Moving the NATs to public subnets fixed the issue – Kappacake Nov 21 '22 at 18:23