Deploying a Custom WAF v2 Rule with the AWS CDK

Search for a command to run...

No comments yet. Be the first to comment.
Understanding why new accounts can have a low limit and how to increase it

If you look at the default VPC in your AWS account, you'll notice there's also a default Security Group. It includes an inbound rule that allows all traffic from itself, a self-referencing rule. This means that any resources with this Security Group ...

Solving why AWS Application Migration Service source servers sometimes *disappear*

I recently passed my 5th AWS certification exam, the AWS Security Specialty (SCS). I started on the journey to obtain a substantial collection of AWS certifications back in May 2021. I'm a freelance cloud developer and consultant, focused on building...

I'm working with a client to migrate their infrastructure to AWS and apply some modest modernizations during phase 1 of the project. During the migration process, we need to keep their new public endpoints in AWS private during the testing phase. We have a few CloudFront distributions deployed in front of ALBs. We're using the AWS CDK to script out their infrastructure. Unfortunately, WAF v2 does not have any L2 constructs for the CDK, so you have to resort to using Cfn*-style L1 constructs.
My use case is pretty straightforward: block all traffic unless it comes from a source IP listed in a WAF IP Set. The WAF CDK code looks like this:
import { CfnOutput } from "aws-cdk-lib"
import { CfnIPSet, CfnWebACL } from "aws-cdk-lib/aws-wafv2"
const migrationTestingIpSet = new CfnIPSet(this, 'MigrationTestingIpSet', {
addresses: [
"1.1.1.1/32", // whitelisted IPs in CIDR format
],
ipAddressVersion: 'IPV4',
scope: 'CLOUDFRONT',
description: 'List of staff allowed to test migration endpoints',
})
const migrationWaf = new CfnWebACL(this, 'MigrationWaf', {
defaultAction: {
block: { }, // Block all traffic except the IP set
},
scope: "CLOUDFRONT",
description: "Allows staff to test migration endpoints before cutover",
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: 'DefaultBlocks',
sampledRequestsEnabled: true,
},
rules: [{
name: "AllowStaffIps",
priority: 1,
statement: {
ipSetReferenceStatement: {
arn: migrationTestingIpSet.attrArn,
}
},
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: 'AllowsToStaffIpSet',
sampledRequestsEnabled: true,
},
action: {
allow: {}
}
}],
})
new CfnOutput(this, "MigrationWafArnOutput", {
description: "Migration WAF Arn",
value: migrationWaf.attrArn,
})
You'll see that there is a default action that blocks all traffic. Then a rule is defined that allows traffic based on the IP Set.
Next, connect your WAF to your CloudFront distribution:
import { Distribution } from "aws-cdk-lib/aws-cloudfront"
const prodAppCdn = new Distribution(this, "ProdAppCdn", {
defaultBehavior: { /* Default Behavior here... */ },
/* other props here */
webAclId: "arn:aws:wafv2:us-east-1:1234567890:global/webacl/WafNameHere/WafAclGuidHere",
})
NOTE: CloudFormation requires deploying WAFs to us-east-1 if you're integrating it with CloudFront. In my case, I created a separate CDK stack since I was deploying everything else to us-east-2.
Due to the cross-reference nature of deploying to us-east-2 vs us-east-1, I included the string value for the WAF ARN instead of referencing it from the WAF stack. The WAF page in the AWS Console doesn't currently show the ARN for the ACL, but the first code snippet I shared returns it as a CfnOutput value to make it easier to copy/paste. Otherwise, you'll have to build the WAF ACL Id using a format similar to the above, filling in your account number, WAF name, and WAF Id appropriately. The WAF ACL Id is a GUID that you can find in the URL of the Console address when you visit the WAF details screen.
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_wafv2-readme.html - Explains L1 constraint
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_WAFv2.html - CloudFormation documentation in case it's helpful
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_wafv2.CfnIPSet.html - How to create the IP Set
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_wafv2.CfnWebACL.html - How to create the WAF ACL
https://stackoverflow.com/questions/72605879/making-the-waf-rule-by-cdk - SO post with some helpful tidbits