This article will help you deploy an F5 BIG-IP WAF in front of your AWS API Gateway to provide additional security. It shows how to deploy a basic WAF policy to protect your API Gateway, and you can expand from there to add Denial of Service or bot, OAuth/JWT authorization, geolocation blocking, and more security services.
Amazon’s API Gateway service provides an excellent mechanism for delivery of the API service, but it doesn’t add the protections you need to secure the applications behind it. Without the protections a WAF provides, APIs are a significant threat surface. OWASP has an API Security project here with some guidance on API protection and APIs feature prominently in the current OWASP Top 10 report.
Don’t we all? You could protect your AWS API Gateway from an existing BIG-IP running anywhere, but it’s likely you’ll deploy one in AWS if this is a fresh deployment. To make that easier, we have Cloud Formation Templates available here. There are even templates specific to WAF deployment you can use.
I deployed by using the 2 NIC template because I prefer to separate the management interface from the traffic interface. I then added a secondary IP to the traffic network interface and an elastic IP for it. I SSH’ed to the management IP with my SSH keys, changed the admin password, and provisioned LTM and ASM on my BIG-IP. All of this can easily be automated if you’re doing this frequently.
There are two deployment scenarios, in front of the API Gateway or behind. Here are the traffic flows for each.
In this example, I’ve used a generic “API service” because in the AWS environment there are many possibilities for the backend. For example, it could be a web server running in EC2 or it could be serverless code running in Lambda. You can achieve high availability by running multiple BIG-IPs in different availability zones and balancing them with F5 DNS, ELB, or Route 53. I’ve noted virtual editions in the diagram because it’s likely you’ll deploy them in EC2, but you could use virtual or hardware anywhere to fulfill this purpose.
The F5 in front is probably the optimal choice for most applications because it could block requests from reaching the API Gateway service, which has a cost associated with it per request. It also provides significant flexibility to have AWS calling other integrated services with ease which could be Lambda, a web server, or anything else. If you control the backend API service, then the F5 behind AWS API Gateway solution is a good option as well if you are not concerned with protecting the API Gateway itself as noted above.
What services you choose to deploy on your BIG-IP will also affect the deployment choice. Because the BIG-IP could be used to also provide OAuth/JWT authorization (instead of Lambda), client certificate auth, geolocation blocking or monitoring, DoS protection, AVR based analytics and reporting, and more, you may want those layered in front of the API Gateway service so that they have the most visibility into original source IPs and the capability to block the traffic before it reaches the API Gateway service.
You’ll have three elastic IPs in this scenario if you deployed with a 2 NIC template like me. One for the management interface, one for the traffic interface, and a third that you added for the virtual server (secondary IP). The elastic IP added for the virtual server (secondary IP) will be used as the destination for traffic in front of the BIG-IP. So in the F5 in front flow, this is what your FQDN should point to. The elastic IP of the traffic interface will be the source IP for traffic behind the BIG-IP. Anything behind the BIG-IP will see this as the source IP, useful knowledge for source IP restrictions.
The guide below assumes you will deploy in front of the API Gateway, which will require a rewrite of the URL and this is shown as the rewrite profile. If you deploy BIG-IP behind the API gateway, you will not need this and can remove it to simplify your deployment.
Of course! I’ve prepared a Postman collection and environment for you that handles the deployment listed below. You can download and import this collection and environment from GitHub here, fill in the required variables, and it will do all the work for you in a few seconds. You can take it a step farther and automate deployment and configuration of the BIG-IP in AWS if you want, but this simple collection assumes you already have a BIG-IP deployed and have assigned an IP that you want to use.
To do this, you’ll import the collection and environment for this use case, as well as the F5_Postman_Workflow collection that provides some good test functions. Open up the environment and set the variables noted below.
bigip_username: your username
bigip_password: your password
bigip_mgmt: management IP of your BIG-IP, use the elastic IP pointing to the management interface so you can reach it
monitor_receive_string: a string you expect to receive to indicate success at the below URI
monitor_uri: the URI you want to monitor, remember that the AWS Api GW stage name will prefix it like /
app_dnsname: the FQDN you will point at the BIG-IP, in the below example I’m using “my-desired-fqdn.com”
aws_apigateway_dnsname: the DNS name used by AWS API gateway for your API
app_ip: the IP you want to use for the virtual server to receive traffic, this is a secondary IP assigned to your traffic network interface
app_name: This will be used as a prefix in the name for all objects created so you can easily identify them
cert_name: You can leave this as “default.crt” for a self-signed or select an SSL certificate uploaded to the BIG-IP
key_name: You can leave this as “default.key” for a self-signed or select an SSL key you uploaded to the BIG-IP
(Note this is new and improved to simplify and you no longer need to manually specify the waf_template because Sergey Starzhinskiy wrote some JS for us that sets the template variable dynamically!)
If you’re using a single NIC, you’ll need to alter the calls because they assume your management runs on 443, and you’ll be running management on 8443. Also, you then use 0.0.0.0/0 for the virtual server (app_ip). Remember that if you’re deploying behind the API Gateway you don’t need to do a rewrite and can modify the calls accordingly and a few of the variables won’t be used. Your monitor and pool will change as well.
This is only the beginning. F5 has an entire multi-part training course to help you with the REST API and modern devops deployment methodologies. Go here to see the FREE Super-NetOps training. If you’re wondering why this matters, go here to see Jason Rahm’s great Lightboard Lesson.
No problem! First, we need to create a few objects.
It will be URI Translation mode, request type, the client URI will be the FQDN you intend to point at the BIG-IP, and the server URI will be the FQDN of the Amazon API Gateway deployment. Reminder, if you’re deploying behind the API Gateway you don’t need to do a rewrite.
You should create an advanced monitor to monitor not just the Amazon API Gateway service, but YOUR stage/deployment. You’ll use an HTTPS monitor type and set your send string to something like:
GET /awsapigw-stage/ HTTP/1.1\r\nHost: my-aws-api-gateway-fqdn.com\r\nConnection: Close\r\n\r\n
In this case, I’m monitoring the URI “/" under the stage I’ve deployed in AWS API Gateway, named “awsapigw-stage”. My API gateway is available at the FQDN "my-aws-api-gateway-fqdn.com", the same one used above on the server side in the rewrite profile. The receive string will be something that appears on that URI when I make a request that indicates the API is working, here I’m expecting the text “Some text indicating my API loaded”.
This will be different if you’re putting the BIG-IP behind the API Gateway.
You will use the ephemeral DNS name based nodes for this solution. Select the monitor you created previously, and create a new FQDN node using the same AWS Api Gateway FQDN used twice previously, and port 443.
This will be different if you’re putting the BIG-IP behind the API Gateway, and you might use standard IP based pool members.
If you want your SSL cert to be trusted, you’ll upload your certificate/key and create a client SSL profile. For this example, we’re just going to use the default self signed ssl profile “clientssl".
If you’re behind the API Gateway, you can also do client cert auth from the API Gateway to the BIG-IP if desired.
Now we’ll create a virtual server to receive and process the traffic. You’ll use the rewrite profile and pool you created previously. Anything not listed here will be left default.
Name: As desired
Type: Standard (default)
Destination Address: An IP address within your VPC that you assign as a secondary to the BIG-IP (or 0.0.0.0/0 if you’re using a single NIC BIG-IP)
Service Port: 443
HTTP Profile: HTTP
SSL Profile (Client): clientssl (or other as noted above)
SSL Profile (Server): serversslSource Address Translation: Auto Map
Rewrite Profile: The one you created earlier
Pool: The one you created earlier
Now you have a functioning reverse proxy for your API. Test it to make sure you can reach it, don’t proceed until this is working.
Next, we’ll add the WAF protections. First, create a new WAF policy. Select the API Security profile if it’s present on your version. If not, you can select a Rapid Deployment Policy, Fundamental, or other as appropriate.
Now you can add the WAF policy and the appropriate logging profile to your virtual server. Here I’m choosing the “Log illegal requests” profile.
The API protection you deploy with this solution is a basic generic policy and is setup in transparent mode. It will start out with a large set of signatures from the API Security template. You’ll want to turn on blocking mode quickly and depending on your API assign the relevant additional profiles (JSON, XML, etc) and other protections as appropriate.
Although someone would have to guess your API Gateway implementation’s DNS name to connect directly now, a recommended practice is still to lock down your API Gateway so that the traffic can only come from BIG-IP. You can do this with IAM and source IP restrictions, you could leverage an API-key and insert the secret key to the request before it leaves the BIG-IP, or you could create a custom security solution using Lambda/Cognito to require authorization (OAuth as example) and have the BIG-IP insert that (in the header as example).