When deploying a Serverless with SAM CLI, normally SAM CLI will create an API Gateway instance automatically.

API Gateway has three modes PRIVATE , REGIONAL or EDGE. In a PRIVATE configuration the API gateway is restricted to a IAM resource or a VPC, this article handles a public API gateway which can be REGIONAL or EDGE.

The goal is to restrict a public API Gateway access to a certain IP address.

Normally this can be done manually through the Resource Policy tab under the API Gateway, the policy would look as following:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:[REGION]:[ACCOUNT_ID]:[GATEWAY_ID]/*/*/*"
        },
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:[REGION]:[ACCOUNT_ID]:[GATEWAY_ID]/*/*/*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": "[IP_ADDRESS]"
                }
            }
        }
    ]
}

In the example the following items need to be replaced to make it work:

  • [REGION] – replace this with desired AWS region.
  • [ACCOUNT_ID] – replace this with your AWS account id.
  • [GATEWAY_ID] – replace with the generated API Gateway id.
  • [IP_ADDRESS] – replace with the IP address to restrict access.

This works great however the downside is that it is a manual procedure, however it is possible in SAM CLI to automate this.

The first step is instead of letting SAM CLI generating the API Gateway to define it ourselves in template.yaml as following and define the Resource Policy:

Resources:
  TestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Test
      EndpointConfiguration: EDGE
      Auth:
        ResourcePolicy:
          CustomStatements: [ 
            {
              Effect: 'Allow',
              Action: 'execute-api:Invoke', 
              Resource: ['execute-api:/*/*/*'],
              Principal: '*' 
            },
            {
              Effect: 'Deny',
              Action: 'execute-api:Invoke', 
              Resource: ['execute-api:/*/*/*'],
              Principal: '*',
              Condition: {
                NotIpAddress: {
                  aws:SourceIp: "[IP_ADDRESS]"
                }
              }
            }
         ]

In this example replace [IP_ADDRESS] with the IP address to restrict access.

Because the API Gateway is self defined now, for all functions leveraging on API Gateway we need to define the API Gateway instance in the property of the event.

Events:
  HelloWorld:
    Type: Api 
    Properties:
      RestApiId:
        Ref: TestApi
      Path: /helloworld
      Method: get

Each function needs the ref set to TestApi to configure them with the self defined API Gateway.

After deploying the new structure the API Gateway will be created as defined including the defined Resource Policy.