SAMでAPI Gateway(REST API)のデフォルトエンドポイントを無効化する

2022.03.20

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

いわさです。

以下の記事でmTLS + カスタムドメインのAPI GatewayをSAMでデプロイしました。
しかし、デフォルトエンドポイントが無効化出来ていませんでした。

この記事ではデフォルトエンドポイントの無効化方法をためします。

DisableExecuteApiEndpointはうまくいかない?

SAMではこのための設定値が用意されています。

AWS::Serverless::Api - AWS Serverless Application Model

DisableExecuteApiEndpoint
クライアントがデフォルトの execute-api エンドポイント (https://{api_id}.execute-api.{region}.amazonaws.com) を使用して API を呼び出すことができるかどうかを指定します。デフォルトで、クライアントはデフォルトのエンドポイントを使用して API を呼び出すことができます。クライアントが API の呼び出しにカスタムドメイン名以外を使用しないようにするには、デフォルトのエンドポイントを無効にします。

タイプ: ブール

必須: いいえ

AWS CloudFormation との互換性: このプロパティは、AWS::ApiGateway::RestApi リソースの DisableExecuteApiEndpoint プロパティに直接渡されます。

このプロパティ早速ためしてみましょう。

  Api:
    Type: AWS::Serverless::Api
    Properties:
      Name: !Ref ApiName
      EndpointConfiguration: REGIONAL
      OpenApiVersion: 3.0.1
      StageName: iwasa-stage
      DefinitionBody:
        Fn::Transform:
          Name: AWS::Include
          Parameters:
            Location: ./openapi.json
      Domain: 
        DomainName: !Ref CustomDomainName
        CertificateArn: !Ref Certificate
        EndpointConfiguration: REGIONAL
        MutualTlsAuthentication: 
          TruststoreUri: !Ref TrustStoreS3Uri
        Route53: 
          DistributionDomainName: !Ref CustomDomainName
          HostedZoneId: !Ref HostedZoneId
        SecurityPolicy: TLS_1_2
      DisableExecuteApiEndpoint: true

  Certificate:
    Type: AWS::CertificateManager::Certificate
    Properties:
      DomainName: !Ref CustomDomainName
      DomainValidationOptions:
        - DomainName: !Ref CustomDomainName
          HostedZoneId: !Ref HostedZoneId
      ValidationMethod: DNS

しかし、デフォルトエンドポイントが無効化されていません。

こ、これは...

AWS::ApiGateway::RestApi リソースの DisableExecuteApiEndpoint プロパティに渡されない

ドキュメントでは、RestApiリソースのDisableExecuteApiEndpointプロパティに渡されるとされていますが、SAMのスタックから展開されたCloudFormationを確認してみましょう。

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "---",
  "Parameters": {
:
  },
  "Resources": {
    "Api": {
      "Type": "AWS::ApiGateway::RestApi",
      "Metadata": {
        "SamResourceId": "Api"
      },
      "Properties": {
        "Body": {
          "paths": {
            "/": {
              "get": {
                "x-amazon-apigateway-integration": {
                  "httpMethod": "GET",
                  "passthroughBehavior": "when_no_match",
                  "type": "http_proxy",
                  "uri": "http://checkip.amazonaws.com",
                  "responses": {
                    "default": {
                      "statusCode": "200"
                    }
                  }
                },
                "responses": {
                  "200": {
                    "content": {
                      "application/json": {
                        "schema": {
                          "$ref": "#/components/schemas/Empty"
                        }
                      }
                    },
                    "description": "200 response"
                  }
                }
              }
            }
          },
          "openapi": "3.0.1",
          "components": {
            "schemas": {
              "Empty": {
                "type": "object",
                "title": "Empty Schema"
              }
            }
          },
          "x-amazon-apigateway-endpoint-configuration": {
            "disableExecuteApiEndpoint": true
          }
        },
        "Name": {
          "Ref": "ApiName"
        },
        "Parameters": {
          "endpointConfigurationTypes": "REGIONAL"
        },
        "EndpointConfiguration": {
          "Types": [
            "REGIONAL"
          ]
        }
      }
    },
:
  }
}

AWS::ApiGateway::RestApiDisableExecuteApiEndpointプロパティには値は渡されていないですね。

ただし、(代わりに?)OpenAPIドキュメントのx-amazon-apigateway-endpoint-configuration.disableExecuteApiEndpointが設定されています。
一見すると動作しそうにも見えるのですが、実はこの設定値、Swagger2.0は最上位ベンダー拡張に存在する必要があるので上記のとおりで良いのですが、OpenAPI3.0ではサーバーオブジェクトのベンダー拡張の下に存在する必要があるそうです。

OpenAPIを使っている場合は直接指定する

よって、ここではSAMのDisableExecuteApiEndpointプロパティは使わずに、OpenAPIドキュメントへ以下のように直接反映させることが現時点の解決策になります。

openapi.json

{
    "openapi" : "3.0.1",
    "servers": [
      {
        "x-amazon-apigateway-endpoint-configuration": {
          "disableExecuteApiEndpoint": true
        }
      }
    ],
    "paths" : {
        :
    },
    "components" : {
        :
    }
  }

デプロイしてみましょう。

お、デフォルトエンドポイントが無効化されている!
cURLでも確認してみます。

$ curl https://apihoge.tak1wa.com                                                     
curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to apihoge.tak1wa.com:443 
$ curl --key ./keys/my_client.key --cert ./keys/my_client.pem https://apihoge.tak1wa.com
120.51.74.235
$ curl https://4pqf8o4iqj.execute-api.ap-northeast-1.amazonaws.com/iwasa-stage
curl: (6) Could not resolve host: 4pqf8o4iqj.execute-api.ap-northeast-1.amazonaws.com

良さそうです。

さいごに

本日はSAMでデフォルトエンドポイントを無効化する方法を確認してみました。

試してないのですが、Swagger2.0であればDisableExecuteApiEndpointが使えるのだと思います。
ドキュメントと挙動は異なりますが。

OpenAPIかSwaggerかなどでこのあたりの挙動が変わってくるとは盲点でした。