S3+Cloudfront+WAFの構成でメンテナンスページを表示する

はじめに

Cloudfront構成の場合に、一時的なメンテナンスページを表示するにはどうすればよいのかを調べて試した備忘録となる。

※今回は一例としてオリジンをS3(静的コンテンツ表示用)とS3(メンテナンスページ用)とする。

環境

AWS
  - S3
  - Cloudfront
  - WAF

準備

以下の準備が完了していること

  • AWSのアカウントが作成されていること

構成図

maintenance.svg

WAFによりアクセスが許可されたもののみ、コンテンツ用のページが配信され、それ以外の場合はメンテナンスページを表示するとしている。

※今回調査するにあたり、

  • CloudFront Functionsを利用して実現する
  • WAFのルールを変更することにより実現する

上記があったが、作業者はコンテンツページを表示させたいということもありルールが簡単に定義できるWAFで実現することにした。

全体の流れ

  • 静的コンテンツ配信用のS3バケットを作成
  • S3をオリジンとしたCloudFrontディストリビューションを作成
  • WAFを作成し、CloudFrontと関連付け
  • WAFの設定変更 (メンテナンスページ用)
  • WAFの設定変更2 (メンテナンス時に特定のIPのみ許可)

作業

まずは、静的コンテンツ配信用のバケットおよびメンテナンス用のバケットおよびを作成する。

S3バケットを作成する

1. AWSコンソールより、「S3」サービスを選択する

create-bucket-01

2. 「バケットを作成する」を選択する

create-bucket-02

3. バケットの設定を行い作成する

create-bucket-03

※後ほど、Cloudfront経由のアクセスのみ許可するという設定にする。
そのため、「パブリックアクセスをブロックする」にはチェックを入れておくこと。

上記で、静的コンテンツ表示用のバケットを作成した。

4. バケットを確認する

s3-static-contents-k が作成されている。
-kがついているのは-kなしでの名前が利用できなかったため。

create-bucket-04

Cloudfrontを作成する

1. AWSコンソールより、「Cloudfront」サービスを選択する

create-cloudfront-01

2. ナビゲーションペインの「ディストリビューション」を選択し、「ディストリビューションを作成」を選択する

create-cloudfront-02

3. 設定を変更し、「ディストリビューション」を作成する

create-cloudfront-03

CloudFrontからのアクセスのみとしたいのでOACを設定する。

create-cloudfront-03-oac

4. ディストリビューションが作成を確認し、バケットポリシーを更新する

「ポリシーをコピー」でコピーしておく。
その後、「S3 バケットの権限に移動してポリシーを更新する」を選択して遷移する。

create-cloudfront-04

5. 「アクセス許可」タブの「バケットポリシー」の「編集」を選択する

setting-bucket-policy-01

6. コピーしたポリシーを入力し「変更の保存」を選択する

setting-bucket-policy-02

7. テスト用のページをアップロードしておく

claudeに作ってもらった↓

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>テストページ</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            line-height: 1.6;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
        h1 {
            color: #333;
            text-align: center;
        }
        .container {
            border: 1px solid #ddd;
            padding: 20px;
            border-radius: 5px;
            background-color: #f9f9f9;
        }
        .test-button {
            background-color: #4CAF50;
            color: white;
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .test-button:hover {
            background-color: #45a049;
        }
        #result {
            margin-top: 20px;
            padding: 10px;
            border: 1px solid #ddd;
            min-height: 50px;
        }
    </style>
</head>
<body>
    <h1>テストページ</h1>
    
    <div class="container">
        <p>これはテスト用のHTMLファイルです。様々な機能をテストするために使用できます。</p>
        
        <h2>基本的なDOM操作テスト</h2>
        <button id="testButton" class="test-button">クリックしてテスト</button>
        <div id="result">結果がここに表示されます</div>
    </div>

    <script>
        // 基本的なJavaScript機能テスト
        document.getElementById('testButton').addEventListener('click', function() {
            const resultElement = document.getElementById('result');
            const now = new Date();
            resultElement.innerHTML = `
                <p>テスト成功! (${now.toLocaleString()})</p>
                <p>ブラウザ: ${navigator.userAgent}</p>
                <p>画面サイズ: ${window.innerWidth} x ${window.innerHeight}</p>
            `;
            resultElement.style.backgroundColor = '#e8f5e9';
        });
    </script>
</body>
</html>

これを保存し、バケットにアップロードしておく。

setting-bucket-policy-03

8. 表示確認

Cloudfrontのディストリビューション名でアクセスできることを確認する。
現状の設定ではindex.htmlまで指定しないと表示されないので注意

setting-bucket-policy-04

ここまでで静的コンテンツ表示用のバケットに対する設定はできた。

WAFの設定

1. AWSコンソールより、「WAF & Shield」サービスを選択する

create-waf-01

2. ナビゲーションペインの「Web ACLs」を選択し、「Create Web ACL」を選択する

create-waf-02

3. Step1でAWSリソースの関連付けを行う

create-waf-03
  • Resource type: Global resources (CloudFront Distributions)
  • Associated AWS resources: 作成したCloudfrontのディストリビューション

を設定しておけばここはOK

4. Step2でルールを設定する

ここでは、まだルールの設定は行わずにデフォルトのままで進める。
実際にメンテナンスページが必要となった場合に変更をして試すことにする。

create-waf-04

5. Step3でルールの優先順位を設定する

ルールはないのでここはスルー

create-waf-05

6. Step4でメトリックの設定

ここもスルー

create-waf-06

7. 設定を確認し作成する

create-waf-07

WAFの設定変更

先ほど作成したWAFについてカスタムレスポンスボディを設定する。

1. 「Web ACLs」から作成した設定を選択する

change-waf-setting-01

2. 「Custom response bodies」タブから「Create custom response body」を選択する

custom-response-body-01

3. メンテナンスページの作成

custom-response-body-02

以下をResponse bodyに入れる。

maintenance.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>メンテナンス中</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            background-color: #f8f9fa;
            padding: 50px;
        }
        .container {
            max-width: 600px;
            margin: 0 auto;
            background: white;
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
        }
        h1 {
            color: #333;
        }
        p {
            color: #666;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>メンテナンス中</h1>
        <p>現在、システムメンテナンスを実施しております。<br>
           ご不便をおかけして申し訳ありません。</p>
        <p>しばらく経ってから再度アクセスしてください。</p>
    </div>
</body>
</html>

WAFの設定変更2

1. 「Web ACLs」から作成した設定を選択する

change-waf-setting-01

2. 「Rules」タブから「Default web ACL action for requests that don’t match any rules」の「Edit」を選択する

change-waf-setting-02

3. 「Block」を選択し「Save」する

以下で設定を行った。

設定項目内容
デフォルトアクションBlock
カスタムレスポンスの有効化Enable
レスポンスコード503
レスポンスボディの指定方法maintenance(作成したカスタムレスポンス)
change-waf-setting-03

4. 確認

CloudFrontのディストリビューション名でアクセスし、メンテナンスページが表示されることを確認

change-waf-setting-04

WAFの設定変更3

今度は自IPの場合はメンテナンスページを表示したくないため、IP Setsを設定する。

1. ナビゲーションペインから「IP Sets」を選択し、「Create IP Set」を選択する

create-ipset-01

2. IP addressesに許可IPを設定する

※はじめはIPv4で指定していたが上手く動作しなかったため、IPv6で作り直した。

create-ipset-02

3. ナビゲーションペインから「Web ACLs」を選択し、作成した設定を選択する

change-waf-setting-01

4. 「Rules」タブから「Add rules」の「Add my own rules and rule groups」を選択する

waf-ipset-01

5. Ruleを設定する

ルールとして先ほど作成したIP setを選択する。

waf-ipset-02 waf-ipset-03

6. 動作確認

自分のIPでアクセスした時は表示されることを確認できる。

waf-ipset-03

これで動作確認はOK

気になった点

この構成だと、以下が問題になるのではないかと思った。
※多分、Terraform, aws-cli, Lambdaなどで自動化する必要がありそう。

  • メンテナンス時にWAFをつけたりかえたりしないといけない ※既存のWAFルールに追加する形だと問題が発生する

参考

おわりに

S3+Cloudfront構成でメンテナンス画面を表示するということをやってみた。
S3のバケットを2つ用意して、S3のカスタムレスポンスでやってみる~だとだめだったので、WAFのカスタムレスポンスでやった。
うまくいったのだが、カスタムレスポンスは4KB以下でないとだめなので、ちょっと凝ったメンテナンスページだと返せなくなるな~と思ったところ。
何かほかにも良い方法がないか調べてみようと思う。

Hugo で構築されています。
テーマ StackJimmy によって設計されています。