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$