AWS CLIで Web サイトを構築、管理、運用する(12日目)

12日目！
ついに折り返し地点が見えてきました！

11日目の予告通り、今回は、 AWS WAF で日本からのアクセスのみを認めるようにします。

# 12日目の要約
日本からのアクセスだけできるようにするよ！

# AWS CLI の準備
このあたりをみて、好きなバージョンとお使いのOSにあった環境設定をしてくださいね。
なんなら、 AWS CloudShell で実行するのも楽でよいと思います。
この記事シリーズは、AWS CloudShell で実行し、実行例を載せています。

バージョン１
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv1.html

バージョン２
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html

# 概要
AWS WAF のルールを使って日本からのアクセスだけをできるようにするよ！

# さあ、やってみよう！
## ルールグループを作成する
WebACLに直接ルールをインラインポリシーのようにハードコーディングしても良いですが、ルールグループを作って、複数のWebACLで再利用できるようにしてみます。

wafv2 create-rule-group コマンドを実行します。
今回は、JSONを事前に作成せず、一発でやってみます。

```sh
aws wafv2 create-rule-group --name GeoRestriction \
--scope=CLOUDFRONT --capacity 1 \
--visibility-config "SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=GeoRestriction"  \
--region us-east-1  \
--rules "Name=GeoRestriction,Priority=0,Statement={NotStatement={Statement={GeoMatchStatement={CountryCodes=[JP]}}}},Action={Block={CustomResponse={ResponseCode=403}}},VisibilityConfig={SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=GeoRestriction}"
```

ルールグループの作成に成功すると、以下のように サマリが json で返ってきます。

```json
{
    "Summary": {
        "Name": "GeoRestriction",
        "Id": "********-****-****-****-************",
        "Description": "",
        "LockToken": "********-****-****-****-************",
        "ARN": "arn:aws:wafv2:us-east-1:************:global/rulegroup/GeoRestriction/********-****-****-****-************"
    }
}
```

## WebACL に関連づける
ルールグループが作成できたら、 WebACL を更新してルールを関連づけます。
WebACLの更新にあたって、 wafv2 get-web-acl コマンドで必要な情報を取得します。

```sh
aws wafv2 get-web-acl --name waf --scope=CLOUDFRONT --region us-east-1 \
--id ********-****-****-****-************ >webacl.json
```

出力した webacl.json の ```"Rules": [``` の下にルールグループの情報を編集、追記します。

```json:webacl.json
        "Rules": [ //<--この行はいじらない。ここから下を追記。
            {
                "Name": "GeoRestriction",
                "Priority": <既存のPriorityの値の最大値に+1>,
                "Statement": {
                    "RuleGroupReferenceStatement": {
                        "ARN": "<ルールグループのARN>"
                    }
                },
                "OverrideAction": {
                    "None": {}
                },
                "VisibilityConfig": {
                    "SampledRequestsEnabled": true,
                    "CloudWatchMetricsEnabled": true,
                    "MetricName": "GeoRestriction"
                }
            },
```

WebACL を更新するコマンド実行に必要な LockToken の値を確認します。

```sh
cat webacl.json | grep LockToken
```

以下のように表示されます。

```json
    "LockToken": "********-****-****-****-************"
```

そして、 WebACL を更新する際に実行する wafv2 update-web-acl コマンドの rules オプション用に webacl.json ファイルから必要な情報を抽出します。

```sh
cat webacl.json | jq .WebACL.Rules > webacl_rule.json
```

そして、以下のように wafv2 update-web-acl コマンドを実行します。

```sh
aws wafv2 update-web-acl --name waf --id <WebACL のID> \
--scope=CLOUDFRONT --region us-east-1 --rules file://webacl_rule.json  \
--lock-token <確認した LockToken の値>  \
--visibility-config "SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=waf" \
--default-action Allow={}
```

コマンド実行に成功すると、以下のように json が返ります。

```json
{
    "NextLockToken": "********-****-****-****-************"
}
```

# 動作確認
※10日目で設定したAWS WAF のルールの内、Anonymous IP list が有効だと　CloudShell からのアクセスが HTTP 403 で防がれてしまうので、解除しています。
※11日目で設定した、 CloudFront の地理的制限も解除しての確認です。

## 日本からのアクセス
東京リージョン(ap-northeast-1)の CloudShell からいつものように curl を実行します。

```sh
curl https://<ドメイン名>
```

日本からのアクセスなので問題なくコンテンツが取得、出力されます。

```HTML
<!DOCTYPE html>
<html lang="ja">
<head>
  <title>Advent calendar 2021</title>
</head>
<body>
  <h1>Hello world!!</h1>
  <h2>Advent calendar 2021 DAY 3</h2>
</body>
</html>
```

## 米国からのアクセス(日本以外からのアクセス)
北部バージニアリージョン(us-east-1)の CloudShell から同じように curl を実行します。

```sh
curl https://<ドメイン名>
```

なんと、何も表示されません！

```HTML
```

念の為、 VPN やプロキシを使ってみたり、米国リージョンの EC2 で Web ブラウザなどで確認してみます。

やはり、なにも表示されません！

これは、カスタムレスポンスを設定していないがため、何も表示されないようです。
さらに、念の為、HTTPステータスコードの値を確認してみます。

```sh
curl -v https://<ドメイン名>
```

以下のようにHTTP 403が返ってきていることがわかりました。
ルールグループを作成する際、ResponseCodeのパラメータに403の値を指定していたので、期待通りの値です。

```sh
(省略)
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 403 
< server: CloudFront
(省略)
```

ちなみに、昨日の時点で確認漏れていましたが、CloudFront で地理的制限を加えて、エラーページ設定をしていないと、以下のように応答が返ってきます。

```HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
The Amazon CloudFront distribution is configured to block access from your country.
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
<BR clear="all">
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: *****************************
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>
```

出してあげる方が親切なのか、はたまた、出さない方が余計な情報を与えないで済むので良いのか。


# まとめ
今回は、 AWS WAF での 地理的制限を設定する方法をお伝えしました。
AWS WAF と CloudFront でブロック時の標準の動作の差異に、そのサービスの性格がでているような気がしました。

* 今回使ったコマンド
 * wafv2 create-rule-group 
 * wafv2 get-web-acl
 * wafv2 update-web-acl

# 予告
数日以内の記事で、セキュリティグループを取り扱います。
ルールの数が少ないと困るので、事前に制限緩和申請を行い、セキュリティグループあたりのルール数を150とか２００に増しておいてください。