kdnakt blog

hello there.

CloudFrontディストリビューションを前段に立ててAPI GatewayでTLSハンドシェイク情報を取得する

Serverless FrameworkでAmazon API Gatewayの裏でLambda関数を実行している。この関数でTLSハンドシェイク情報を取得したくて試したメモ。

[やりたいこと]

Serverless Frameworkを使って、こんな感じのserverless.ymlAPI Gateway経由でLambda関数を呼び出せるようにしている。

service: cloudfront-apigw
frameworkVersion: '3'

provider:
  name: aws
  runtime: nodejs18.x
  region: ap-northeast-1

functions:
  api:
    handler: index.handler
    events:
      - httpApi:
          path: /
          method: get

このLambda関数へのリクエストについて、API Gatewayに接続しているクライアントのTLSハンドシェイク情報を取得する必要が出てきた。

過去に似たような要件ではALBを利用しており、ALBのアクセスログを後で回収することで、TLSハンドシェイク情報を取得できた。ssl_cipherとかssl_protocolのフィールドがそれだ。

docs.aws.amazon.com

API Gatewayでも似たような機能はないか、と調べてみたものの、下記のようなStackoverflowの書き込みが見つかっただけ。2019年時点ではできない、と書かれていた。

stackoverflow.com

その後のAPI Gatewayの機能リリース情報や、マッピングテンプレートに関するドキュメントを探してみたものの、目ぼしいものは見つからず。

docs.aws.amazon.com

なんとかならないか、と思っていた時に、このリリースを思い出した。CloudFrontを通せばTLSハンドシェイク情報を取得できるのでは...?

aws.amazon.com

[CloudFrontディストリビューションを被せる]

ということで、早速API Gatewayの前段にCloudFrontディストリビューションを実装してみる。以下を参考に必要最小限でディストリビューションを追加した。

krymtkts.github.io

何度か試行錯誤しつつ、最終的に先のserverless.ymlに以下を追加すると、CloudFrontディストリビューションを追加できた。

resources:
  Resources:
    MyDistribution:
      Type: AWS::CloudFront::Distribution
      Properties:
        DistributionConfig:
          Enabled: true
          Origins:
            - Id: MyApiGateway
              DomainName: !Join
                - "."
                - - !Ref HttpApi
                  - execute-api
                  - !Ref AWS::Region
                  - amazonaws.com
              CustomOriginConfig:
                OriginProtocolPolicy: https-only
          DefaultCacheBehavior:
            TargetOriginId: MyApiGateway
            ViewerProtocolPolicy: https-only
            ForwardedValues:
              QueryString: false
            # https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html#managed-cache-policy-caching-disabled
            # CachingDisabled
            CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
            # https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-origin-request-policies.html
            # AllViewerExceptHostHeader
            OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac

 

CachePolicyIdOriginRequestPolicyIdは、何かしら指定しないといけなそうだったので、CachingDisabledポリシーとAllViewerExceptHostHeaderポリシーをそれぞれ指定した。API Gatewayが動的なレスポンスを返すため、キャッシュは不要とした。
AllViewerExceptHostHeader2023年2月にリリースされた管理ポリシーで、同じくAPI Gateway向けのものである。

マネジメントコンソール上では、どちらもRecommended for API Gateway (API Gatewayでの利用推奨)と書かれており、わかりやすかった。

serverless deployコマンドを実行すると、無事CloudFrontディストリビューションが作成された。

デプロイしているLambda関数は以下のようなコードとなっている。受け取ったイベントをほぼそのままレスポンスとして返すだけのシンプルな内容だ。

module.exports.handler = async (event) => {
  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: "Go Serverless v3.0! Your function executed successfully!",
        input: event,
      },
      null,
      2
    ),
  };
};

作成されたCloudFrontディストリビューションのURLをブラウザで開くと、次のように表示された。
event.headersの中に"cloudfront-viewer-tls"というキーでTLSハンドシェイク情報が入っている。今回の場合は、"TLSv1.3:TLS_AES_128_GCM_SHA256:fullHandshake"なので、TLSバージョンはTLS1.3、暗号スイートはTLS_AES_128_GCM_SHA256、初回の旧ハンドシェイクが実行されたことが分かる。

[今後の課題]

これでやりたいことはほぼ実現できたが、今回の実装にはまだいくつか課題がある。

API GatewayのリージョンAPIエンドポイントやプライベートAPIエンドポイントでは2023年3月現在ではTLS1.3を利用できない*1が、先に見たようにCloudFrontではTLS1.3を利用できる。
加えて、API GatewayのリージョンAPIエンドポイントやプライベートAPIエンドポイントでは前方秘匿性のない暗号スイートを無効化できないが、CloudFrontではTLSv1.2_2019またはTLSv1.2_2021のポリシーを利用することで前方秘匿性に準拠できる*2
セキュリティ的にもAPI Gatewayを単独で使うのではなく、CloudFrontディストリビューションと組み合わせる構成の方が良さそうだ。

[まとめ]

  • API Gateway単体でTLSハンドシェイク情報を取得するのは難しそう
  • CloudFrontのCloudFront-Viewer-TLS-headerを利用するとAPI Gateway(のバックエンド、今回はLambda関数)でTLSハンドシェイク情報を取得できる
  • API GatewayのリージョンAPIエンドポイントやプライベートAPIエンドポイントではTLS1.3や前方秘匿性に対応していないので、CloudFrontディストリビューションと組み合わせて使うのが良さそう
  • ソースコードは以下のリポジトリにある

github.com

*1:API Gatewayでもエッジ最適化APIエンドポイントを利用すればTLS1.3を利用できる。

docs.aws.amazon.com

*2:もちろんALBでもELBSecurityPolicy-FS-1-2-Res-2020-10のセキュリティポリシーを利用すれば前方秘匿性を保つことができる。ただし、API GatewayのリージョンAPIエンドポイントやプライベートAPIエンドポイント同様、ALBは2023年3月現在TLS1.3に対応していない。

docs.aws.amazon.com