3
votes

Objective

  • I am using the „new“ API Gateway Websocket service of AWS
  • I want to connect using a Websocket client to a Lambda function
  • I want to respond to the client using the ApiGatewayManager
  • Lambda function needs to be in a VPC with my RDS for better security (RDS is not public accessible, except for specified IPs in the security group)
  • I am using Node.js 8 in combination with Typescript

Approach

I created a regional AWS API Gateway for Websockets and added a Lambda function for $connect, $disconnect and one for the action subscribeChannel.

I am able to connect and send messages to connected clients.

I also created a VPC with 3 private subnets, all of them are located in eu-central-1 each one in a different Availability Zone (AZ).

Lambda Functions loose their access to the public internet when you add them to a VPC, therefore one approach is adding another public subnet and adding a NAT Gateway to it.

Now I am changing the routing table of the private subnet to delegate 0.0.0.0/0 to the NAT and in the routing table of the public subnet it routes 0.0.0.0/0 to a Internet Gateway.

This seems to work to get access to the public internet, e.g. I am able to request https://google.com but the ApiGatewayManagement times out as if cannot resolve the AWS service.

Then I looked into VPC Endpoints, as they are designed to make public AWS Services available in private subnets, without routing through the internet. I am able to set it up and receive the private DNS urls. But here I am stuck, I do not know how to use it in my setup/code.

I am managing the whole project using the serverless framework and Cloud Formation resources.

Issue

If the action handler for subscribeChannel is associated with the VPC then the request to XXXXXXXX.execute-api.eu-central-1.amazonaws.com/develop times out, as it cannot reach the public internet.

Is a NAT Gateway the correct approach or do I need to use a VPC Endpoint for execute-api? How do I correctly configure the VPC to use this private DNS?

Code

async function channelHandler(event, context) {
    return new aws_sdk_1.ApiGatewayManagementApi({
        apiVersion: "2018-11-29",
        endpoint: event.requestContext.domainName + "/" + event.requestContext.stage,
    })
    .postToConnection({
        ConnectionId: event.requestContext.connectionId,
        Data: "Hello, world!",
    }).promise()
    .then(() => {
        return {
            statusCode: 200,
            body: "Sent message!",
        };
    })
    .catch((error) => {
        return {
            statusCode: 500,
            body: JSON.stringify(error),
        };
    });
}

Used resources so far:

1

1 Answers

1
votes

I did recreated the full project from my configuration and "magically" started working. Therefore I can only make an assumption for what was going on:

Every Availability Zone has a private and a public subnet. According to the AWS Documentation for NAT Gateways it is necessary to create a NAT Gateway in each of them, but I only had one NAT Gateway in Zone A configured.

I changed the configuration of my Lambda functions to be placed in only one AZ (I do not want to have full redundancy until the project is in production) and now the NAT Gateway is in the same one.

Simple graph:

Client --[AWS]--> Lambda --[Private Subnet]--> NAT --[Public Subnet]--> API Gateway 

I am still wondering if it is possible to reach API Gateway using a VPC Endpoint instead of the API Gateway, but for now I will stay with this configuration.