🌐

CloudFrontのビューワープロトコルポリシーにRedirect HTTP to HTTPSを設定したときの動作の確認

に公開

概要

CloudFrontのドキュメントに、ビューワープロトコルポリシーに Redirect HTTP to HTTPS を設定した時の挙動が書かれています。

Amazon CloudFront デベロッパーガイド ビューワーに HTTPS を要求する

Redirect HTTP to HTTPS
ビューワーは両方のプロトコルを使用できます。HTTP GET および HEAD リクエストは自動的に HTTPS リクエストにリダイレクトされます。CloudFront は新しい HTTPS URL とともに HTTP ステータスコード 301 (Moved Permanently) を返します。ビューワーはこの HTTPS URL を使用して CloudFront にリクエストを再送信します。

このときオリジンプロトコルポリシーに Match Viewer を設定していると、ビューワーがHTTPでCloudFrontにアクセスした場合はHTTPSにリダイレクトされるので、オリジンにはHTTPSでアクセスされます。

この動作を確認しておきたかったので、環境を準備して試してみました。

環境構築用のCloudFormationテンプレート

全体

CFnテンプレート全体
cloudfront-apigw-mock.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFront distribution fronting an API Gateway mock integration

Parameters:
  StageName:
    Type: String
    Default: prod
    Description: Stage name for the API Gateway deployment referenced by CloudFront.
  ApiKeyValue:
    Type: String
    NoEcho: true
    Description: API key shared only with CloudFront (used as X-Api-Key)

Resources:
  ApiGatewayRestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: MockApiWithCloudFront
      Description: API Gateway with a mock integration used as a CloudFront origin
      EndpointConfiguration:
        Types:
          - REGIONAL

  ApiGatewayResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      ParentId: !GetAtt ApiGatewayRestApi.RootResourceId
      PathPart: mock
      RestApiId: !Ref ApiGatewayRestApi

  ApiGatewayMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      HttpMethod: GET
      ResourceId: !Ref ApiGatewayResource
      RestApiId: !Ref ApiGatewayRestApi
      AuthorizationType: NONE
      ApiKeyRequired: true
      Integration:
        Type: MOCK
        RequestTemplates:
          application/json: '{"statusCode": 200}'
        IntegrationResponses:
          - StatusCode: 200
            ResponseTemplates:
              application/json: '{"message": "This is a mock response"}'
      MethodResponses:
        - StatusCode: 200

  ApiGatewayDeployment:
    Type: AWS::ApiGateway::Deployment
    Properties:
      RestApiId: !Ref ApiGatewayRestApi
      Description: Deployment for mock integration
    DependsOn: ApiGatewayMethod

  ApiGatewayStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      StageName: !Ref StageName
      DeploymentId: !Ref ApiGatewayDeployment
      RestApiId: !Ref ApiGatewayRestApi

  # API キーと使用量プラン(CloudFrontからのみ使用)
  ApiKey:
    Type: AWS::ApiGateway::ApiKey
    Properties:
      Name: CloudFrontOnlyKey
      Enabled: true
      Value: !Ref ApiKeyValue

  UsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      UsagePlanName: CloudFrontOnlyUsagePlan
      ApiStages:
        - ApiId: !Ref ApiGatewayRestApi
          Stage: !Ref ApiGatewayStage

  UsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    Properties:
      KeyId: !Ref ApiKey
      KeyType: API_KEY
      UsagePlanId: !Ref UsagePlan

  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: true
        Comment: CloudFront distribution redirecting HTTP viewers to HTTPS and matching origin protocol
        DefaultCacheBehavior:
          TargetOriginId: ApiGatewayOrigin
          ViewerProtocolPolicy: redirect-to-https
          AllowedMethods:
            - GET
            - HEAD
          CachedMethods:
            - GET
            - HEAD
          Compress: true
          CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # Managed-CachingDisabled
        Origins:
          - Id: ApiGatewayOrigin
            DomainName: !Sub "${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com"
            OriginPath: !Sub "/${StageName}"
            OriginCustomHeaders:
              - HeaderName: X-Api-Key
                HeaderValue: !Ref ApiKeyValue
            CustomOriginConfig:
              OriginProtocolPolicy: match-viewer
              OriginSSLProtocols:
                - TLSv1.2
              HTTPPort: 80
              HTTPSPort: 443
        PriceClass: PriceClass_200
        HttpVersion: http2and3

Outputs:
  CloudFrontDomainName:
    Description: Domain name of the CloudFront distribution
    Value: !GetAtt CloudFrontDistribution.DomainName

  ApiInvokeUrl:
    Description: Direct invoke URL for the API Gateway stage
    Value: !Sub "https://${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com/${StageName}/mock"

今回関係ある部分の抜粋

AWS::CloudFront::Distribution で以下を指定しています。

プロパティ
ViewerProtocolPolicy redirect-to-https
OriginProtocolPolicy match-viewer

また、動作確認用のオリジンとしてAPI Gatewayを設定しています。

  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: true
        Comment: CloudFront distribution redirecting HTTP viewers to HTTPS and matching origin protocol
        DefaultCacheBehavior:
          TargetOriginId: ApiGatewayOrigin
          ViewerProtocolPolicy: redirect-to-https
          AllowedMethods:
            - GET
            - HEAD
          CachedMethods:
            - GET
            - HEAD
          Compress: true
          CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # Managed-CachingDisabled
        Origins:
          - Id: ApiGatewayOrigin
            DomainName: !Sub "${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com"
            OriginPath: !Sub "/${StageName}"
            OriginCustomHeaders:
              - HeaderName: X-Api-Key
                HeaderValue: !Ref ApiKeyValue
            CustomOriginConfig:
              OriginProtocolPolicy: match-viewer
              OriginSSLProtocols:
                - TLSv1.2
              HTTPPort: 80
              HTTPSPort: 443
        PriceClass: PriceClass_200
        HttpVersion: http2and3

動作確認

環境構築

API Gatewayへのアクセスを制限するために、CloudFrontでAPIキーを付与します。
付与するAPIキーを最初に作成し、 ApiKey 変数に格納しておきます。

ApiKey=$(openssl rand -hex 32)

API Gatewayのステージ名を test 、APIキーの値に上記の ApiKey の値を設定し、CloudFormationでCloudFrontとAPI Gatewayをデプロイします。

aws cloudformation create-stack --stack-name TestCloudFront --template-body file://cloudfront-apigw-mock.yaml --parameters ParameterKey=StageName,ParameterValue=test ParameterKey=ApiKeyValue,ParameterValue="${ApiKey}"

CloudFormationでのデプロイが完了すると CloudFrontDomainName にCloudFrontのFQDNが出力されるので、 CloudFront 変数にFQDNを格納します。

CloudFront=$(aws cloudformation describe-stacks --stack-name TestCloudFront --query 'Stacks[0].Outputs[?OutputKey==`CloudFrontDomainName`].OutputValue | [0]' --output text)

ビューワーからHTTPでCloudFrontにアクセスした場合

curl でプロトコルをhttpに設定してCloudFrontにアクセスした場合は、最終的なアクセス先を示す url_effective でプロトコルがhttpsになっていることが確認できました。

curl -s -w "%{http_code} %{url_effective}\n" -L http://${CloudFront}/mock
{"message": "This is a mock response"}200 https://dxclb8f0tip3a.cloudfront.net/mock

ビューワーからHTTPSでCloudFrontにアクセスした場合

curl でプロトコルをhttpsに設定してCloudFrontにアクセスした場合は、リダイレクトされることなくMatch Viewer の設定に従ってhttpsでオリジンにアクセスされています。

curl -s -w "%{http_code} %{url_effective}\n" -L https://${CloudFront}/mock
{"message": "This is a mock response"}200 https://dxclb8f0tip3a.cloudfront.net/mock

GETメソッドでの通信の流れ

今回の設定でのクライアントからオリジンまでの通信の流れをまとめると以下のようになります。

参考

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/using-https-viewers-to-cloudfront.html#configure-cloudfront-HTTPS-viewers
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/using-https-cloudfront-to-custom-origin.html#using-https-cloudfront-to-origin-distribution-setting

Discussion