Internet voting may never become a reality; however, securing your APIs should always be on the forefront of standard development practices. One of the tools offered by AWS to secure your application is the AWS WAF. The AWS WAF can be configured through the AWS console in order to create web access control lists and add individual firewall rules. Using the console for security engineers is a good start; however, provisioning of cloud resources through ClickOps is not considered a best practice in software development. This is where the AWS CDK comes in.
Codifying ClickOps WAF configuration
The AWS CDK is one of the tools offered from AWS in order to create Infrastructure as Code (IaC). The CDK differentiates itself from other tools such as Terraform by using standard programming languages instead of a domain specific language (DSL). Since the CDK is one of the new up and coming methods for creating IaC, that is what will be used for this article.
In this article I will outline my experience using the CDK, present source code to a simple example that blocks API access by country code, and recommend ways to streamline development when configuring the WAF with the CDK.
The Source Code
The source code for this article can be located here in GitHub:
https://github.com/drewword/aws-cdk-waf-country-block
State of The CDK
The current version of the CDK that I used for the demo is 1.59.0. The CDK is receiving updates all of the time, so things may have changed or improved by the time you read this article. When using the CDK, there are various layers of library support classes. These range from the AWS solutions constructs (https://aws.amazon.com/solutions/constructs/) all the way down to providing escape hatches for getting around things that aren’t available yet. (https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html)
Currently the state of support for the AWS WAF is towards the lower end, where simple CloudFormation wrapper classes are provided. Not ideal.
When looking at other IaC options, I noticed that Terraform isn’t much better at this stage either.
Versions of CloudFormation WAF
It appears that there are several different versions of CloudFormation code to use when coding the WAF as well, which doesn’t make things any easier. From what I could determine, WAFv2 was the best option to utilize, and the shadow CDK classes for v2: (https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html)
Ready to Use Rules
Fortunately the AWS WAF does provide pre-configured rules that could easily be used in conjunction with CDK configuration, so this might be an option to follow up on for some organizations. (https://aws.amazon.com/about-aws/whats-new/2017/11/ready-to-use-managed-rules-now-available-on-aws-waf/) I wanted to configure my own rules, so that is what this article will focus on.
Rolling Wrappers
The best option I can recommend at this point if you are going head first into using the CDK with the AWS WAF would be to create some wrapper classes around some of the common configuration you may want to deploy. There will obviously be a balancing act between creating your own CDK library vs. simply using what’s available; however, when only the CloudFormation “Cfn” classes are available, that is probably the best way to insulate yourself from the implementation details.
Examining the Code
OK, enough sounding like a politician, let’s get busy blocking our API from foreign actors in our fictitious voting API.
To start with, we will create a very simple Lambda, and pass that Lambda to our Country Code Blocker class. Here is the demo CDK stack that combines the two. On line 11, a country code array is passed in to the CountryCodeBlocker. We will start with blocking the US. For a United States presidential election, we at least need to block ourselves. :)
The HelloWorldLambda simply creates a hello world API, or any other API that you would want to add some WAF protection.
Blocking Countries
In order to attach our WAF to our API, there are essentially two steps.
- Create a WebACL with an array of rules.
- Create the association with the resources.
For step 1, you will see on line 19 the creation of the web access control. (Within CountryCodeBlocker.ts) This is using the CDK wafv2 API (which mirrors the CloudFormation WAF v2 API). As you can see, this is not necessarily the most readable. It’s really passing in general JSON configuration to an API.
Keeping the code short was essential here, as lower level classes of the CDK is not the brightest point of the toolkit. This is where I recommend wrapping your desired functionality in your own class, and giving your developers an easier time at implementing this for their multitude of APIs.
Creating the Rule Association
Luckily the call to make the association from either your load balancer, or API Gateway endpoint is relatively straightforward as show below.
The API Gateway ARN must be constructed per the AWS CLI documentation. (https://docs.aws.amazon.com/cli/latest/reference/wafv2/associate-web-acl.html), as follows:
The ARN must be in one of the following formats:
For an Application Load Balancer: ``arn:aws:elasticloadbalancing:region :account-id :loadbalancer/app/load-balancer-name /load-balancer-id ``
For an Amazon API Gateway stage: ``arn:aws:apigateway:region ::/restapis/api-id /stages/stage-name ``
Here is the implementation for getResourceARNForEndpoint:
The final bits of interest is the creation of the wafv2 CfnWebACL RuleProperty. This is show below:
This has very rough checks for ensuring the rule conforms to the “RuleProperty” class; however, this does not ensure your configuration will work properly. Some JSON blocks are required for some rules, and others will blow up with odd exceptions if configured incorrectly. If in doubt, I recommend configuration the the AWS console first to see what is required for your particular rule.
Running the Demo
OK, let’s run the demo. After downloading the code, all you need to do is run
cdk deploy
After running deploy, you will see a URL output for your new API. If you are in the United States, when hitting the API, you will see the following result from API Gateway:
{"message":"Forbidden"}
Well, that was boring! How do we get to our API? Well, just use your favorite VPN, and change your country! If you don’t have a VPN provider, you can download the Opera browser free of charge, which has a VPN built in free!
After turning on your VPN (and making sure your requests are coming from outside the US), you should see:
{"hello":"world"}
Console Comparison
So what do the rules look like in the AWS console? Let’s take a look.
Here is the result when something matches:
Luckily there is a rule builder within the console, which allows you to see roughly how the CDK code should be configured. While the CDK only offers Cfn wrapper classes for the WAF, it can be a helpful step to visually configure rules, look at the JSON rule editor, and then create the infrastructure code from there.
Conclusion
Before wrapping up, don’t forget to destroy your stack, so you don’t incur any extra charges in your account:
cdk destroy
Hopefully this simple example gives insight into how you can use the AWS WAF, CDK, and API Gateway together within your organization.