TerraformでAWS WAFを基礎から学ぶ(ロギング編)

# はじめに
AWS WAF＋Terraform記事第3弾。

基本のルール設定編の続き。
今回は、ロギングの設定ついて確認をしていく。
[第2弾の記事](https://qiita.com/neruneruo/items/47189fb7fdc3efa00a74)は直接は関係ないので、参考程度に見ておいていただければ。

Firehoseの設定が必要なのでやや複雑になる。

# ロギングの設定
ロギングの設定には、```aws_wafv2_web_acl_logging_configuration``` のリソースを使う。

```HCL
resource "aws_wafv2_web_acl_logging_configuration" "iprestriction" {
  log_destination_configs = [aws_kinesis_firehose_delivery_stream.web_acl_log.arn]
  resource_arn            = aws_wafv2_web_acl.iprestriction.arn
}
```

これで、Firehose と Web ACL を繋ぐイメージだ。

で、Firehoseについては以下のように設定をする。
なお、[公式ドキュメント](https://docs.aws.amazon.com/waf/latest/developerguide/logging.html)に書かれている通り、Firehose のストリーム名は ```aws-waf-logs-``` で始まっていないと ```terraform apply``` がエラーになるため、注意が必要だ(固定で設定されるIAMで、上記のストリーム名しか権限を与えていないため)。
Firehose には S3 バケットへのアクセス権限を設定しておこう。

```HCL
################################################################################
# Kinesis Firehose                                                             #
################################################################################
locals {
  firehose_stream_name = "aws-waf-logs-iprestriction"
}

resource "aws_kinesis_firehose_delivery_stream" "web_acl_log" {
  name        = local.firehose_stream_name
  destination = "s3"

  s3_configuration {
    role_arn   = aws_iam_role.firehose.arn
    bucket_arn = aws_s3_bucket.web_acl_log.arn
  }
}

################################################################################
# S3                                                                           #
################################################################################
resource "aws_s3_bucket" "web_acl_log" {
  bucket = local.bucket_name
  acl    = "private"
}

################################################################################
# IAM Role for Kinesis Firehose                                                #
################################################################################
resource "aws_iam_role" "firehose" {
  name               = local.firehose_role_name
  assume_role_policy = data.aws_iam_policy_document.firehose_assume.json
}

data "aws_iam_policy_document" "firehose_assume" {
  statement {
    effect = "Allow"

    actions = [
      "sts:AssumeRole",
    ]

    principals {
      type = "Service"
      identifiers = [
        "firehose.amazonaws.com",
      ]
    }
  }
}

resource "aws_iam_role_policy_attachment" "firehose" {
  role       = aws_iam_role.firehose.name
  policy_arn = aws_iam_policy.firehose_custom.arn
}

resource "aws_iam_policy" "firehose_custom" {
  name   = local.firehose_policy_name
  policy = data.aws_iam_policy_document.firehose_custom.json
}

data "aws_iam_policy_document" "firehose_custom" {
  statement {
    effect = "Allow"

    actions = [
      "s3:AbortMultipartUpload", 
      "s3:GetBucketLocation", 
      "s3:GetObject", 
      "s3:ListBucket", 
      "s3:ListBucketMultipartUploads", 
      "s3:PutObject",
    ]

    resources = [
      "*",
    ]
  }
}
```

この設定を行うと、マネージメントコンソールでロギングの設定が Enabled になる。

![キャプチャ18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/c60d6531-5f0f-733c-1d2e-70caabdd3e01.png)

Edit logging のボタンを押すと、以下のように詳細を確認できる。

![キャプチャ17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/b4079c09-84b1-d507-94e6-41f255b1e68c.png)

これで WAF を設定しているリソースにアクセスすると、以下のようにログが出力される。

```JSON
{
  "timestamp": 1607851908001,
  "formatVersion": 1,
  "webaclId": "arn:aws:wafv2:ap-northeast-1:xxxxxxxxxxxx:regional/webacl/waf-iprestrict-test-webacl/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "terminatingRuleId": "iprestriction",
  "terminatingRuleType": "REGULAR",
  "action": "BLOCK",
  "terminatingRuleMatchDetails": [],
  "httpSourceName": "APIGW",
  "httpSourceId": "xxxxxxxxxxxx:xxxxxxxxxx:prod",
  "ruleGroupList": [],
  "rateBasedRuleList": [],
  "nonTerminatingMatchingRules": [],
  "httpRequest": {
    "clientIp": "xxx.xxx.xxx.xxx",
    "country": "JP",
    "headers": [
      {
        "name": "Accept",
        "value": "*/*"
      },
      {
        "name": "CloudFront-Forwarded-Proto",
        "value": "https"
      },
      {
        "name": "CloudFront-Is-Desktop-Viewer",
        "value": "true"
      },
      {
        "name": "CloudFront-Is-Mobile-Viewer",
        "value": "false"
      },
      {
        "name": "CloudFront-Is-SmartTV-Viewer",
        "value": "false"
      },
      {
        "name": "CloudFront-Is-Tablet-Viewer",
        "value": "false"
      },
      {
        "name": "CloudFront-Viewer-Country",
        "value": "JP"
      },
      {
        "name": "Host",
        "value": "xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com"
      },
      {
        "name": "User-Agent",
        "value": "curl/7.61.1"
      },
      {
        "name": "Via",
        "value": "2.0 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.cloudfront.net (CloudFront)"
      },
      {
        "name": "X-Amz-Cf-Id",
        "value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      },
      {
        "name": "X-Amzn-Trace-Id",
        "value": "Root=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      },
      {
        "name": "X-Forwarded-For",
        "value": "xxx.xxx.xxx.xxx, xxx.xxx.xxx.xxx"
      },
      {
        "name": "X-Forwarded-Port",
        "value": "443"
      },
      {
        "name": "X-Forwarded-Proto",
        "value": "https"
      },
      {
        "name": "Content-Length",
        "value": "0"
      },
      {
        "name": "Connection",
        "value": "Keep-Alive"
      }
    ],
    "uri": "/prod/employee",
    "args": "id=00001",
    "httpVersion": "HTTP/1.1",
    "httpMethod": "GET",
    "requestId": "xxxxxxxxxxxxxxxx"
  }
}
```

# Redacted fields って何だ？
↑のキャプチャに Redacted fields という項目がある。
これは、ログにセンシティブな情報を記録しないようにするための設定だ。

設定した項目について、ログ上でマスクされるようになる。

たとえば、User Agent について表示したくない場合、```aws_wafv2_web_acl_logging_configuration``` のリソースを以下のように設定すれば良い。

```HCL
resource "aws_wafv2_web_acl_logging_configuration" "iprestriction" {
  log_destination_configs = [aws_kinesis_firehose_delivery_stream.web_acl_log.arn]
  resource_arn            = aws_wafv2_web_acl.iprestriction.arn

  redacted_fields {
    single_header {
      name = "user-agent"
    }
  }
}
```

こうすることで、ログ中の該当のヘッダ項目が

```JSON
      {
        "name": "User-Agent",
        "value": "REDACTED"
      },
```

といった具合にマスクされる。上記の通り、この設定についてはケースインセンシティブのようだ。

ちなみに、複数設定する場合は、

```HCL
resource "aws_wafv2_web_acl_logging_configuration" "iprestriction" {
  log_destination_configs = [aws_kinesis_firehose_delivery_stream.web_acl_log.arn]
  resource_arn            = aws_wafv2_web_acl.iprestriction.arn

  redacted_fields {
    query_string {}
  }

  redacted_fields {
    single_header {
      name = "user-agent"
    }
  }
}
```

と、```redacted_fields``` のブロックを書き並べれば良い。
ただし、**Terraformのバグで ```single_header``` 以外は設定されない(無視される)**。
残念ながら、マネージメントコンソールなりCLIから設定する必要がありそうだ……。早くバグフィックスされることを切に願う……。

ともあれ、これで一通りの Web ACL の設定はできるようになった！
あとはルールの設定に慣れるだけだ！