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

ついに10日目！！！！！
正直ここまで続けられるとは思いませんでした。。

というわけで、予告通り、セキュリティ周りいってみましょ。

今後、静的サイトに加えて動的コンテンツを入れていく予定なので 今回は Web Application Firewall (WAF) を導入します。

AWS では、 AWS WAF というサービスが用意されています。なぜか、Cloudfront のコンソールでは Amazon WAF という表記だったりしますが・・・

さて、やっていきましょ！

# 10日目の要約
WAFをつかうよ！

# 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 をCloudFront　と関連づけてセキュリティ向上させるよ！

# さあ、やってみよう！
## AWS WAF の WebACL に関連づけるマネージドルールを json ファイルにまとめよう
AWS WAF のルールは AWS が用意している マネージドルールというものがあります。さらにその中には無償で使える基本パック的なルールが存在します。
今回は、そのルールを3つピックアップして json ファイルで用意しておきます。
対象のルールは以下の通りです。

| 名前 | ルールの管理名| 説明 |
|:----|:------------|:----|
|Anonymous IP list|AWS-AWSManagedRulesAnonymousIpList|このグループには、viewer IDの難読化を許可するサービスからのリクエストをブロックできるルールが含まれています。これには、VPN、プロキシ、Torノード、およびホスティングプロバイダーから発信されたリクエストが含まれる場合があります。これは、アプリケーションからIDを隠そうとしている可能性のあるビューアを除外する場合に役立ちます。|
|Core rule set|AWS-AWSManagedRulesCommonRuleSet|Webアプリケーションに一般的に適用できるルールが含まれています。これにより、OWASPの出版物に記載されている脆弱性を含む、さまざまな脆弱性の悪用に対する保護が提供されます。|
|Known bad inputs|AWS-AWSManagedRulesKnownBadInputsRuleSet|無効であることがわかっており、脆弱性の悪用または発見に関連するリクエストパターンをブロックできるルールが含まれています。これにより、悪意のある攻撃者が脆弱なアプリケーションを発見するリスクを減らすことができます。|
|Linux operating system|AWS-AWSManagedRulesLinuxRuleSet|LFI攻撃など、Linuxに固有の脆弱性の悪用に関連するリクエストパターンをブロックするルールが含まれています。これにより、ファイルの内容を公開したり、攻撃者がアクセスしてはならないコードを実行したりする攻撃を防ぐことができます。|

以下のように、json ファイルを作ります。

```json:waf_rules.json
[
            {
                "Name": "AWS-AWSManagedRulesCommonRuleSet",
                "Priority": 0,
                "Statement": {
                    "ManagedRuleGroupStatement": {
                        "VendorName": "AWS",
                        "Name": "AWSManagedRulesCommonRuleSet"
                    }
                },
                "OverrideAction": {
                    "None": {}
                },
                "VisibilityConfig": {
                    "SampledRequestsEnabled": true,
                    "CloudWatchMetricsEnabled": true,
                    "MetricName": "AWS-AWSManagedRulesCommonRuleSet"
                }
            },
            {
                "Name": "AWS-AWSManagedRulesLinuxRuleSet",
                "Priority": 1,
                "Statement": {
                    "ManagedRuleGroupStatement": {
                        "VendorName": "AWS",
                        "Name": "AWSManagedRulesLinuxRuleSet"
                    }
                },
                "OverrideAction": {
                    "None": {}
                },
                "VisibilityConfig": {
                    "SampledRequestsEnabled": true,
                    "CloudWatchMetricsEnabled": true,
                    "MetricName": "AWS-AWSManagedRulesLinuxRuleSet"
                }
            },
            {
                "Name": "AWS-AWSManagedRulesKnownBadInputsRuleSet",
                "Priority": 1,
                "Statement": {
                    "ManagedRuleGroupStatement": {
                        "VendorName": "AWS",
                        "Name": "AWSManagedRulesKnownBadInputsRuleSet"
                    }
                },
                "OverrideAction": {
                    "None": {}
                },
                "VisibilityConfig": {
                    "SampledRequestsEnabled": true,
                    "CloudWatchMetricsEnabled": true,
                    "MetricName": "AWS-AWSManagedRulesKnownBadInputsRuleSet”
                }
            },
            {
                "Name": "AWS-AWSManagedRulesAnonymousIpList",
                "Priority": 1,
                "Statement": {
                    "ManagedRuleGroupStatement": {
                        "VendorName": "AWS",
                        "Name": "AWSManagedRulesAnonymousIpList"
                    }
                },
                "OverrideAction": {
                    "None": {}
                },
                "VisibilityConfig": {
                    "SampledRequestsEnabled": true,
                    "CloudWatchMetricsEnabled": true,
                    "MetricName": "AWS-AWSManagedRulesAnonymousIpList”
                }
            }
        ]
```

ファイルが作成できたら、以下のように wafv2 create-webacl コマンドを実行して WebACL を作成します。

```sh
aws wafv2 create-web-acl --name waf --scope=CLOUDFRONT \
--region us-east-1 --default-action "Allow={}"  \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=waf  \
--rules file://waf_rules.json
```

正常に実行できたら、以下のようなレスポンスが返ります。

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

これで、ルールが紐づいている WebACL が作成できました。
上記の ARN の値を用いて、 CloudFront の設定をここ数日と同様に変更します。

## CloudFront の設定変更を準備する
やはり、基本的には、6日目で実施したものと似た内容です。

cloudfront get-distribution-config コマンドで現状の設定を取得する
ETag の値を確認する
取得した json ファイルに対して、設定変更を加える
cloudfront update-distribution コマンドを実行して更新する
この記事では、3と4についてのみ記載します。１と２の実施内容については、6日目の記事をご確認ください。

### 取得した json ファイルに対して設定変更を加える
WebACL を設定する箇所については、以下のようになっていると思います。

```json
"WebACLId": "",
```

この WebACLId に対して、作成した WebACL の ARN を指定します。

```json
"WebACLId": "arn:aws:wafv2:us-east-1:************:global/webacl/waf/********-***-****-****-************"
```

## cloudfront update-distribution コマンドを実行して更新する
準備ができたら、6日目と同様に cloudfront update-distribution コマンドを実行して、設定変更を行います。

```sh
aws cloudfront update-distribution --id <CloudFront Distribution ID> ¥
--cli-input-json file://distribution_webacl.json ¥
--if-match 確認したETag の値
```

コマンド実行に成功すると、Distribution の内容が記された json が返されます。
やはり長いので割愛します。

# 動作確認
いつもどおり CloudShell から curl コマンドを実行します。

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

するとどうでしょう。HTTP403が返ってきます！

```HTML
<!DOCTYPE html>
<html lang="ja">
<meta charset="UTF-8">
<head>
  <title>えらーです m9('ω')</title>
</head>
<body>
  <h1>残念〜！</h1>
  <h2>アクセスできませーん！</h2>
</body>
</html>
```

おそらくですが、CloudShell の機能が「Anonymous IP list」ルールに抵触しているのかとおもいます(個人の感想です)

これだと今後の動作確認に支障が出かねないので、curl 実行時は ローカル環境(Macbook Pro(macOS 12.0.1))から実施します。

では、改めて・・・

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

ローカル側では問題なく 正常に index.html の内容が返ってきました。

```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>
```

ちなみに Core rule set には UserAgent の設定がされていない(NoUserAgent_HEADER)というルールが存在します。なので、以下のように UserAgent が設定されていない状態で curl をローカル側で実行すると弾かれるようになります。

```sh
curl https://ドメイン名/ -A ""
```

```HTML
<!DOCTYPE html>
<html lang="ja">
<meta charset="UTF-8">
<head>
  <title>えらーです m9('ω')</title>
</head>
<body>
  <h1>残念〜！</h1>
  <h2>アクセスできませーん！</h2>
</body>
</html>
```


# まとめ
CloudShell が Anonymous IP list ルールでブロックされるっぽい(?)ことに驚きですが、AWS WAF が動作しているという証左にもなりますね💦💦

ということで、 AWS WAF を導入し、セキュリティ向上につなげられたのではないかと思います。

* 今回使ったコマンド
 *  wafv2 create-web-acl
 * (cloudfront get-distribution-config)
 * cloudfront update-distribution