Deploying Next-Gen WAF with Terraform
IMPORTANT: This feature is only available to Next-Gen WAF customers with access to the Next-Gen WAF control panel.
This tutorial guides you through deploying Fastly's Next-Gen Web Application Firewall (Next-Gen WAF) for web and API endpoint security using the Edge Deployment method, which allows you to add an edge security service onto our Edge Cloud Platform without needing to make any modifications to your own hosting environment.
You will use the following Terraform providers:
Prerequisites
Before deploying the Next-Gen WAF, ensure you have the following:
- A Fastly Edge API key with service creation and management permissions;
- A Next-Gen WAF API key with Corp Admin permissions;
- An established Next-Gen WAF corp and site.
1. Configure Terraform providers
Ensure Terraform 0.13+ is configured with the required providers for Fastly:
terraform { required_providers { fastly = { source = "fastly/fastly" version = ">= 5.7.1" } sigsci = { source = "signalsciences/sigsci" version = ">= 3.0.0" } http = { source = "hashicorp/http" } }}
2. Define variables
Declare the necessary variables and resources for the Fastly Edge VCL configuration, Next-Gen WAF settings, and dynamic snippets. This includes specifying domain names, backend hostnames, and API keys.
variable "FASTLY_API_KEY" { type = string description = "This is API key for the Fastly VCL edge configuration."}
variable "USER_VCL_SERVICE_DOMAIN_NAME" { type = string description = "Frontend domain for your service." default = "ngwaf-tf-demo.global.ssl.fastly.net"}
variable "USER_VCL_SERVICE_BACKEND_HOSTNAME" { type = string description = "hostname used for backend." default = "http-me.glitch.me"}
variable "NGWAF_CORP" { type = string description = "Corp name for NGWAF"}
variable "NGWAF_SITE" { type = string description = "Site name for NGWAF"}
variable "NGWAF_EMAIL" { type = string description = "Email address associated with the token for the NGWAF API."}variable "NGWAF_TOKEN" { type = string description = "Secret token for the NGWAF API." sensitive = true}
3. Set values for variables
The values for the declared variables must be available to the environment where Terraform is running following HashiCorp's guidance for managing variables.
4. Update the Fastly VCL service
Before linking the Next-Gen WAF edge deployment to the VCL service, you must add placeholders for dynamic snippets and a dictionary that will be used by the Next-Gen WAF integration. If the configurations are not added by Terraform, then Terraform will attempt to remove or replace the settings needed for the Next-Gen WAF Edge implementation. This behavior exists because the Next-Gen WAF Edge implementation uses the Terraform provider to make updates which impact the resources defined by the Fastly provider. Populating the dictionary and dynamic snippets with the desired values for the Next-Gen WAF edge deployment will be handled via the Terraform provider.
The dynamic snippets are simply commented VCL and act as a placeholder for the modifications that will occur via the Next-Gen WAF edge deployment. It is worth noting that both the dictionary and dynamic snippets are versionless. This means when modifications are made (even in subsequent service versions), those modifications will persist.
The priority of the snippets ngwaf_config_miss
and ngwaf_config_pass
are intentionally high, to avoid conflicts with any existing VCL logic.
resource "fastly_service_dynamic_snippet_content" "ngwaf_config_init" { for_each = { for d in fastly_service_vcl.frontend-vcl-service.dynamicsnippet : d.name => d if d.name == "ngwaf_config_init" }
service_id = fastly_service_vcl.frontend-vcl-service.id snippet_id = each.value.snippet_id content = "### Fastly managed ngwaf_config_init" manage_snippets = false}
resource "fastly_service_dynamic_snippet_content" "ngwaf_config_miss" { for_each = { for d in fastly_service_vcl.frontend-vcl-service.dynamicsnippet : d.name => d if d.name == "ngwaf_config_miss" }
service_id = fastly_service_vcl.frontend-vcl-service.id snippet_id = each.value.snippet_id content = "### Fastly managed ngwaf_config_miss" manage_snippets = false}
resource "fastly_service_dynamic_snippet_content" "ngwaf_config_pass" { for_each = { for d in fastly_service_vcl.frontend-vcl-service.dynamicsnippet : d.name => d if d.name == "ngwaf_config_pass" }
service_id = fastly_service_vcl.frontend-vcl-service.id snippet_id = each.value.snippet_id content = "### Fastly managed ngwaf_config_pass" manage_snippets = false}
resource "fastly_service_dynamic_snippet_content" "ngwaf_config_deliver" { for_each = { for d in fastly_service_vcl.frontend-vcl-service.dynamicsnippet : d.name => d if d.name == "ngwaf_config_deliver" }
service_id = fastly_service_vcl.frontend-vcl-service.id snippet_id = each.value.snippet_id content = "### Fastly managed ngwaf_config_deliver" manage_snippets = false}
5. Integrate the Next-Gen WAF Edge deployment
Use the Signal Sciences provider to create the Next-Gen WAF Edge Service and link it to the Fastly VCL service. This step involves setting up an edge dictionary and updating dynamic snippets maintained by Fastly.
NOTE: The updated dynamic snippets will not be overwritten by subsequent terraform updates because of the manage_snippets = false
setting in the dynamic snippet Terraform configuration. If manage_snippets
is not set to false
, then the dynamic snippets will be overwritten and traffic for your service will not be protected by the edge Next-Gen WAF deployment.
provider "sigsci" { corp = var.NGWAF_CORP email = var.NGWAF_EMAIL auth_token = var.NGWAF_TOKEN fastly_api_key = var.FASTLY_API_KEY}
resource "sigsci_edge_deployment" "ngwaf_edge_site_service" { # https://registry.terraform.io/providers/signalsciences/sigsci/latest/docs/resources/edge_deployment site_short_name = sigsci_site.ngwaf_edge_site.short_name}
resource "sigsci_edge_deployment_service" "ngwaf_edge_service_link" { # https://registry.terraform.io/providers/signalsciences/sigsci/latest/docs/resources/edge_deployment_service site_short_name = sigsci_site.ngwaf_edge_site.short_name fastly_sid = fastly_service_vcl.frontend-vcl-service.id
activate_version = true percent_enabled = 100
depends_on = [ sigsci_edge_deployment.ngwaf_edge_site_service, fastly_service_vcl.frontend-vcl-service, fastly_service_dynamic_snippet_content.ngwaf_config_init, fastly_service_dynamic_snippet_content.ngwaf_config_miss, fastly_service_dynamic_snippet_content.ngwaf_config_pass, fastly_service_dynamic_snippet_content.ngwaf_config_deliver, ]}
6. Apply configuration
Apply the Terraform configuration using the following command.
terraform apply -parallelism=1
-parallelism=1
is required based on guidance from the Signal Sciences Terraform provider. In case of errors…
When the configuration is applied, the dynamic snippets will be added in a VCL version. The Signal Sciences provider will then do the following:
- Clone the existing active configuration.
- Populate the dynamic snippets and Dictionary with the values needed for the Next-Gen WAF integration.
- Activate the new version.
TIP: Your local Terraform state for the VCL service won't reflect this newly incremented version of the VCL service. Run terraform apply -parallelism=1 again in order to match the local state to the remote state. This
terraform apply
should not make any changes to the VCL service. After a successful run, you should see outputs like the following:
Outputs:
live_waf_love_output = <<EOT #### Click the URL to go to the Fastly VCL service #### https://cfg.fastly.com/LINKEDSERVICEID
#### Click the URL to go to the Fastly NGWAF service #### https://dashboard.signalsciences.net/corps/my_corp_name/sites/my_site_name #### Send a test request with curl. #### curl -i "https://devhub-ngwaf-terraform-tutorial.global.ssl.fastly.net/anything/whydopirates?likeurls=theargs" -d foo=bar
#### Send a test as traversal with curl. #### curl -i "https://devhub-ngwaf-terraform-tutorial.global.ssl.fastly.net/anything/myattackreq?i=../../../../etc/passwd" -d foo=bar
#### Send a test as XSS with curl. #### curl -i "https://devhub-ngwaf-terraform-tutorial.global.ssl.fastly.net/anything/myattackreq?foo=%3Cscript%3E" -d foo=bar
#### Troubleshoot the logging configuration if necessary. #### https://docs.fastly.com/en/guides/setting-up-remote-log-streaming#troubleshooting-common-logging-errors curl https://api.fastly.com/service/OuEbVB7UoymNTqI9ghCpE5/logging_status -H fastly-key:$FASTLY_API_KEY
EOTresponse_body = [ "LINKEDSERVICEID",]
Deployment considerations
Different designs using Terraform will require different approaches. This the following sections contain a few considerations.
Deploying Next-Gen WAF edge to an a new VCL service
For new implementations, you may use the complete Terraform implementation. When running the command terraform apply -parallelism=1
, the implicit dependencies and the depends_on
blocks within the resources ensure that the resources are created in the correct order.
Deploying Next-Gen WAF edge to an existing VCL service
The above methodology can work for an existing VCL service as well. It's very important to follow the guidance for updating the Fastly VCL service which makes Terraform aware of the dynamic snippets and dictionary. This configuration must occur before using the sigsci_edge_deployment_service
Terraform resource. Otherwise, the dynamic snippets will be overwritten or deleted by subsequent terraform apply
runs. For more information on the manage_snippets
false configuration, please see our Fastly Terraform provider documentation for the resource fastly_service_dynamic_snippet_content
.
You may complete the Next-Gen WAF edge implementation without sending traffic to the WAF. This can be helpful to test or gradually ramp traffic to the WAF edge implementation. The Edge_Security
Dictionary may also be set to the value 0
within the Terraform resource sigsci_edge_deployment_service
using the percent_enabled
field. Our Traffic Ramping documentation has details for the percent_enabled
field.
Deploying Next-Gen WAF edge to an existing Next-Gen WAF site
If you already have a Next-Gen WAF site you wish to use with the Next-Gen WAF edge implementation, then you may remove the Terraform resource block sigsci_site
:
resource "sigsci_site" "ngwaf_edge_site" { short_name = var.NGWAF_SITE display_name = var.NGWAF_SITE block_duration_seconds = 86400 agent_anon_mode = "" agent_level = "log"}
You must then update all Terraform resources that depend on the sigsci_site
resource, by replacing all references to sigsci_site.ngwaf_edge_site.short_name
with the value var.NGWAF_SITE
.
Optional: Identifying Fastly Delivery services linked with Next-Gen WAF edge deployments
Terraform can provide helpful outputs when doing a deployment. The example below shows you how to identify different Delivery services linked to a Next-Gen WAF edge deployment.
provider "http" {}
data "http" "linked_fastly_services" { url = "https://dashboard.signalsciences.net/api/v0/corps/${var.NGWAF_CORP}/sites/${var.NGWAF_SITE}/edgeDeployment"
request_headers = { x-api-user = var.NGWAF_EMAIL x-api-token = var.NGWAF_TOKEN Content-Type = "application/json" }}
output "response_body" { value = [for item in jsondecode(data.http.linked_fastly_services.response_body)["ServicesAttached"] : item.id]}
Full reference implementation
The fastly/ngwaf-terraform-edge-deploy repository closely follows the recommendations in this tutorial.