前提
Cloudfront – ALB – EC2 構成で、Cloudfrontの前段にWAFを設定し、特定のパスに対してIP制限とBasic認
証を同時に設定します。
また、本記事では下記要件を想定しています。
- インターネット向けにフルオープンしているWebページがある。
- ⼀部のパスに公開前のコンテンツを配置し確認したい。
- 「⼀部のパス」は本記事では「/test01/test02」配下全てのコンテンツとしています。
- そのため指定したパスに対して IP制限 および Basic認証を設定する。
IP制限、Basic認証はApache等の設定でも実装可能ですが、本記事では下記を考慮しWAFでの実装とします。
- AWS のセキュリティベストプラクティスに記載のある「全レイヤーでセキュリティを適⽤する」に則り、WAFによるセキュリティをCloudfrontに適⽤します。
- Webページをホストしているサーバーにトラフィックが届かないため、サーバーの負荷軽減が期待できます。
構成図

やってみる
1. IPセットの作成
許可するIPセットを作成します。
[WAF & Shield]→[IP sets]→[Create IP set]から必要な項⽬を⼊⼒し作成します。
| 項⽬ | 概要 |
|---|---|
| IP set name | リソース名(任意) |
| Description | 説明(任意) |
| Region | どのリージョンに作成するか |
| IP version | IPv4 or IPv6 |
| 項⽬ | 概要 |
|---|---|
| IP addresses | 許可する(制限する)IPを記載 |

2. Web ACLs の作成
Cloudfront に設定する ACL を設定します。
[WAF & Shield]→[Web ACLs]→[Create web ACL]から必要な項⽬を⼊⼒し作成します。
2-1. Describe web ACL and associate it to AWS resources
| 項⽬ | 概要 |
|---|---|
| Name | リソース名(任意) |
| 項⽬ | 概要 |
|---|---|
| Description | 説明(任意) |
| CloudWatch metric name | CloudWatch メトリクス名(デフォルトでは Name と同じ値が⼊ります) |
| Resource type | 関連付けるリソースのタイプ |
| Body size limit | リクエストサイズ制限 |
| Associated AWS resources | 関連付けるリソース |


2-2. Add rules and rule groups
| 項⽬ | 概要 |
|---|---|
| Rules | ACLのルール設定 |
| 項⽬ | 概要 |
|---|---|
| Default web ACL action for requests that don’t match any rules | デフォルトアクション(Allow / Block) |
| Token domain list | トークンドメインのリスト |

2-3. Rulesの設定(IP制限, Basic認証)
ここで設定する Rules でIP制限とBasic認証を実装します。
[Rules]→[Add rules]→[Add my own rules and rule groups]からカスタムルールを作成していきます。

IP制限ルールの作成
下記設定でルールを作成します。
ここで作成するルールは「許可IPセットで指定したIP以外が指定のパスにアクセスした場合ブロックする」ルールです。
| 項⽬ | 設定値 | 設定値概要 |
|---|---|---|
| Rule type | Rule builder | ユーザーカスタムルールを作成する |
| Name | allow_address_rule | ルール名(任意) |
| Type | Regular rule | ⼀般的なルール |
| If a request | matches all the statement (AND) | リクエストのマッチ条件(AND) |

▼ Statement 1
| 項⽬ | 設定値 | 設定値概要 |
|---|---|---|
| Negate statement (NOT) | OFF(チェックしない) | 以下の条件をNOT条件にするかどうか |
| Inspect | URI Path | 制限を適⽤するパスの指定 |
| Match type | Starts with string | パスの始まりが String to match で⼀致するもの全て |
| String to match | /test01/test02/ | 適⽤するパスの指定 |
| Text transformation | None | リクエストを⽂字列変換するかどうか |

▼ Statement 2
| 項⽬ | 設定値 | 設定値概要 |
|---|---|---|
| Negate statement (NOT) | ON(チェックする) | 以下の条件をNOT条件にするかどうか |
| Inspect | Originates from an IP address in | IPセットをオリジンとする |
| IP set | asaoka-waf-test-ipset | どのIPセットをオリジンとするか |
| IP address to use as the originating address | Source IP address | 何を発信元IPアドレスとして使⽤するか |

▼ Action
| 項⽬ | 設定値 | 設定値概要 |
|---|---|---|
| Action | Block | マッチした通信をどう処理するか |
| Custom response | 指定しない | – |
| Add label | 指定しない | – |

Basic認証ルールの作成
下記設定でルールを作成します。
ここで作成するルールは「指定のパスにアクセスがあった場合、Basic認証を要求し認証失敗したものはブロックする」ルールです。
| 項⽬ | 設定値 | 設定値概要 |
|---|---|---|
| Rule type | Rule builder | ユーザーカスタムルールを作成する |
| Name | basic_auth_rule | ルール名(任意) |
| Type | Regular rule | ⼀般的なルール |
| If a request | matches all the statement (AND) | リクエストのマッチ条件(AND) |

▼ Statement 1
| 項⽬ | 設定値 | 設定値概要 |
|---|---|---|
| Negate statement (NOT) | OFF(チェックしない) | 以下の条件をNOT条件にするかどうか |
| Inspect | URI Path | 制限を適⽤するパスの指定 |
| 項⽬ | 設定値 | 設定値概要 |
|---|---|---|
| Match type | Starts with string | パスの始まりが String to match で⼀致するもの全て |
| String to match | /test01/test02/ | 適⽤するパスの指定 |
| Text transformation | None | リクエストを変換するかどうか |

▼Statement 2
| 項⽬ | 設定値 | 設定値概要 |
|---|---|---|
| Negate statement(NOT) | ON(チェックする) | 以下の条件をNOT条件にするかどうか |
| Inspect | Single header | 単⼀のヘッダーを検査させます |
| Header field name | authorization | ヘッダーキーを指定します |
| Match type | Exactly matches string | 完全⼀致 |
| String to match | Basic dGVzdC11ZXNyOnI1WUFMYmtY | リクエストで検索する⽂字列(※1) |
| 項⽬ | 設定値 | 設定値概要 |
|---|---|---|
| Text transformation | None | リクエストを⽂字列変換するかどうか |

(※1)「String to match」に記⼊する値について
こちらに設定する値については、事前にターミナル等で下記を実⾏し base64 エンコードした値を設定し
ます。
echo -n user:password | base64
▼ Action
| 項⽬ | 設定値 | 設定値概要 |
|---|---|---|
| Action | Block | マッチした通信をどう処理するか |
| Custom response | Enable | – |
| Response code | 401 | エラー時のレスポンスコード |
| Key | www-authenticate | – |
| 項⽬ | 設定値 | 設定値概要 |
|---|---|---|
| Value | Basic | – |

2-4. Set rule priority
ルールを適⽤する優先度を指定します。
今回は先にIP制限で設定外のIPからのアクセス遮断を⾏い、許可IPからのアクセスだった場合にBasic認証を要求する流れとします。

2-5. Configure metrics
Amazon Cloudwatch で確認するメトリクスについて設定できます。
今回はデフォルト設定のままとします。

2-6. Review and create web ACL
設定した値を確認し [Create web ACL] を押下します。
こちらでACLの設定は完了です。
3.動作確認
3-1. IPセットで許可しなかったIPからのアクセスした場合
▼ 「/」へのアクセス
制限はかけていないので、正常にページを閲覧できます。

▼ 「/test01/」へのアクセス
「/」へのアクセスと同様に制限はかけていないので、正常にページを閲覧できます。

▼ 「/test01/test02/」へのアクセス
「/test01/test02/」以下の階層に対してはWAFでIP制限をかけているため、403エラーが返ってきます。

▼ 「/test01/test02/test03/」へのアクセス
「/test01/test02/」へのアクセスと同様に「/test01/test02/」以下の階層に対してはWAFでIP制限をかけているため、403エラーが返ってきます。

3-2. IPセットで許可したIPからアクセスした場合
▼ 「/」へのアクセス
制限はかけていないので、正常にページを閲覧できます。

▼ 「/test01/」へのアクセス
「/」へのアクセスと同様に制限はかけていないので、正常にページを閲覧できます。

▼ 「/test01/test02/」へのアクセス
IP制限ルールでは遮断されないため、Basic認証が要求されます。

Basic認証をクリアするとテストページが表⽰されました。

▼ 「/test01/test02/test03/」へのアクセス
「/test01/test02/」へのアクセスと同様に、IP制限ルールでは遮断されないため、Basic認証が要求されます。

Basic認証をクリアするとテストページが表⽰されました。

4.[番外編] つまづいたところ
私が設定時につまづいたところを共有します。
4-1. 指定パスの設定値について
指定パスを設定する際の注意点です。
WAF ruleを作成する際 [Statement] で制限をかけるパスを指定しました。
(本記事では 「2.3 Rulesの設定」の [Statement1] の部分に該当します)
こちらで下記の設定をしてしまっていました。
| 項⽬ | 設定値 |
|---|---|
| Negate statement results | OFF |
| Inspect | URI path |
| Match type | Exactly matches string |
| String to match | /test01/test02/* |

上記設定としてしまった場合、Match type を⽂字列の完全⼀致としていることから、
正規表現が使⽤できず、要件どおりの制限がかからず全てのページが閲覧できる状態となってしまいます。
この場合、本番公開前のコンテンツが誤って公開されてしまっている状態となるため注意が必要です。
▼ 「/test01/test02/」へのアクセス(指定したIPからの接続)

▼ 「/test01/test02/test03/」へのアクセス(指定したIPからの接続)

この場合の制限のかかり⽅は、サーバーのファイル構造を以下のように変更した場合がわかりやすいと思います。
▼ 「/test01/test02/*」へのアクセス(指定したIPからの接続)
String to match で指定した「/test01/test02/*」と⼀致するためBasic認証が要求されます。

▼ 「/test01/test02/*/test04」へのアクセス(指定したIPからの接続)
こちらのパスには制限をかけたかったのですが、Basic認証が要求されずページが表⽰されてしまいました。
このことからこちらの設定では「/test01/test02/*」に制限はかかりますが、それ以下の階層にはルールは適⽤されず、要件通りの制限が実装できていないことがわかります。
