📀

CDKのCloudFront → S3 WebsiteHostingの構成で、504エラーが発生する件を調査してみた

2022/10/31に公開

何が起こったか?

CloudFront → WebsiteHostingのS3の構成をCDKで作成していました。

ところが、なぜか CloudFront → S3 の接続で504エラーが起こります。

初回の作成時だけではなく、CloudFrontの設定値を更新した際も同じエラーが起こるので、解消しないと面倒です。

さらに、見た目上は特に設定値を何も変えずに、マネジメントコンソールからCloudFrontを再デプロイするとなぜか解消されます。設定値の問題ではなく、CDKやCloudFormation特有のエラーかと思い、調査してみることにしました。

実現したい構成

構成は、S3をリダイレクトのためだけに利用する構成です。リダイレクトのためだけに使用するS3には、オブジェクトを保存しません。

S3をリダイレクトに利用する構成を知らない場合は、以下のようなブログをご覧ください。

https://dev.classmethod.jp/articles/s3-static-website-hosting-redirect-configuring/

構成図です。

※ちなみに、今回はリダイレクト用のS3を設定していますが、通常のウェブサイトホスティングのS3にも当てはまる話だということが、調査後に判明しました。

CDKでdeployしてみる

デプロイしたあと、CloudFrontのURLを叩きます。すると、以下のようにやはり504エラーです。

『オリジンを編集』→ オリジンドメインの先頭に”http://” を加えて 『変更を保存』をする。

※http:// を付けても、変更を保存押下時には先頭のhttp:// は取れるし、再度オリジン編集時にもhttp:// は消える。

デプロイが完了した後に再度CloudFrontのURLを叩くと、今度は成功してexample.comにRedirectしました。

さらに、設定値を更新して今度はCDKで再デプロイします。

すると、また504エラーとなりました。

ここまでの挙動は、以前から知っていた挙動です。

ここで利用したCDKのコードです。
cdk init —language typescript
でテンプレートを作成した後、libフォルダ下のファイルしか修正していません。

lib/app-stack.ts

import * as cdk from 'aws-cdk-lib';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from "aws-cdk-lib/aws-cloudfront-origins";
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';

export class CdkCfS3Stack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    
    const redirectBucket = new s3.Bucket(this, "RedirectBucket", {
      bucketName: "redirect-test-20221030",
      encryption: s3.BucketEncryption.S3_MANAGED,
      enforceSSL: true,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      eventBridgeEnabled: true,
      websiteRedirect: {
        hostName: "example.com",
        protocol: s3.RedirectProtocol.HTTPS,
      },
    });

    // CloudFront
    const RedirectDistribution = new cloudfront.Distribution(this, "RedirectDistribution", {
      enabled: true,
      priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL,
      minimumProtocolVersion: cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021,
      httpVersion: cloudfront.HttpVersion.HTTP2,
      enableIpv6: true,
      defaultBehavior: {
        origin: new origins.HttpOrigin(redirectBucket.bucketWebsiteDomainName, {}),
        viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
        allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD,
        cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
        compress: true,
        cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD,
      },
    });
  }
}

マネジメントコンソールからCloudFrontを作成してみる

先ほどCDKで作成したものと同じS3に対して、マネジメントコンソールからCloudFrontを新たに設定してみます。

以下を検証するためです。

仮定: マネジメントコンソールから作成した場合、最初からRedirectが成功する

すべて、CDKから作成されたCloudFrontの値と揃えました。

デプロイ完了後、CloudFrontのURLを叩きます。

予想通り、example.comへのリダイレクトが成功しました。

CDKとマネジメントコンソールでの差分を調べてみる

CLIコマンドで、本当に設定値に差分が無いか調査してみます。

以下のコマンドで吐き出されたjsonを調査しました。

aws cloudfront get-distribution --id <ID>

すると、以下の気になるdiffが検出されました。

CDK側が、https-onlyの設定値です。

-                            "OriginProtocolPolicy": "https-only",
+                            "OriginProtocolPolicy": "http-only",

もしこの設定値が原因なら、CDKでデプロイしたものをマネジメントコンソールから再デプロイしたとき、この設定値が変更されているはずです。

こちらも試してみます。

OriginProtocolPolicyがマネジメントコンソールからの再デプロイ時に変更されるか調査

504のエラーが起きているCloudFrontディストリビューションの設定値を用意し、先ほど同様にhttp:// を先頭につけて、再デプロイします。

そして、こちらの設定を行う前と後で、設定値を見比べてみます。

すると予想通り、マネジメントコンソールで修正した前と後では、

OriginProtocolPolicyが https-only → http-only に 書き変わっていました!

つまり、OriginProtocolPolicyがhttp-onlyになるようにCDK側のコードを修正してデプロイすれば、マネジメントコンソールでの編集を挟まなくても、見ることが出来るはずです。

というわけで、設定値を変更してデプロイしてみます。

OriginProtocolPolicyをhttp-onlyにしてCDKデプロイ

差分は以下の部分になります。

こちらを修正し再デプロイして、エラーが出なければ、原因特定となります。

        origin: new origins.HttpOrigin(redirectBucket.bucketWebsiteDomainName, {
          protocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
        }),

こちら再デプロイ後CloudFrontをのURLを叩くと、やはり正常にリダイレクトが行われました。

修正後のコード全文はこちらです。

lib/app-stack.ts

import * as cdk from 'aws-cdk-lib';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from "aws-cdk-lib/aws-cloudfront-origins";
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';

export class CdkCfS3Stack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    
    const redirectBucket = new s3.Bucket(this, "RedirectBucket", {
      bucketName: "redirect-test-20221030",
      encryption: s3.BucketEncryption.S3_MANAGED,
      enforceSSL: true,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      eventBridgeEnabled: true,
      websiteRedirect: {
        hostName: "example.com",
        protocol: s3.RedirectProtocol.HTTPS,
      },
    });

    // CloudFront
    const RedirectDistribution = new cloudfront.Distribution(this, "RedirectDistribution", {
      enabled: true,
      priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL,
      minimumProtocolVersion: cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021,
      httpVersion: cloudfront.HttpVersion.HTTP2,
      enableIpv6: true,
      defaultBehavior: {
        origin: new origins.HttpOrigin(redirectBucket.bucketWebsiteDomainName, {
          protocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
        }),
        viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
        allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD,
        cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
        compress: true,
        cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD,
      },
    });
  }
}

結論

「『静的ウェブサイトホスティングのS3をオリジンとするCloudFront』をCDKでデプロイする際には、protocolPolicyの値をhttp-onlyと明示する必要がある」ということでした。

今回のエラーの特定になぜ時間がかかったのか?

今回のエラーですが、業務で遭遇したものでした。

CDKでデプロイしてもなぜか504エラーで、マネジメントコンソールから手作業で設定すると上手くいくため、頭を抱えていました。

また、以下のような挙動によって、本腰を入れないと原因特定が出来ませんでした。

  • CDKではデフォルトでOriginProtocolPolicyがhttps-onlyとなる。
  • マネジメントコンソール上では、おそらく静的ウェブサイトホスティングのS3をターゲットとすると、自動的にOriginProtocolPolicyがhttp-onlyとなる。
  • マネジメントコンソールでは、OriginProtocolPolicyを設定する場所が無い。(どうやら、古いバージョンのマネジメントコンソールでは設定できたらしい)

個人的な理由として、業務中に中々このような挙動を詳細に調査することができなかった、という理由もあります。

業務中は次のタスクもあるため、細かい挙動を調査する時間を渋ってしまう傾向に私はあるみたいです。

結局、落ち着いて、筋道を立てて調査すれば、2時間足らずで原因が特定できたので、急がば回れの気持ちで調査するようにしたいですね。

また、マネジメントコンソールでは良しなに設定してくれるけど、CDKだと明示しないといけない設定値があるということも、肝に銘じてやっていこうと思います。

今回のソースです。
https://github.com/engineer-taro/cdk-cf-s3-inspection

Discussion