Skip to content

tomoima525/elastic-ip-lambda

Repository files navigation

Static IP for Lamdba function, using NAT instance

  • This sample project demonstrates how to set up Elastic IP for a Lambda function using a NAT instance.
  • The Lambda function has a static IP address, the Elastic IP of the NAT instance.

    log elastic IP

Why static IP?

  • Some services require a static IP to allow access. For example, Stripe requires an IP allowlist to access restricted data. You can allow the IP of a Lambda function to access those services.

Why NAT instance?

While you can set up static IP for a Lambda function using a NAT Gateway, it is expensive. NAT instance is much cheaper when you don't have to worry about the scalability of the NAT instance.

Architecture

  • NAT instance exists in multiple AZs, with a route table that routes all traffic to the internet gateway.
  • NAT instances are in a public subnet, with an Elastic IP attached.
  • Lambda function is in a private subnet, with a route table that routes all traffic to the NAT instance.

architecture

Implementations

There are 2 patterns to implement this architecture. I implemented both patterns, but recommended to use Pattern2.

Pattern1: Use AWS official AMI(deprecated)

AWS provides an AMI that is specifically for NAT. However it reached the end of maintenance support on December 31, 2023.

  • With this pattern we have to manually set Elastic IP. We access NAT instance by accessing the child node of VPC
// Use escape hatch to attach EIP
const natInstance1 = vpc.node
  .findChild("public-Subnet1")
  .node.findChild("NatInstance") as ec2.Instance;

Pattern2. Create your own AMI

  • In the CDK, I used Amazon Linux 2023, which has ens5 as a primary network interface. This may differ depending on the AMI you use. You can check the primary network interface by running netstat -i command on the instance. We use this interface to set up NAT.
netstat -i
Kernel Interface table
Iface             MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
ens5             9001    19194      0      0 0          2275      0      0      0 BMRU
lo              65536       12      0      0 0            12      0      0      0 LRU
  • The AMI should be deployed under the following settings:

    • Source/Dest. check is disabled
    • Exists in public subnet of VPC created
    • Set Keypair for SSH access
    • Set Security Group for SSH access (Accept port 22)
  • We use addUserData function to run the following commands when the instance is created.

//init-script.sh
# install iptable
sudo yum install iptables-services -y
sudo systemctl enable iptables
sudo systemctl start iptables

# Turning on IP Forwarding
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

# Making a catchall rule for routing and masking the private IP
# Amazon Linux 2023 primay network interface is ens5
sudo iptables -t nat -A POSTROUTING -o ens5 -s 0.0.0.0/0 -j MASQUERADE
sudo /sbin/iptables -F FORWARD
sudo service iptables save
  • Inside CDK, this script is set up as follows:
const initScriptPath = path.join(`${__dirname}/`, "init-script.sh");
const userData = fs.readFileSync(initScriptPath, "utf8");
customNat.addUserData(userData);
  • Route all traffic from private subnets to NAT instance. Since AZ is 2, we have 2 private subnets
const privateSubnets = vpc.selectSubnets({
  subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
}).subnets as ec2.Subnet[];
privateSubnets[0].addRoute(`NAT-route-0`, {
  routerId: customNat.instanceId,
  routerType: ec2.RouterType.INSTANCE,
  destinationCidrBlock: "0.0.0.0/0",
});
privateSubnets[1].addRoute(`NAT-route-1`, {
  routerId: customNat.instanceId,
  routerType: ec2.RouterType.INSTANCE,
  destinationCidrBlock: "0.0.0.0/0",
});
  • Set up Elastic IP for NAT instance
const elasticIp = new ec2.CfnEIP(this, "ElasticIp");
new ec2.CfnEIPAssociation(this, "EipAssociation", {
  eip: elasticIp.ref,
  instanceId: customNat.instanceId,
});

Migrating existing NAT Gateway to NAT instance

  • If you have an existing NAT Gateway, you can migrate to NAT instance with some manual steps.

Let's say you have VPC that has RDS and Lambda function. For public access, you have a NAT Gateway in the public subnet. Below is the CDK code to set up such environment.

pnpm cdk deploy --app 'npx ts-node bin/simple-nat.ts'

Now let's migrate to NAT instance.

Step 1. Create custom nat in the same VPC

pnpm cdk deploy --app 'npx ts-node bin/nat-migration.ts'

Step 2: Remove NAT Gateway manually from console

  • We can not remove NAT Gatwway from CDK due to the bug related to VPC update
  • Go to NAT gateway in VPC console to delete the NAT Gateway image image image

Step 3: Route all traffic from private subnets to NAT instance

  • 0.0.0.0/0 routing to NAT Gateway in Private Subnet will cause conflicts when we route all traffic from Private subnets to NAT instance route

  • You can do so from AWS console. Since AZ is 2, we have 2 private subnets

  • Head to VPC and select Resource map tab image

  • Select Private Subnet from Route tables in Resource map image

  • Remove NAT Gateway routes and remove image

  • Add NAT instance instead image

References

How to deploy

  • Update .env file with your AWS account ID.
CDK_ACCOUNT=xxxxxxxxxxxx
CDK_REGION=us-west-2
pnpm i
pnpm cdk deploy:ami // for using existing AMI (Pattern1)
pnpm cdk deploy:custom // for using custom AMI (Pattern2)

About

Elastic IP set up for Lambda function

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published