🛡️

AWS CDKでWAF(WebACL)を定義する

2024/04/17に公開

概要

今回はAWS CDKAWS WAFを設定する方法をまとめます。

AWS WAFとは

AWS WAFはウェブリクエストの許可・ブロック・監視(カウント)を行うための機能です。
主にCloudFrontやALB、API Gatewayなどのサービスに設定して使用されることが多いです。
https://aws.amazon.com/jp/waf/

ルールについて

AWS WAFでは「特定のIPからのアクセスをブロックする」や「特定の地域からのアクセスをブロックする」といった通信を許可する・しないを定めたルールを組み合わせて使用します。
これはサードパーティが提供しているものもあれば、自作することも可能です。
また、AWSがプリセットとして提供してくれているマネージドルールというものもあります。
今回はマネージドルールを利用します。

使用するルール

今回は5つのマネージドルールを使用します。
ベースラインルールグループからAWSManagedRulesCommonRuleSetAWSManagedRulesAdminProtectionRuleSetAWSManagedRulesKnownBadInputsRuleSetを。
https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/aws-managed-rule-groups-baseline.html

IP評価ルールグループからAWSManagedRulesAmazonIpReputationListAWSManagedRulesAnonymousIpListを使用します。
https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/aws-managed-rule-groups-ip-rep.html

CDK

実際のコードは以下のようになります。
マネージドルールを使用する場合、その名前を設定する方式です。
overrideActionというプロパティを用いることで、挙動をオーバライドすることが可能です。

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as wafv2 from 'aws-cdk-lib/aws-wafv2';

export class SampleStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const webAcl = new wafv2.CfnWebACL(this, 'SampleWebAcl', {
      // デフォルトではすべてallowにする
      defaultAction: {
        allow: {}
      },
      // CloudFrontを対象とする場合は'CLOUDFRONT'それ以外は'REGIONAL'
      scope: 'REGIONAL',
      // メトリクス等の設定
      visibilityConfig: {
        cloudWatchMetricsEnabled: true,
        metricName: 'SampleMetric',
        sampledRequestsEnabled: true,
      },
      // WAFルール設定
      rules: [
        {
          name: "AWSManagedRulesCommonRuleSet",
          priority: 1,
          statement: {
            managedRuleGroupStatement: {
              vendorName: "AWS",
              name: "AWSManagedRulesCommonRuleSet",
            },
          },
          // アクションの上書き設定
          // overrideAction: { none: {} }とした場合は何も上書きしない=ルールの設定に従う
          // overrideAction: { count: {} }とした場合はブロックせずに集計だけする
          overrideAction: { none: {} },
          visibilityConfig: {
            cloudWatchMetricsEnabled: true,
            sampledRequestsEnabled: true,
            metricName: "AWSManagedRulesCommonRuleSet",
          },
        },
        {
          name: "AWSManagedRulesAdminProtectionRuleSet",
          priority: 2,
          statement: {
            managedRuleGroupStatement: {
              vendorName: "AWS",
              name: "AWSManagedRulesAdminProtectionRuleSet",
            },
          },
          overrideAction: { none: {} },
          visibilityConfig: {
            cloudWatchMetricsEnabled: true,
            sampledRequestsEnabled: true,
            metricName: "AWSManagedRulesAdminProtectionRuleSet",
          },
        },
        {
          name: "AWSManagedRulesKnownBadInputsRuleSet",
          priority: 3,
          statement: {
            managedRuleGroupStatement: {
              vendorName: "AWS",
              name: "AWSManagedRulesKnownBadInputsRuleSet",
            },
          },
          overrideAction: { none: {} },
          visibilityConfig: {
            cloudWatchMetricsEnabled: true,
            sampledRequestsEnabled: true,
            metricName: "AWSManagedRulesKnownBadInputsRuleSet",
          },
        },
        {
          name: "AWSManagedRulesAmazonIpReputationList",
          priority: 4,
          statement: {
            managedRuleGroupStatement: {
              vendorName: "AWS",
              name: "AWSManagedRulesAmazonIpReputationList",
            },
          },
          overrideAction: { none: {} },
          visibilityConfig: {
            cloudWatchMetricsEnabled: true,
            sampledRequestsEnabled: true,
            metricName: "AWSManagedRulesAmazonIpReputationList",
          },
        },
        {
          name: "AWSManagedRulesAnonymousIpList",
          priority: 5,
          statement: {
            managedRuleGroupStatement: {
              vendorName: "AWS",
              name: "AWSManagedRulesAnonymousIpList",
            },
          },
          overrideAction: { none: {} },
          visibilityConfig: {
            cloudWatchMetricsEnabled: true,
            sampledRequestsEnabled: true,
            metricName: "AWSManagedRulesAnonymousIpList",
          },
        },
      ],
    });

    // リソースとの紐付け
    new wafv2.CfnWebACLAssociation(this, 'WafAssociation', {
      resourceArn: "【ALBなどのリソースのARN】",
      webAclArn: webAcl.attrArn
    });
  }
}

まとめ

ここまでの内容でWAFの設定自体はできると思います。
今回使ったのはマネージドルールですが、それをいきなり適用すると通常のリクエストにも関わらず思わぬ箇所がブロックされることがあるようです。
https://mazyu36.hatenablog.com/entry/2023/02/21/191736

なので最初はCountモードで設定して、各種リクエストがルールに抵触していないかを監視しながら運用をスタートさせるのがおすすめです。

Discussion