AWS AppSync with WAF (wooo!) & CDK + CF
Earlier this month AWS announced support for AWS Web Application Firewall (WAF) integration with AWS Appsync, a managed graphql API service. This is actually a really cool addition that is likely to increase uptake of Appsync. Let's take a better and look and as always I'll provide a Cloud Development Kit (CDK) repo and CloudFormation (CF) ready to go.
This is kind of a big deal, for a couple of reasons:
- The integration in AppSync is actually really tight, AWS have worked pretty hard to remove the usual barriers with this type of configuration
- Increased security for your AppSync deployment (who doesn't love that!)
What the heck is AppSync
The image above from AWS doest the best at explaining, but to summarize let's just say AWS have always had a hand in mobile app dev space. You'll be familiar with a bunch of the SDK's already and maybe AWS Mobile Hub which has now been rolled into AWS Amplify. Now, Amplify is an accelerator, it's a way to get an MVP up and running fast and one of the key integrations is, you guessed it...AppSync.
AppSync provides a GraphQL API as a managed service, you can wire up multiple datasources and authentication. There are some key stands outs for AppSync that might be appealing to readers:
- Offline mode
- AppSync supports caching on the mobile client so if the user drops out network coverage or there is an interruption to the service it will continue working and then magically sync up when restored. Pretty neat!
- Multiple standards certifications
- AWS Cognito authentication
- If you don't know what cognito is this is authentication service by AWS, you can use local users or hook up social logs, SAML etc. Authentication of some sort is required, there are some other options available like OpenID, IAM, the default token auth has a default short limit but can be extended to 365 days.
It’s not possible to have an AppSync API without some sort of authentication mechanism associated with it.
There is a really great write up by Brice Pelle from AWS about the security of AppSync, here are the highlights but I encourage you to read the full post:
- DDoS protection out of the box
- Granular access / fined grained access control (field level)
- Multiple authentication methods possible
- As mentioned above, fully compliant with standards such as SOC, ISO, PCI etc (it's a long list)
- Public endpoints
- As probably expected for a service used in the mobile app space, the endpoints are public
Remember this bit...
AppSync endpoints are public
Enter the web application firewall (WAF)
So, we have all of these great security features, what is this WAF about then? Web Application Firewalls provide a firewall most commonly for API Gateways and Application Load Balancers in AWS, they've been around for a while now and there are some AWS solutions for automation around firewall management. Cool cool.
Why do I want a WAF for AppSync? Well there are a few things that come to mind:
- Company security requirements
- You may have a company policy that recommends or requires the use a security gateway .e.g firewall for public facing services. This can often be hand in hand with authentication requirements.
- You may operate out of a single country and wish to block all other countries and maybe just block a certain country or three
- Rate limiting
- AppSync does have built in throttling but you may want to further reduce based on your app profile
- You won't get any reports from AppSync about security related items
- Good security practice
- If you use AppSync for something like a single page web app you won't be protected against SQL injection, XSS attacks, the typical OWASP Top 10 but the AWS WAF can help
Also important to onsider the AWS shared responsibility model
Let's build stuff
Now for the juicy part, let's go ahead and build something. My previous posts haven't included a diagram of the build, this changes now:
- AWS Web Application Firewall
We are going to add some rules to this and link it to AppSync
We'll use cognito to provide username & password access
This is our graphQL API
This is going to be our persistent datastore
Just set it up already - CloudFormation Deploy
If you've seen my previous posts you'll see a theme coming along here, I like AWS CDK and CloudFormation. With the Deploy Stack button below you can deploy the above stack in your account, pretty cool!
Just follow the CloudFormation prompts, there are no parameters this time around. If you're nervous keep reading.
And for the CDK folk
For all the CDK fans out there you can access the CDK code in the open source github repo so you can chop and change it as much as you like, see what I've done, criticize etc. If you do have any tips please feel free to share in the github repo or get in touch email@example.com. Access the repo below:
Key configuration items (WAF)
This is a post about AppSync and WAF so let's dive into the WAF configuration picked up for this config. The AWS WAF has a bunch of rules that you can apply, there is a concept of capacity units and you only get 1500, this means you can't just apply everything. This is a good thing when you think about because it makes you think about what rules you actually need.
The rules picked up for this deployment:
- Restrict to certain countries, I've selected Australia but I've set this to count which means just let me know if this rule sees hits but don't enforce. I recommend enabling though, I've set to count for this demo just because we have readers from all over.
- Rate limit
- Restrict to 100 requests for 5 minutes from the same IP
- AWS Core ruleset
- This picks up some common OWASP Top 10 controls
WAF settings review
Once you've deployed the stack or gone your own way manually you'll find a Web ACL created and will be associated to our AppSync API. You'll find three rules (as above).
Note: When you first go into the WAF service it's usually set to US, change the region you've deployed into.
For all the WAF pros, notice the user-agent is disabled. AppSync doesn't support changing the no-agent header. The core rule set enabled by default will block AppSync access!!!
Add a user to Cognito
At this point we can see that everything looks OK, for those wanting to test out AppSync a little more let's run through setting up a user in cognito.
We'll add a local cognito, navigate to cognito > manage user pools > tnc pool and then go to users and groups.
The next part is create user, this is pretty straight forward but do a couple of things:
- Deselect the checked boxes
- Use a password with
- Uppercase, lowercase, number, symbol, 8+ chars
Note: Force change password will be set, we'll get to that later. Also, this is for local users, as you can imagine if you'd used something like Google or Apple you wouldn't be creating local users.
So let's test AppSync
AWS have done a pretty good job in the console this time around, I know we all have a gripe from time to time in the console space. AppSync give you a basic way to view the graphQL schema but you can also interact with it e.g. query your API, which includes read and writes.
Jump over to the AppSync service in the AWS console, click the talkncloud API and click the Run A Query orange button.
You should be presented with something like this:
Notice the login with UserPools button? That's the cognito userpool and we've already set a user up, so go ahead and login. If you've only got one clientID it should be the only one from the list, if you have more you'll need to track down the right one.
Now, remember the cognito force password change option? This will prompt you to change the password on first use, go ahead and enter a new password. After that, you should be logged in.
Create a new TODO in AppSync
You'll need to select Mutation from the drop down and hit the + button, this bring item into the explorer tree.
Select Name and Description, to the right of those items you can now enter some text:
You can see I've entered:
- Name: Zazu
- Description: I've got a lovely bunch of coconuts
Hit the orange play button:
If everything works you should see a pretty short return on the very right and there will be a unique ID, that is the ID for our item in DynamoDB. Sweet!
Now let's query it back, copy the ID returned above and select Query from the drop and hit the + button. Now in the getTodo query you can select id, name, description. In the ID field enter the ID from the previous step, for me this is "4abf9123-77c1-4551-b9ee-76f4a4472c31". Now you should see the item, this is retrieved from our DynamoDB through the AppSync API using GraphQL:
If you head over to DynamoDB you should see an entry in your dynamo table, nice and easy!
Alright, there is a lot going on here so let's quickly run through with what we ended up with:
- We've got a graphQL API
- We've forced login through cognito
- We've got authentication with named users
- Disabled guest sign up
- Associated to our identity provider in cognito to our cognito app client
- We've got authentication with named users
- We've got a web application firewall with three rules
- Block by country
- Limit requests
- Core rules like OWASP
- Associated the firewall with AppSync
- We've got a web application firewall with three rules
AppSync is a managed service with a bunch of really great security features out of the box. Now with WAF support users have even more flexibility to secure the AppSync public endpoint. I really like the integration here with AppSync, you can tell the AppSync team are putting the effort in this space, if you check out the console you can toggle options like WAF with little effort. I've really just scratched the service here on this whole stack, each service really needs it's own write up but hopefully this gives you a start to check them out.
As always, keen for feedback, drop me a line if questions or concerns, happy to help others or share tips.
Bit of clean up warning, if or when you delete the cloudformation stack you'll need to delete the dynamoDB table. This can be done from the DynamoDB console.