How to configure AWS Lambda functions to use a outbound fixed ip

For our Serverless project running on AWS infrastructure we needed an outbound Lambda API call to a SaaS platform which demands a whitelist of the source IP addresses. Which is pretty hard since AWS has a whole range. Luckily there is a trick to target your Lambda function in a VPC which can be configured to use a elastic IP for outbound communication.

High Level Design

Steps to configure the VPC / Network

Step 1 – create/use a VPC

Our AWS Network configuration always start with an AWS VPC (Virtual Private Cloud). You can use an existing VPC and configure your subnets there, or create a new one. Network configuration on AWS can look simple with the GUI wizard, but the setup of your VPC had major impact on all your resources so for real production a thing to remember. In the example I use CIDR block 10.0.0.0/16 which gives me (way to much) options.

Step 2 – create a public and 1-n private subnets

We will need 1 public subnet to attach to a NAT Gateway and route our traffic to the Internet. The example can work with 1 private subnet which hosts your Lambda function. However for availability purposes you would want to have multiple private subnets in different availability zones for your lambda to run. In the example I use CIDR block 10.0.11.0/24 for the public subnet and 10.0.21.0/24, 10.0.22.0/24 and 10.0.23.0/24 for the 3 private subnets on each eu-west-1 availability zone.

Step 3 – Configure the Internet Gateway and the public subnet configuration

We need an Internet Gateway for our VPC, attach to our public subnet and a public route table configuration which targets all (0.0.0.0/0) outbound traffic from the public subnet.

Step 4 – Configure the NAT Gateway and the private subnet configuration

We need a NAT Gateway which uses an elastic IP address. By adding a private route table which we attach to our private subnet(s) we make sure that all functions in the VPC will use our elastic IP for outbound communication. At lease, if we make sure the private route table contains a (0.0.0.0/0) target to the NAT Gateway

Configure your Lambda function

Make sure your Lambda function(s) use the configured VPC by selecting it in the VPC pulldown and then select all the private subnet(s).

Notes:

  • Make sure your Lambda runs with the IAM AWSLambdaVPCAccessExecutionRole

CloudFormation source code

Template can be found in my aws-cloudformation Git repository as well:

Resources:
  ######################
  ## VPC basics
  ######################
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
      - Key: Name
        Value: t10-fn-vpc-dev
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: t10-fn-internetgateway-dev
  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId:
        Ref: InternetGateway
      VpcId:
        Ref: VPC

  ######################
  ## Subnet Public
  ######################
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: VPC
      AvailabilityZone: eu-west-2a
      CidrBlock: 10.0.11.0/24
      MapPublicIpOnLaunch: true
      Tags:
      - Key: Name
        Value: t10-fn-public-subnet-az1-dev
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: VPC
      Tags:
      - Key: Name
        Value: t10-fn-public-rt-dev
  PublicRouteTableRoute1:
    Type: AWS::EC2::Route
    DependsOn: InternetGateway
    Properties:
      RouteTableId:
        Ref: PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Ref: InternetGateway
  PublicRouteTableAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId:
        Ref: PublicSubnet1
      RouteTableId:
        Ref: PublicRouteTable
  PublicElasticIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain:
        Ref: VPC
  NatGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt: PublicElasticIP.AllocationId
      SubnetId:
        Ref: PublicSubnet1
      Tags:
      - Key: Name
        Value: t10-fn-natgateway-dev

  ######################
  ## Subnet Private
  ######################
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: VPC
      AvailabilityZone: eu-west-2a
      CidrBlock: 10.0.21.0/24
      MapPublicIpOnLaunch: false
      Tags:
      - Key: Name
        Value: t10-fn-private-subnet-az1-dev
  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: VPC
      AvailabilityZone: eu-west-2b
      CidrBlock: 10.0.22.0/24
      MapPublicIpOnLaunch: false
      Tags:
      - Key: Name
        Value: t10-fn-private-subnet-az2-dev
  PrivateSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: VPC
      AvailabilityZone: eu-west-2c
      CidrBlock: 10.0.23.0/24
      MapPublicIpOnLaunch: false
      Tags:
      - Key: Name
        Value: t10-fn-private-subnet-az3-dev
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: VPC
      Tags:
      - Key: Name
        Value: t10-fn-private-rt-dev
  PrivateRouteTableRoute1:
    Type: AWS::EC2::Route
    DependsOn: NatGateway
    Properties:
      RouteTableId:
        Ref: PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId:
        Ref: NatGateway
  PrivateRouteTableAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId:
        Ref: PrivateSubnet1
      RouteTableId:
        Ref: PrivateRouteTable
  PrivateRouteTableAssociation2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId:
        Ref: PrivateSubnet2
      RouteTableId:
        Ref: PrivateRouteTable
  PrivateRouteTableAssociation3:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId:
        Ref: PrivateSubnet3
      RouteTableId:
        Ref: PrivateRouteTable

  ######################
  ## Security NACL
  ######################
  NetworkAcl:
    Type: AWS::EC2::NetworkAcl
    Properties:
      VpcId:
        Ref: VPC
      Tags:
      - Key: Name
        Value: t10-fn-nacl-dev
  NetworkAclEntryfn100:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      CidrBlock: 0.0.0.0/0
      Egress: 'false'
      NetworkAclId:
        Ref: NetworkAcl
      Protocol: "-1"
      RuleAction: allow
      RuleNumber: "100"
  NetworkAclEntryOutbound100:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      CidrBlock: 0.0.0.0/0
      Egress: 'true'
      NetworkAclId:
        Ref: NetworkAcl
      Protocol: "-1"
      RuleAction: allow
      RuleNumber: "100"
  PrivateSubnetNetworkAclAssociation1:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId:
        Ref: PrivateSubnet1
      NetworkAclId:
        Ref: NetworkAcl
  PrivateSubnetNetworkAclAssociation2:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId:
        Ref: PrivateSubnet2
      NetworkAclId:
        Ref: NetworkAcl
  PrivateSubnetNetworkAclAssociation3:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId:
        Ref: PrivateSubnet3
      NetworkAclId:
        Ref: NetworkAcl

  ######################
  ## Security Group(s)
  ######################
  LambdaSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: t10-fn-lambda-sg
      GroupDescription: t10-fn-lambda-sg
      SecurityGroupIngress:
      - IpProtocol: -1
        CidrIp: 0.0.0.0/0
      VpcId:
        Ref: VPC
      Tags:
      - Key: Name
        Value: t10-fn-lambda-sg-dev

  ######################
  ## OUTPUT
  ######################

  #Outputs:
  #  VPC:
  #    Description: A reference to the created VPC
  #    Value:
  #      Ref: VPC
  #    Export:
  #      Name: t10-fn-vpc-id$

References