Many times I have been asked to explain how Azure WAF and Firewalls can be deployed as CI/CD with a tool like Azure DevOps and using Terraform templates as Infrastructure-as-Code. Both of these tools are very popular and as customers shift security to the left, I have invested some time playing and testing with Azure DevOps and integrating Terraform templates to manage my Azure infrastructure.
Here I just want to show you a simple example to deploy the OWASP insecure WebApp ‘JuiceShop’ within an Azure WebApp, protected by an Application Gateway that has the WAF module turned ON. My WAF configuration will be managed by an Azure WAF policy
The idea is to create a simple pipeline where we run all the mandatory Terraform steps (init, plan, validate & deploy). Also, one of the great things about using Terraform with Azure is that you can use an Storage Account blob as the Terraform state file, which means that you can collaborate with other DevSecOps engineers as the state file is just not a file in your computer. We need to define Azure as your Terraform ‘backend’ where you declare the location of the state file. For that purpose we need to create a Service Connection within your Azure DevOps project which has ‘Contributor’ rights to the subscription where you deploy your infrastructure
You can clone the code from my github repository: https://github.com/davisanc/AzureWAFPoliciesandTerraform
The main.tf file will define the terraform state file, create your Resource Group, WAF Policy and a storage account
The CreateJuiceShopWebApp.tf file will create a WebApp using an App Service plan and uses a docker image to build the application
Finally, the attachWAFPolicy.tf creates the VNET resources, Application Gateway configuration and finally attaches the WAF policy by using the firewall_policy_id command. Here you may prefer to use terraform variables to read your subscription ID and Resource Group name instead of declaring the full path statically
When you create a Pipeline within an DevOps project, you will specify the location of your code. You can fork or clone the repository to your local machine and use Visual Studio Code to make any changes.
You will need to create a service connection so Azure DevOps can deploy resources to your Azure Subscription
Within the pipeline the job will first install the Terraform module
Your next step will be to run Terraform Init, here you need to declare the path to the terraform state file. This can be any storage account blob. In my case, I have already created another Resource Group with a blob which will store the state file (this is not part of the terraform code). The ‘key’ value is the full path to the blob
Here you use the service connection that has created the service principal in your Azure AD with contributor rights
This follows by running Terraform plan
And finally you run Terraform validate and apply to create all the infrastructure services for first time or any changes to your resources
Any subsequent changes you make to your terraform code will trigger the job when you select ‘Continuous Integration’
When you run the Pipeline for first time, you will see in the Job console all the steps defined in the pipeline and hopefully you don’t get any errors
These will be the resources created in your subscription
If you go to the WebApp and click on the URL, it will look something like this (the URL uses random digits to make it unique))
Go to the Account page and try using a simple SQL Injection attack by giving this input as username and any password: ‘ OR 1=1–
Now, we will repeat the same test but we will go through the Application Gateway which has the WAF module turned ON
If everything goes well, you will get an HTTP 403 error page as the WAF has blocked the SQL Injection attempt
You can see the logs by enabling diagnostics logs in the Application Gateway and enable all the WAF Logs. Here the best practice is to use a Log Analytics or Sentinel workbook where you can visualize these logs in a better way
Thanks for reading!