- 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.
- 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.
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.
- 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.
There are 2 patterns to implement this architecture. I implemented both patterns, but recommended to use Pattern2.
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;
- 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 runningnetstat -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,
});
- 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.
pnpm cdk deploy --app 'npx ts-node bin/nat-migration.ts'
- 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
-
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
- https://docs.aws.amazon.com/vpc/latest/userguide/VPC_NAT_Instance.html
- https://medium.com/nerd-for-tech/how-to-turn-an-amazon-linux-2023-ec2-into-a-nat-instance-4568dad1778f
- 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)