A curious use-case came up to me this week. We have a REST API in AWS API Gateway that integrates with a Lambda. This is set up using Serverless. This is a multi-tenant system and because a former client didn’t do their cleanup, we’re still receiving a lot of calls that basically return errors (because the tenant no longer exists on our side). In AWS Lambda, this means a lot of useless invocations and a higher bill at the end of the month. Here’s a way a returning the error in API Gateway, before your Lambda is invoked. In other words, let’s have the AWS infrastructure handle this for us.

The Resources

This screenshot of the AWS Console clarifies what we want to end up with:

Let’s break this down. Previously, we only had the /{proxy+} resource. This means that the path is sent to our lambda and the lambda would handle the routing. You could choose to put your resources in API Gateway instead of using what is called Proxy Integration, but that’s not really the issue here (and also just a matter of preference in my opinion).

When a request is made to https://example.com/goodtenant/whatever, we want to invoke our Lambda and return the response. But if our bad tenant makes a request to https://example.com/badtenant/whatever, we want to stop them short and immediately return an error.

So in the screenshot above, any request to /badtenant or a sub-path of that should return an error.

The Integration

Now, we also need this badtenant resource to point to a mock integration and return (in our case) a HTTP 403. This is what it should look like:

We need an integration of type “MOCK” and have it return a HTTP 403 for any result that the mock endpoint returns.

In a mock endpoint, you can put some basic logic to return a response. Look at it like a mini-lambda. In our case, it doesn’t really matter what we return, because we’ll return a 403 in the end anyway.

The Serverless File

Now for the most important part, this is what you serverless.yml should look like (omitting the uninteresting parts):

functions:
  main:
    handler: lib/index.handler
    name: myLambda
    events:
      - http: POST /{proxy+}

...

resources:
  Resources:
    ApiGatewayResourceBadTenant:
     Type: AWS::ApiGateway::Resource
     Properties:
       ParentId:
         Fn::GetAtt: ["ApiGatewayRestApi", "RootResourceId"]
       PathPart: "badtenant"
       RestApiId:
         Ref: ApiGatewayRestApi
    ApiGatewayResourceBadTenantProxyVar:
      Type: AWS::ApiGateway::Resource
      Properties:
        ParentId:
          Ref: ApiGatewayResourceBadTenant
        PathPart: "{proxy+}"
        RestApiId:
          Ref: ApiGatewayRestApi
    ApiGatewayResourceeBadTenantProxyVarAny:
      Type: AWS::ApiGateway::Method
      Properties:
        HttpMethod: ANY
        ResourceId:
          Ref: ApiGatewayResourceBadTenantProxyVar
        RestApiId:
          Ref: ApiGatewayRestApi
        Integration:
          Type: MOCK
          PassthroughBehavior: NEVER
          RequestTemplates:
            application/json: "{\"statusCode\":403}"
          IntegrationResponses:
            - SelectionPattern: .*
              StatusCode: 403
        MethodResponses:
          - StatusCode: 403
        AuthorizationType: NONE

In essence, what this does is define a new resource called ApiGatewayResourceBadTenant. The parent (ApiGatewayRestApi) is created by the Serverless framework and is always called ApiGatewayRestApi. Under the resource, we create the proxy resource. And under that, we create the method.

The method contains a MOCK integration and returns a 403.

Testing

After deploying this with serverless, the necessary resources should be created. If we make a call to our bad tenant, we see the 403:

This is pure API Gateway and not a single of our Lambda’s was invoked.

That’s It!

It took me a while to put the pieces together, but in the end it’s a fairly simple and elegant solution. We don’t need to do anything in the AWS console manually, which means everything is automated and remains in source control. But we can still stop the bad tenant from triggering our Lambda’s too much, which is good for our Amazon bill!

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.