AWS WAF のログを他アカウントの S3 に直接出力してみた

AWS WAF ログの出力先の選択肢が増えた先日のアップデートを利用し、他アカウント S3 への AWS WAF ログ出力を試してみました。
2021.12.03

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、大前です。

先日のアップデートで AWS WAF のログ出力先に S3 と CloudWatch Logs も設定できる様になりました。

この機能を利用して AWS WAF のログを他アカウントの S3 バケットに出力してみましたので、備忘録がてらブログに残していきます。

構成

下図の構成を実現します。AWS アカウント A 上に存在する AWS WAF のログを AWS アカウント B の S3 バケットに直接出力します。今回は CloudFront に AWS WAF をアタッチしました。

やってみた

WebACL の作成(AWS アカウント A 側作業)

検証に利用する AWS WAF WebACL の作成から始めます。作成はコンソールで実施しました。

Name は "log-to-s3"、Resource Type は "CloudFront distributions" としました。

ログが出せれば良いのでルールは空のものを作成します。Default Action のみ、Block としました。

他の設定はデフォルトのまま進め WebACL を作成します。作成した WebACL の ARN は後ほど利用するので、「Copy ARN」でコピーしてメモしておきます。

S3 バケットの作成(AWS アカウント B 側作業)

続いて、WebACL を作成したアカウントとは別のアカウントに S3 バケットを作成します。AWS WAF ログの出力先となる S3 バケットは、バケット名が「aws-waf-logs-」で始まる必要があります。

Your bucket names for AWS WAF logging must start with aws-waf-logs- and can end with any suffix you want. For example, aws-waf-logs-testBucket2.

今回は「aws-waf-logs-<アカウントID>-2」という名前のバケットを作成しました。バケット名以外は全てデフォルトで作成します。("-2"がついてるのは色々検証していたため。。)

AWS WAF ログ設定してみる

WebACL、S3 バケットそれぞれの準備が整ったので AWS WAF ログの設定をしてみたいと思います。(CloudFront がない場合は用意しましょう)

軽く調べてみたのですが、現状コンソールでは他アカウントの S3 を AWS WAF のログ出力先として指定できない様で、現状は CLI や CFn 等で実施する必要がありそうです。

CloudFormation には AWS::WAFv2::LoggingConfiguration という AWS WAF のログ設定を行うためのリソースが用意されていますので、今回はこれを利用してログ設定をしていきます。(2021/8 ごろに追加されていた模様です)

用意した CloudFormation テンプレートは以下の通りです。ログを設定したい AWS WAF WebACL の ARN と、出力先となる S3 バケットの ARN を指定するだけのシンプルなものになっています。LoggingFilter 等の設定も可能ですので、今回利用していないオプションが必要であれば上記のドキュメントを参照ください。

AWSTemplateFormatVersion: "2010-09-09"
Description: "Setup AWS WAF Log(Global) export to S3"
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
  S3BucketArn:
    Type: String
    Description: "Arn of S3 Bucket"
  WebACLArn:
    Type: String
    Description: "Arn of WebACL"

# ------------------------------------------------------------#
# Resources
# ------------------------------------------------------------#
Resources:
  WAFLogConfig:
    Type: "AWS::WAFv2::LoggingConfiguration"
    Properties:
      LogDestinationConfigs:
        - !Ref S3BucketArn
      ResourceArn: !Ref WebACLArn

クロスアカウントで S3 へのログ出力する場合は事前にバケットポリシー設定が必要

公式ドキュメントには以下記載があり、WebACL と S3 が同じアカウントであればログ設定時に S3 バケットポリシーをよしなに入れてくれます。

If the user creating the log owns the bucket, the service automatically attaches the following policy to the bucket to give the log permission to publish logs to it:

ログを作成したユーザーがバケットを所有している場合、サービスは自動的に以下のポリシーをバケットに付加し、ログにログを公開する権限を与えます。(DeepL 翻訳)

一方で、アカウントが異なる場合は事前に手動でポリシーを入れる必要がある旨が記載されています。(当たり前と言えば当たり前ですが)

If the user creating the log doesn't own the bucket, or doesn't have the GetBucketPolicy and PutBucketPolicy permissions for the bucket, the log creation fails.

ログを作成するユーザーがそのバケットを所有していない場合や、そのバケットに対して GetBucketPolicy や PutBucketPolicy の権限を持っていない場合、ログの作成は失敗します。(DeepL 翻訳)

今回はクロスアカウントに該当するため、上記ドキュメント内のサンプルポリシー等を参考に、先ほど作成した S3 バケットに以下バケットポリシーを追加しました。WebACL のリージョンによって微妙にポリシーが変わる可能性もありますので、あくまで参考程度に留めてください。

{
    "Version": "2012-10-17",
    "Id": "AWSLogDeliveryWrite20150319",
    "Statement": [
        {
            "Sid": "AWSLogDeliveryWrite",
            "Effect": "Allow",
            "Principal": {
                "Service": "delivery.logs.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::<作成した S3 バケット名>/AWSLogs/<AWS WAF が存在するアカウント ID>/*",
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": "<AWS WAF が存在するアカウント ID>",
                    "s3:x-amz-acl": "bucket-owner-full-control"
                },
                "ArnLike": {
                    "aws:SourceArn": "arn:aws:logs:us-east-1:<AWS WAF が存在するアカウント ID>:*"
                }
            }
        },
        {
            "Sid": "AWSLogDeliveryAclCheck",
            "Effect": "Allow",
            "Principal": {
                "Service": "delivery.logs.amazonaws.com"
            },
            "Action": "s3:GetBucketAcl",
            "Resource": "arn:aws:s3:::<作成した S3 バケット名>",
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": "<AWS WAF が存在するアカウント ID>"
                },
                "ArnLike": {
                    "aws:SourceArn": "arn:aws:logs:us-east-1:<AWS WAF が存在するアカウント ID>:*"
                }
            }
        }
    ]
}

ちなみに、バケットポリシーを追加しない状態で CloudFormation スタック作成を実行すると以下の様にエラーが発生します。参考までに。

CloudFormation スタックを実行してログ設定を入れる

一通りの事前準備ができたので、上記 CloudFormation テンプレートを実行してログ設定を入れてみます。

今回 CloudFront 向け WebACL を利用しているからだと思いますが、CloudFormation のスタック作成はバージニア北部で実行する必要がありました。

スタックの名前は適当に入力し、S3 と WebACL の ARN をそれぞれ入力し、実行します。

無事スタック作成が完了しました。

該当の WebACL のログ設定を確認すると意図した S3 バケットが指定されていることが確認できます。

CloudFront に適当にアクセスし、S3 にログが出力されることも確認できました。

おわりに

アップデート後にコンソールを眺めていたところ、コンソール上では他アカウントの S3 に AWS WAF ログを出力する方法がわからなかったのが背景でした。

実際に試してみて、無事に他アカウントの S3 にもログが出力できてよかったです。

今までの Kinesis Firehose を利用する方法と比較して非常に設定しやすく、嬉しいアップデートであることが実感できました。

マルチアカウント環境における AWS WAF ログの集約にも役立ちそうです。

以上、AWS 事業本部の大前でした。

参考