🛡️
[AWS CDK] WAFのログをS3 or CloudWatchに直接出力する構成のCDKコード
[アップデート] AWS WAFのログを直接CloudWatch LogsおよびS3に出力可能になりました | DevelopersIO
こちらをCDKで構築する日本語情報があまり見当たらなかったので記事にしてみます。
注意点として、ログ出力先のS3とCloudWatchのリソース名にはPrefixとして aws-waf-logs-
を必ず付けないといけないので押さえておきましょう(自分は当初、これを認識しておらずやや時間を使ってしまいました)。
環境
- aws-cdk-lib 2.30.0
- constructs 10.1.43
APIGW+WebACLを定義
まずはAPIGWのログをWAF WebACLと紐付けるCDKコードを準備しました。
import { Construct } from "constructs"
import * as cdk from "aws-cdk-lib"
export class WafStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props)
// APIGW+Lambda
const restApi = new cdk.aws_apigateway.RestApi(this, "restApi", {
restApiName: "restApi",
deployOptions: {
stageName: "dev",
},
})
const pingFn = new cdk.aws_lambda.Function(this, "pingFn", {
code: cdk.aws_lambda.Code.fromInline(`
exports.handler = (event, context, callback) => {
callback(null, { statusCode: 200, body: JSON.stringify({ message: "pong" }) });
};
`),
handler: "index.handler",
runtime: cdk.aws_lambda.Runtime.NODEJS_16_X,
})
restApi.root.addMethod(
"GET",
new cdk.aws_apigateway.LambdaIntegration(pingFn)
)
// WAF Web ACL
const webAcl = new cdk.aws_wafv2.CfnWebACL(this, "wafV2WebAcl", {
defaultAction: { allow: {} },
scope: "REGIONAL",
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: "wafV2WebAcl",
},
rules: [
{
name: "AWSManagedRulesCommonRuleSet",
priority: 1,
statement: {
managedRuleGroupStatement: {
vendorName: "AWS",
name: "AWSManagedRulesCommonRuleSet",
},
},
overrideAction: { none: {} },
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: "AWSManagedRulesCommonRuleSet",
},
},
],
})
// APIGWとWebACLを紐付ける
const webAclAssociation = new cdk.aws_wafv2.CfnWebACLAssociation(
this,
"webAclAssociation",
{
resourceArn: `arn:aws:apigateway:${this.region}:${this.account}:/restapis/${restApi.restApiId}/stages/dev`,
webAclArn: webAcl.attrArn,
}
)
webAclAssociation.addDependsOn(webAcl)
webAclAssociation.addDependsOn(
restApi.deploymentStage.node.defaultChild as cdk.CfnResource
)
}
}
S3にログ出力
まずはS3バケットにログ出力する場合。
webAclAssociation.addDependsOn(webAcl)
webAclAssociation.addDependsOn(
restApi.deploymentStage.node.defaultChild as cdk.CfnResource
)
+
+ // WAFログ用S3バケット
+ const bucket = new cdk.aws_s3.Bucket(this, "awsWafLogsBucket", {
+ bucketName: `aws-waf-logs-${this.account}-bucket`,
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
+ blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL,
+ encryption: cdk.aws_s3.BucketEncryption.S3_MANAGED,
+ })
+
+ // WAFログ出力設定
+ const logConfig = new cdk.aws_wafv2.CfnLoggingConfiguration(
+ this,
+ "wafV2LoggingConfiguration",
+ {
+ logDestinationConfigs: [bucket.bucketArn],
+ resourceArn: webAcl.attrArn,
+ }
+ )
+ logConfig.addDependsOn(webAcl)
+ logConfig.addDependsOn(bucket.node.defaultChild as cdk.CfnResource)
}
}
デプロイし、AWSマネコンにて設定できていることを確認しました。
CloudWatchにログ出力
続いてCloudWatch Logsにログ出力する場合。
webAclAssociation.addDependsOn(webAcl)
webAclAssociation.addDependsOn(
restApi.deploymentStage.node.defaultChild as cdk.CfnResource
)
+
+ // WAFログ用CloudWatchロググループ
+ const logGroup = new cdk.aws_logs.LogGroup(this, "awsWafLogsLogGroup", {
+ logGroupName: "aws-waf-logs-log-group",
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
+ })
+
+ // WAFログ出力設定
+ const logConfig = new cdk.aws_wafv2.CfnLoggingConfiguration(
+ this,
+ "wafV2LoggingConfiguration",
+ {
+ logDestinationConfigs: [
+ `arn:aws:logs:${this.region}:${this.account}:log-group:${logGroup.logGroupName}`,
+ ],
+ resourceArn: webAcl.attrArn,
+ }
+ )
+ logConfig.addDependsOn(webAcl)
+ logConfig.addDependsOn(logGroup.node.defaultChild as cdk.CfnResource)
}
}
こちらもデプロイし、AWSマネコンにて設定できていることを確認しました。
ちなみに、 logDestinationConfigs:
の値は [logGroup.logGroupArn]
でも良さそうに見えますが、この値は arn:aws:logs:ap-northeast-1:xxxxxxxxxxxx:log-group:aws-waf-logs-log-group:*
といったように末尾に :*
が付いているためエラーになってしまいます。
❌ WafStack failed: Error: The stack named WafStack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "Error reason: The ARN isn't valid. A valid ARN begins with arn: and includes other information separated by colons or slashes., field: LOG_DESTINATION, parameter: arn:aws:logs:ap-northeast-1:xxxxxxxxxxxx:log-group:aws-waf-logs-log-group:* (Service: Wafv2, Status Code: 400, Request ID: xxxxxxxxxxxxxxxxx, Extended Request ID: null)" (RequestToken: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, HandlerErrorCode: InvalidRequest)
at prepareAndExecuteChangeSet
そのためロググループARNは自前で文字列結合しています。
参考
- AWS CDKでWAFv2を構築しIPアドレス制限を試してみた | DevelopersIO
- [アップデート] AWS WAFのログを直接CloudWatch LogsおよびS3に出力可能になりました | DevelopersIO
- CloudFormationでAWS WAFを構築してみた(2022年1月版) | DevelopersIO
- AWS CDK を使用してStep Functions 用の Lambda ステートマシンを作成 - AWS Step Functions
- AWS Managed Rules for AWS WAFの検知ログ生成アクセスパターン集 | DevelopersIO
Discussion