i need to connect elastic cache and dynamo db from a single lambda function. My code is
exports.handler = (event, context, callback) => {
var redis = require("redis");
var client;
function connectRedisClient() {
client = redis.createClient(6379, "dgdfgdfgdfgdfgdfgfd.use1.cache.amazonaws.com", { no_ready_check: true });
}
connectRedisClient();
client.set('sampleKey', 'Hello World', redis.print);
console.log("set worked");
client.quit();
var AWS = require("aws-sdk");
var docClient = new AWS.DynamoDB.DocumentClient();
var table = "dummy";
var year = 2015;
var title = "The Big New Movie";
var params = {
TableName: table,
Item: {
"userid": "manafcj",
"year": year,
"title": title,
"test1": [645645, 7988],
"info": {
"plot": "Nothing happens at all.",
"rating": 0
}
}
};
console.log("Adding a new item...");
docClient.put(params, function (err, data) {
if (err) {
console.error("Unable to add item. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("Added item:", JSON.stringify(data, null, 2));
}
});
callback(null, 'Hello from Lambda');
};
I executed this lambda code without configuring vpc, elastic cache section is not working , but dynamo insertion is done perfectly.
after that i made setup for VPC in my account by following steps.
create vpc
name : test-vpc-name
CIDR block:172.31.0.0/16
Tenancy:Default
Create a new subnet.
name tag : test-subnet-1a
CIDR block :172.31.0.0/20
name tag : test-subnet-1b
CIDR block :172.31.16.0/20
Create a route table
name tag : test-route-table
Create a internet gateway
name:test-internet-gateway
Attach VPC
Route all outbound 0.0.0.0/0 traffic in routes
Create a route table subnet association
Create a NAT Gateway
subnet : test-subnet-1a
also i have configured my elastic cache setup by following steps
Create subnet cache group
name : test-cache-group
Create elastic cache
type: redis
Cluster Name : test-cache
subnet cache group : test-cache-group
Finally, i have configured newly created vpc on my lambda function. Then redis-elastic cache connection is working fine, but dynamo db connection is lost. I need both working fine from a single lambda function.
I think, some fault in VPC configuration with NAT Gateway.
What is the actual issue in this setup?
Lambda and DynamoDB are executed in the AWS Public Cloud. Both are services executed in a internet facing environment. The Elastic Cache Cluster, otherwise, is user managed service that runs on your own VPC.
The first option to give access to your elastic cache cluster to your lambda function is using a NAT instance to foward external network connections to Elastic Cache cluster inside your VPC. You can get use the instructions from this document to help you with this task.
The second option, is the one that you already tried. Amazon says that when you configure this option it does not means that the Lambda will be executed inside your VPC. What is does it define the Elastic Network Interface of the Lambda container to access your VPC. At the end of day I don't think that this makes difference. You can see the details here.
But the point is, the container where your lambda is executed has only one Elastic Network Interface. If you configure your lambda to use your VPC, the Network Interface will be configured to access your subnet using a private IP and lost the internet connection. So, it will not be able to access DynamoDB unless you have a configure NAT instance/Gateway in your VPC.
As per you told us. You configured your VPC with a NAT Gateway. If all were correctly configured, this should be working. Maybe you can try the fist option, leaving your lambda outside your VPC and configuring the NAT Gateway to route the inboud connections to your Elastic Cache Cluster.
Why don't try and tell us the result?
There is now a relatively easy solution: VPC Endpoints.
"Previously, if you wanted your EC2 (elroy: or lambda) instances in your VPC to be able to access DynamoDB, you had two options. You could use an Internet Gateway (with a NAT Gateway or assigning your instances public IPs) or you could route all of your traffic to your local infrastructure via VPN or AWS Direct Connect and then back to DynamoDB."
"A VPC endpoint for DynamoDB enables Amazon EC2 instances in your VPC to use their private IP addresses to access DynamoDB with no exposure to the public Internet...Your EC2 instances do not require public IP addresses, and you do not need an Internet gateway, a NAT device, or a virtual private gateway in your VPC. You use endpoint policies to control access to DynamoDB. Traffic between your VPC and the AWS service does not leave the Amazon network. "
The above quotes come from the links below. Note the the references to "EC2 instances" apply to lambda contexts as well.
See https://aws.amazon.com/blogs/aws/new-vpc-endpoints-for-dynamodb/
and
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/vpc-endpoints-dynamodb.html
Edited to provide more details in line.
Related
I've followed the tutorial here to create a VPC with public and private subnets.
Then I set up an AWS lambda function inside the public subnet to test if it could connect to the outside internet.
Here's my lambda function written in python3
import requests
def lambda_handler(event, context):
r = requests.get('http://www.google.com')
print(r)
The function above failed to fetch the content of http://www.google.com when I set it inside the public subnet in a VPC.
Here's the error message:
"errorMessage": "HTTPConnectionPool(host='www.google.com', port=80):
Max retries exceeded with url: / (Caused by
NewConnectionError(': Failed to establish a new connection: [Errno 110]
Connection timed out',))", "errorType": "ConnectionError",
I don't understand why.
The route table of the public subnet looks like this:
The GET request to http://www.google.com should match igw-XXXXXXXXX target. Why can't the internet-gateway(igw) deliver the request to http://www.google.com and get back the website content?
This article says that I must set the lambda function inside the private subnet in order to have internet access.
If your Lambda function needs to access private VPC resources (for
example, an Amazon RDS DB instance or Amazon EC2 instance), you must
associate the function with a VPC. If your function also requires
internet access (for example, to reach a public AWS service endpoint),
your function must use a NAT gateway or instance.
But it doesn't explain why I can't set the lambda function inside the public subnet.
Lambda functions connected to a VPC public subnet cannot typically access the internet.
To access the internet from a public subnet you need a public IP or you need to route via a NAT that itself has a public IP. You also need an Internet Gateway (IGW). However:
Lambda functions do not, and cannot, have public IP addresses, and
the default route target in a VPC public subnet is the IGW, not a NAT
So, because the Lambda function only has a private IP and its traffic is routed to the IGW rather than to a NAT, all packets to the internet from the Lambda function will be dropped at the IGW.
Should I Configure my Lambda Function for VPC Access?
If your Lambda function does not need to reach private resources inside your VPC (e.g. an RDS database or Elasticsearch cluster) then do not configure the Lambda function to connect to the VPC.
If your Lambda function does need to reach private resources inside your VPC, then configure the Lambda function to connect to private subnets (and only private subnets).
NAT or Not?
If the Lambda function only needs access to resources in the VPC (e.g. an RDS database in a private subnet) then you don't need to route through NAT.
If the Lambda function only needs access to resources in the VPC and access to AWS services that are all available via private VPC Endpoint then you don't need to route through NAT. Use VPC Endpoints.
If your Lambda function needs to reach endpoints on the internet then ensure a default route from the Lambda function's private subnets to a NAT instance or NAT Gateway in a public subnet. And configure an IGW, if needed, without which internet access is not possible.
Be aware that NAT gateway charges per hour and per GB processed so it's worth understanding how to reduce data transfer costs for NAT gateway.
Best Practices
When configuring Lambda functions for VPC access, it is an HA best practice to configure multiple (private) subnets across different Availability Zones (AZs).
Intermittent Connectivity
Be sure that all the subnets you configure for your Lambda function are private subnets. It is a common mistake to configure, for example, 1 private subnet and 1 public subnet. This will result in your Lambda function working OK sometimes and failing at other times without any obvious cause.
For example, the Lambda function may succeed 5 times in a row, and then fail with a timeout (being unable to access some internet resource or AWS service). This happens because the first launch was in a private subnet, launches 2-5 reused the same Lambda function execution environment in the same private subnet (the so-called "warm start"), and then launch 6 was a "cold start" where the AWS Lambda service deployed the Lambda function in a public subnet where the Lambda function has no route to the internet.
You can make a lambda function access the public internet from within your VPC, you just need to make sure you really need it.
For accessing resources external to AWS such as Google API (like OP's example) you do need a Public IP. For other cases like RDS or S3 you don't need Public IP, you can use a VPC Endpoint, so communication between your Lambda and the desired AWS Service doesn't leave AWS network.
By default some AWS Services are indeed reached via public internet, but it doesn't have to be.
[EDIT]
Someone was concerned about scalability in the comments, but they missed this from AWS Docs:
"Multiple Lambda functions can share a network interface, if the functions share the same subnet and security group"
Also, you must have a Public IP for reaching Public Internet, whether you're using Lambda, EC2, ECS, even if you use a NAT Gateway it needs an Elastic Public IP if you want to reach the public internet through it.
Solution
To do that, you need to assign Elastic Public IPs to the Network Interfaces for each subnet linked to your lambda. First let's figure which subnets and security groups are linked to your lambda:
Next, go to EC2 Service, find the Public IPs menu under Network & Security. Allocate one IP for each subnet (in the example above there are two subnets).
Go to Network Interfaces menu, find the network interfaces attached to your lambda (same subnet and security group).
Associate the Public IPs in the actions menu for each one:
That's it, now your Lambda can reach out to public internet.
I have a kubernetes cluster and an RDS configured in terraform, now i want to whitelist the node-IPs for the RDS. Is there a way to somehow access the node-pool instances from the cluster-config? What i basically want for the RDS config is something like
ip_configuration {
dynamic "authorized_networks" {
for_each = google_container_cluster.data_lake.network
iterator = node
content {
name = node.network.ip
value = node.network.ip
}
}
}
But from what i see there seems to be no way to get a list of the nodes/the-IPs..
I tried
ip_configuration {
authorized_networks {
value = google_container_cluster.my_cluster.cluster_ipv4_cidr
}
}
which resulted in Non-routable or private authorized network (10.80.0.0/14).., invalid so it looks like this only works with public IPs. Or i have to setup a separate VPC for that?
i would suggest you to first set up the NAT gateway in front of the GKE so that you can manage your all outgoing traffic from a single egress point.
You can use this terraform to create & setup the NAT gateway : https://registry.terraform.io/modules/GoogleCloudPlatform/nat-gateway/google/latest/examples/gke-nat-gateway
Module source code : https://github.com/GoogleCloudPlatform/terraform-google-nat-gateway/tree/v1.2.3/examples/gke-nat-gateway
Using NAT gateway your all Nodes traffic will be going out of single IP and you can whitelist this single IP into the RDS.
Since RDS is in AWS service, VPC peering is not possible otherwise if are using the GCP SQL that would also one option.
I am trying to produce the correct CDK scripts (TypeScript) to create an environment with a Lambda (accessed via API Gateway) that can call an RDS (Sql Server instance).
I believe I have it mostly working, but I wanted to connect to the RDS instance from my development machine to run some queries and check on a few things.
My RDS instance is in a private subnet, and so I believe in order to connect to it I need to add an Internet Gateway and security group to allow access on the appropriate ports.
For the life of me I can figure out the last piece, how to add the internet gateway using CDK.
The latest script I have tried is as follows:
const privateSubnectConfiguration = {
cidrMask: 26,
name: 'private-subnet',
subnetType: SubnetType.PRIVATE,
};
const publicSubnectConfiguration = {
cidrMask: 26,
name: 'public-subnet',
subnetType: SubnetType.PUBLIC,
};
const vpc = new Vpc(this, props.generateId('vpc'), {
maxAzs: 2,
subnetConfiguration: [privateSubnectConfiguration, publicSubnectConfiguration],
natGateways: 1,
});
vpc.addGatewayEndpoint(props.generateId('internet-gateway'), {
service: { name: "ig-service" }
})
Which then errors with The Vpc Endpoint Service 'ig-service' does not exist (Service: AmazonEC2; Status Code: 400; Error Code: InvalidServiceName;
I see references to the CfnInternetGateway in the docs, but just can't figure out how to attach a new one to my VPC?
Can you please help with the syntax?
First of all, you cannot directly access a database in a private subnet. You have to deploy a proxy instance in your public subnet and forward the required ports to access your database.
When using CDK VPC construct, an Internet Gateway is created by default whenever you create a public subnet. The default route is also setup for the public subnet.
So you should remove addGatewayEndpoint() from your code, which adds a Gateway VPC Endpoint that you don't need.
You may also consider using SubnetType.ISOLATED to create a private subnet without a NAT GW, which may be redundant in your case. SubnetType.PRIVATE creates a NAT Gateway by default.
I'm trying to programmatically set up a new instance of Cloud SQL which has a Private IP, but the creation fails. I need a Private IP to connect from a GKE Kubernetes cluster.
Programmatically creating a new Cloud SQL instance only succeeds after a Cloud SQL with a Private IP has been created manually first. My assumption is that the manual creation sets up the necessary VPC network peering. However it fails in case no Cloud SQL instance was created manually first.
How do I programatically create the VPC network peering required to create a Cloud SQL instance which has a Private IP?
This is the request I'm making to create the Cloud SQL instance with a private ip.
const res = await client.request({
url: `https://www.googleapis.com/sql/v1beta4/projects/${projectId}/instances`,
method: "POST",
data: {
name: "my-database-8",
settings: {
tier: "db-f1-micro",
ipConfiguration: {
privateNetwork: `projects/${projectId}/global/networks/default`,
ipv4Enabled: true
}
},
databaseVersion: "MYSQL_5_7"
}
})
I would expect the Cloud SQL instance with private networking to be created successfully, even when no Cloud SQL instance was created manually first.
My assumption is that the manual creation sets up the necessary VPC network peering.
You are correct.
How do I programatically create the VPC network peering required to create a Cloud SQL instance which has a Private IP?
It involves reserving an IP address range in your VPC, and establish peering with one of networking services. Detailed steps provided in the public doc. (look at gcloud section).
I have been debugging, configuring and you name it the last couple of hours and i can't seem to figure out why this is happening.
I am trying to invoke a lambda function which is just retrieving basic information from ec2. when i test this lambda function in the aws console it seems to be working fine. However, invoking it in another lambda, using following code;
BasicAWSCredentials awsCreds = new BasicAWSCredentials("key1" , "key2");
AWSLambdaClientBuilder builder = AWSLambdaClientBuilder.standard()
.withRegion("eu-west-1")
.withCredentials(new AWSStaticCredentialsProvider(awsCreds));
AWSLambda client = builder.build();
InvokeRequest req = new InvokeRequest()
.withFunctionName("GetWhateverIneed");
InvokeResult result = client.invoke(req);
it simply times out. No response whatsoever... Both Lambdas are connected to the VPC and all subnets
I think it is my new VPC that is causing this problem. My VPC is consisting of:
1 VPC .
-2x Subnets (1x Private with ipv4 10.0.0.0/17, 1x Public with ipv4 10.0.128.0/17).
-1x IGW Connected to the Private subnet.
-1x NAT Gateway connected to the Public subnet .
-2x Endpoints (One for Ec2, One for SecretsManager)
I have also configured two route tables,
One for the "public" subnet:
"Routes" ->
Destination: 10.0.0.0/16 Target: local
Destination: 0.0.0.0/0 Target: My Internet Gateway(IGW)
One for the "private" subnet:
"Routes" ->
Destination: 10.0.0.0/16 Target: local .
Destination: 0.0.0.0/0 Target: My nat
I have made sure both of my Lambdas is running on the same VPC, they are both using the same security group:
This is my first time working with VPC so it is possible that i have missed something.
If your Lambda function is VPC attached, it needs to be able to communicate via your VPC to the AWS API. Lambdas do not talk to other Lambdas over the network, they initiate requests with the AWS API or an API Gateway, which passes the request on to the Lambda Function.
If you need a VPC attached Lambda to initiate another Lambda, it needs to be able to get to the AWS API or API Gateway via the internet. Alternatively, you can keep it all on private networks by adding a VPC Endpoint to the API Gateway Service.
A pattern I follow in similar circumstances is described in this previous post of mine: https://stackoverflow.com/a/43969112/6427978