ENGINEERING ARTICLE
AWS WAFでEC2のメンテナンスページへリダイレクトする方法|ループの回避も解説
この記事は約8分で読めます
こんにちは、 @Manabu です。
AWS WAFでメンテナンス画面を表示する場合は、カスタムレスポンスへHTMLを設定する方法があります。
ただ、インフラとフロントエンドの担当者が異なる場合などに、フロント側で作成したメンテナンスページへリダイレクトしてほしいと依頼されることもあります。
このとき、全てのリクエストをメンテナンスページへリダイレクトするだけでは、リダイレクト先へのアクセスも同じWAFルールに一致します。
その結果、同じURLへのリダイレクトが繰り返され、ブラウザでリダイレクトループが発生します。
今回は、AWS WAFのカスタムレスポンスを使い、通常のアクセスをEC2上のメンテナンスページへリダイレクトしながら、ループを回避する方法を紹介します。
この記事は、以下のような方におすすめです。
こんな方におすすめ
- WAFを使ってサイトをメンテナンス状態に切り替えたい
- EC2に配置したメンテナンスページを表示したい
- WAFのリダイレクトループを解消したい
今回の構成
今回想定する構成は、次のとおりです。
ブラウザ
↓
CloudFront + AWS WAF
↓
ALBまたはEC2
↓
/maintenance.html
通常のページへアクセスされた場合は、WAFが302 FoundとLocationヘッダーを返します。
GET /
↓
302 Found
Location: /maintenance.html
↓
GET /maintenance.html
↓
WAFを通過してEC2のページを表示
WAFがEC2上のHTMLを読み込んで返すわけではありません。
最初のリクエストに対してWAFがリダイレクトを返し、ブラウザが/maintenance.htmlへ再度アクセスします。その再リクエストだけをオリジンへ通す仕組みです。
リダイレクトループが発生する設定
例えば、メンテナンス中に全てのリクエストを次のURLへリダイレクトしたとします。
https://example.com/maintenance.html
条件にURIパスの除外がない場合、次のような流れになります。
/
→ /maintenance.htmlへリダイレクト
/maintenance.html
→ /maintenance.htmlへリダイレクト
/maintenance.html
→ /maintenance.htmlへリダイレクト
ブラウザには、次のようなエラーが表示されます。
ERR_TOO_MANY_REDIRECTS
リダイレクト先も同じルールの対象になっていることが原因です。
ループを回避するルール
リダイレクトループを回避するには、/maintenance.html以外のリクエストだけをリダイレクト対象にします。
ルールの考え方は次のとおりです。
ホスト名が example.com
AND
URIパスが /maintenance.html ではない
→ 302で /maintenance.html へリダイレクト
WAFのルールでは、次の条件を組み合わせます。
| 設定 | 内容 |
|---|---|
| Statement | AND statement |
| 条件1 | Hostがexample.comと一致する |
| 条件2 | URI pathが/maintenance.htmlと一致しない |
| Action | Block |
| Custom response code | 302 |
| Response header | Location: /maintenance.html |
WAFでは、Blockアクションのカスタムレスポンスへ3xxステータスコードとLocationヘッダーを設定することで、リダイレクトを返せます。
/maintenance.htmlはルールに一致しないため、Web ACLの後続ルールで問題がなければCloudFrontからオリジンへ転送されます。
Web ACLのデフォルトアクション
この構成は、Web ACLのデフォルトアクションがAllowであることを想定しています。デフォルトアクションがBlockの場合は、メンテナンスページを通すための許可ルールも必要です。
AWSマネジメントコンソールで設定する
ルールを追加する
対象のWeb ACLを開き、「Rules」から独自ルールを追加します。
Rule builderでは、複数の条件を設定するためAND statementを選択します。
対象のホスト名を指定する
複数のサイトで同じWeb ACLを使用している場合は、メンテナンス対象のホスト名を指定します。
Inspect: Single header
Header field name: host
Match type: Exactly matches string
String to match: example.com
Text transformation: Lowercase
1つのサイトだけに関連付けているWeb ACLであっても、対象を明確にするためホスト名を条件に含めておくと安全です。
メンテナンスページを除外する
次に、URIパスが/maintenance.htmlと一致する条件を作成し、結果を反転させます。
Inspect: URI path
Match type: Exactly matches string
String to match: /maintenance.html
Text transformation: None
Negate statement result: 有効
これで、/maintenance.html以外のURIパスがルールに一致します。
カスタムレスポンスを設定する
ルールのアクションにはBlockを選択し、カスタムレスポンスを設定します。
Response code: 302
Response header name: Location
Response header value: /maintenance.html
一時的なメンテナンスなので、301 Moved Permanentlyではなく302 Foundを使用します。
301はブラウザや検索エンジンに長く保持される可能性があるため、メンテナンス解除後も意図しないリダイレクトが続く原因になります。
CloudFormationで記載する場合
CloudFormationでは、次のようなWAFルールになります。
Rules:
- Name: MaintenanceRedirect
Priority: 10
Statement:
AndStatement:
Statements:
- ByteMatchStatement:
FieldToMatch:
SingleHeader:
Name: host
PositionalConstraint: EXACTLY
SearchString: example.com
TextTransformations:
- Priority: 0
Type: LOWERCASE
- NotStatement:
Statement:
ByteMatchStatement:
FieldToMatch:
UriPath: {}
PositionalConstraint: EXACTLY
SearchString: /maintenance.html
TextTransformations:
- Priority: 0
Type: NONE
Action:
Block:
CustomResponse:
ResponseCode: 302
ResponseHeaders:
- Name: Location
Value: /maintenance.html
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: MaintenanceRedirect
SampledRequestsEnabled: true
このルールはAWS::WAFv2::WebACLのRulesへ追加します。
環境に合わせて、ホスト名とメンテナンスページのパスを変更してください。
CSSや画像を使用する場合の注意点
メンテナンスページから外部ファイルを読み込む場合は、そのファイルへのリクエストもWAFを通ります。
例えば、次のファイルを使用するとします。
/maintenance.html
/maintenance-assets/style.css
/maintenance-assets/logo.png
/maintenance.htmlだけを除外すると、CSSや画像はメンテナンスページへリダイレクトされます。そのため、デザインが崩れたり画像が表示されなかったりします。
対応方法は次の2つです。
HTML内にCSSを記載する
メンテナンスページを1つのHTMLで完結させます。
<style>
/* メンテナンスページ用のCSS */
</style>
画像を使用しない簡単なページであれば、この方法が最も設定を減らせます。
アセット用パスも除外する
CSSや画像を分ける場合は、/maintenance-assets/から始まるURIパスもリダイレクト対象から除外します。
URI pathが /maintenance.html と一致しない
AND
URI pathが /maintenance-assets/ から始まらない
個別のファイルを1つずつ許可するより、メンテナンス専用ディレクトリにまとめると管理しやすくなります。
作業者だけ通常ページへアクセスさせる
メンテナンス中も開発者や確認担当者だけ通常ページへアクセスさせたい場合は、IP setを利用できます。
リダイレクト条件を次のようにします。
ホスト名が example.com
AND
URIパスが /maintenance.html ではない
AND
送信元IPが作業者用IP setに含まれていない
作業者のIPアドレスはリダイレクトルールに一致しないため、通常どおりオリジンへアクセスできます。
IPアドレスが変わる環境では、切り替え前に現在のグローバルIPアドレスを確認してください。
ルールの優先順位に注意する
WAFは、優先順位の数値が小さいルールから評価します。
メンテナンス用ルールより前にBlockアクションのルールがあり、そのルールにリクエストが一致すると、メンテナンスページへリダイレクトされず403などが返されます。
一方で、メンテナンス用ルールを最優先にすると、通常はマネージドルールで遮断されるリクエストにも302を返すことになります。
どちらを優先するかは運用方針によりますが、次のような順番が考えられます。
- 明確に遮断したい攻撃元やリクエスト
- 作業者などの例外条件
- メンテナンス用リダイレクト
- その他のマネージドルール
切り替え前に、既存ルールとの優先順位を必ず確認しましょう。
動作確認
メンテナンスページ以外へアクセスし、302とLocationヘッダーが返ることを確認します。
curl -I https://example.com/
想定する結果は次のとおりです。
HTTP/2 302
location: /maintenance.html
次に、メンテナンスページがリダイレクトされず、オリジンから返されることを確認します。
curl -I https://example.com/maintenance.html
HTTP/2 200
最後に、ブラウザでも次の項目を確認します。
- 通常ページからメンテナンスページへ移動する
- リダイレクトループが発生しない
- CSSや画像が表示される
- 作業者用IPアドレスから通常ページを確認できる
- メンテナンス解除後に通常ページへ戻る
メンテナンスページのステータスコード
WAFからの最初の応答は302ですが、リダイレクト先のメンテナンスページはEC2側の設定によって200になります。
検索エンジンへ一時的な停止であることを明確に伝えたい場合は、オリジン側でメンテナンスページのレスポンスを503 Service Unavailableにし、必要に応じてRetry-Afterヘッダーを付ける方法も検討します。
ただし、ブラウザへ単純にページを表示するだけなら200でも動作します。SEOや監視への影響を含めて、要件に合ったステータスコードを選択してください。
カスタムレスポンスとの使い分け
フロント側で作成したページを表示する必要がなければ、WAFのカスタムレスポンスへHTMLを直接登録する方がシンプルです。
WAF内のHTMLを返す
→ オリジンへアクセスしない
EC2のページへリダイレクトする
→ フロント側で作成したページを利用できる
→ メンテナンスページとアセットの除外が必要
WAFだけでメンテナンス画面を返す方法は、以下の記事で紹介しています。
REFERENCEAWSのWAFでメンテナンス画面を表示!アクセス不可を簡単に対応IT自習室↗
まとめ
AWS WAFでは、Blockアクションのカスタムレスポンスへ302とLocationヘッダーを設定することで、EC2上のメンテナンスページへリダイレクトできます。
ただし、全てのURIを対象にすると、リダイレクト先の/maintenance.htmlも同じルールに一致してループが発生します。
メンテナンスページと必要なCSS・画像のパスをリダイレクト条件から除外することが重要です。
設定後は、通常ページの302、メンテナンスページのレスポンス、アセットの表示、作業者用IPアドレスからの接続まで確認してからメンテナンスを開始しましょう。