Defining API Gateway APIs using OpenAPI cover image

Use an OpenAPI specification to define API Gateway APIs. This is good practice because:

  • The specification forces you to think about the design upfront before touching code which is expensive to correct later
  • This contract first approach allows frontend, backend, and test developers to work in parallel
  • Endpoints can be mocked for developers and testers to test against
  • Serverless Application Model (SAM) uses the specification to configure the API Gateway API, this makes it living documentation, evolving alongside code and infrastructure.

TL;DR

  1. Create an OpenAPI specification with API Gateway Extensions to OpenAPI
  2. Copy the file to an S3 bucket
  3. Define an API Gateway (AWS::Serverless::Api) resource in your SAM template
  4. Use the AWS::Include transform to include and transform the OpenAPI specification from S3
  5. Assign the AWS::Include transform function to the DefinitionBody property of the API Gateway resource
  6. Package and deploy your SAM application

Source Code

The source code and instructions to build and deploy this example to AWS can be found here: https://github.com/karlkyck/api-gateway-openapi. Running this example on AWS will incur costs so be sure to delete the CloudFormation stacks when you are finished experimenting.

OpenAPI Definition

Start by creating your OpenAPI specification in a dedicated file.

The following OpenAPI specification defines one RESTful endpoint with two operations on that endpoint.

---
openapi: 3.0.3
 
...
 
x-amazon-apigateway-policy:
  Version: '2012-10-17'
  Statement:
    - Effect: Allow
      Principal: '*'
      Action: execute-api:Invoke
      Resource:
        - execute-api:/*/*/*
      Condition:
        IpAddress:
          'aws:SourceIp':
            Ref: AllowedIpsList
 
...

Where AllowedIpsList is a comma separated list of IPs or CIDR blocks defined as a parameter in your CloudFormation template.

Rationale

When it comes to accessing and consuming resources on the cloud a least privileged approach is best. IP restriction on your API Gateway APIs can help.

It is possible to apply an API Gateway Resource Policy to an API Gateway API during deployment via CloudFormation.

CloudFormation Template

Your API Gateway API definition and reference to your OpenAPI specification is defined in your SAM (Serverless Application Model) template.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Parameters:
  AllowedIpsList:
    Type: List<String>

Globals:
  Function:
    Runtime: nodejs12.x
    Timeout: 30
    AutoPublishAlias: live

Resources:

  ApiGatewayApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: live
      EndpointConfiguration: REGIONAL
      DefinitionBody:
        openapi: 3.0.3
        info:
          title: API Gateway IP Filtering Example API
          version: 1.0.0

        x-amazon-apigateway-policy:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal: '*'
              Action: execute-api:Invoke
              Resource:
                - execute-api:/*/*/*
              Condition:
                IpAddress:
                  'aws:SourceIp':
                    Ref: AllowedIpsList

        paths:
          /api/example:
            get:
              summary: Example API Endpoint
              operationId: example
              responses:
                '200':
                  description: Success
              x-amazon-apigateway-integration:
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ExampleFunction.Arn}:live/invocations
                httpMethod: POST
                type: aws_proxy

  ExampleFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      InlineCode: |
        exports.handler = (event, context, callback) => {
        	callback(
        		null,
        		{
        			statusCode: 200,
        			body: JSON.stringify({
        				message: 'Hello World'
        			})
        		});
        };
      Events:
        ApiGatewayApiEvent:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGatewayApi
            Path: /api/example
            Method: get

The DefinitionBody property of the ApiGatewayApi allows you to specify an OpenAPI specification definition which is transformed and applied by CloudFormation upon deployment.

The list of allowed IPs is passed to the SAM template as the AllowedIpsList parameter. This parameter is referenced from within the OpenAPI specification using the Ref function.

OpenAPI Specification

The OpenAPI specification, embedded in the CloudFormation template above, contains the definition of your API. The API Gateway Resource Policy is declared in the specification as an API Gateway Extension to OpenAPI.

---
openapi: 3.0.3
info:
  title: API Gateway IP Filtering Example API
  version: 1.0.0
 
x-amazon-apigateway-policy:
  Version: '2012-10-17'
  Statement:
    - Effect: Allow
      Principal: '*'
      Action: execute-api:Invoke
      Resource:
        - execute-api:/*/*/*
      Condition:
        IpAddress:
          'aws:SourceIp':
            Ref: AllowedIpsList
 
paths:
  /api/example:
    get:
      summary: Example API Endpoint
      operationId: example
      responses:
        '200':
          description: Success
      x-amazon-apigateway-integration:
        uri:
          Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ExampleFunction.Arn}:live/invocations
        httpMethod: POST
        type: aws_proxy

Here we see the Ref function referring to the AllowedIpsList parameter.

This API Gateway Resource Policy is allowing all invocations where the source IP is in the list of IPs supplied.

Source Code

The source code and instructions to build and deploy this example to AWS can be found here: https://github.com/karlkyck/api-gateway-ip-filtering. Running this example on AWS will incur costs so be sure to delete the CloudFormation stacks when you are finished experimenting.

Conclusion

This is a simple, cheap way to allow access to your API Gateway APIs only from certain IP addresses. AWS WAF is an alternative, but it incurs costs.

About the author

Hi, I'm Karl Kyck a cloud architect specialising in building sustainable serverless architectures on AWS.