Scenario #4: Manage an F5 BIG-IP Advanced WAF Policy on different devices¶
The goal of this lab is to manage an F5 BIG-IP Advanced WAF Policy on multiple devices, including:
Different standalone devices, serving the same applications.
Different devices serving different purposes; for example, changes tested first in a BIG-IP QA/test environment before applying them into a production environment.
Pre-requisites¶
On the BIG-IP:
BIG-IP version 16.1 or newer
Advanced WAF Provisioned
Credentials with REST API access
On Terraform:
Using F5 BIG-IP provider version 1.15.0 or newer
Using Hashicorp versions following Releases and versioning
Create a policy¶
Create 4 files:
variables.tf¶1variable qa_bigip {} 2variable prod_bigip {} 3variable username {} 4variable password {}
inputs.auto.tfvars¶1qa_bigip = "10.1.1.9:443" 2prod_bigip = "10.1.1.8:443" 3username = "admin" 4password = "whatIsYourBigIPPassword?"
main.tf¶1terraform { 2 required_providers { 3 bigip = { 4 source = "F5Networks/bigip" 5 version = "1.15" 6 } 7 } 8} 9provider "bigip" { 10 alias = "qa" 11 address = var.qa_bigip 12 username = var.username 13 password = var.password 14} 15provider "bigip" { 16 alias = "prod" 17 address = var.prod_bigip 18 username = var.username 19 password = var.password 20} 21 22data "http" "scenario4" { 23 url = "https://raw.githubusercontent.com/fchmainy/awaf_tf_docs/main/0.Appendix/scenario4.json" 24 request_headers = { 25 Accept = "application/json" 26 } 27} 28 29resource "bigip_waf_policy" "s4_qa" { 30 provider = bigip.qa 31 application_language = "utf-8" 32 partition = "Common" 33 name = "scenario4" 34 template_name = "POLICY_TEMPLATE_FUNDAMENTAL" 35 type = "security" 36 policy_import_json = data.http.scenario4.body 37} 38 39resource "bigip_waf_policy" "s4_prod" { 40 provider = bigip.prod 41 application_language = "utf-8" 42 partition = "Common" 43 name = "scenario4" 44 template_name = "POLICY_TEMPLATE_FUNDAMENTAL" 45 type = "security" 46 policy_import_json = data.http.scenario4.body 47}
Note
The template name can be set to anything. When it is imported, the value is overwritten.
This example references an existing policy from a GitHub repository, but you can also create a policy from zero on both BIG-IPs.
Initialize, plan, and apply your new Terraform project.
foo@bar:~$ terraform init Initializing the backend... Initializing provider plugins... [...] Terraform has been successfully initialized! foo@bar:~$ terraform plan -out scenario4 > output_scenario4.1 foo@bar:~$ more output_scenario4.1 foo@bar:~$ terraform apply "scenario4"
For both BIG-IPs, verify that the two policies are present and consistent.
Simulate a WAF Policy workflow¶
A common workflow includes the following processes:
Enforce attack signatures in the test environment.
Verify that these changes do not break the application and identify potential false positives (FP).
Finalize the changes in a test environment, before applying them to your production environment.
Enforce attack signatures on the test environment
In order to track attack signature changes, use a Terraform HCL map to add a signature list definition in the inputs.auto.tfvars file:
signatures = { 200101559 = { signature_id = 200101559 description = "src http: (Header)" enabled = true perform_staging = false } 200101558 = { signature_id = 200101558 description = "src http: (Parameter)" enabled = true perform_staging = false } 200003067 = { signature_id = 200003067 description = "\"/..namedfork/data\" execution attempt (Headers)" enabled = true perform_staging = false } 200003066 = { signature_id = 200003066 description = "\"/..namedfork/data\" execution attempt (Parameters)" enabled = true perform_staging = false } 200003068 = { signature_id = 200003068 description = "\"/..namedfork/data\" execution attempt (URI)" enabled = true perform_staging = false } }
Create a signatures.tf file with a map to all the attack signatures defined, previously:
variable "signatures" { type = map(object({ signature_id = number enabled = bool perform_staging = bool description = string })) } data "bigip_waf_signatures" "map_qa" { provider = bigip.qa for_each = var.signatures signature_id = each.value["signature_id"] description = each.value["description"] enabled = each.value["enabled"] perform_staging = each.value["perform_staging"] } data "bigip_waf_signatures" "map_prod" { provider = bigip.prod for_each = var.signatures signature_id = each.value["signature_id"] description = each.value["description"] enabled = each.value["enabled"] perform_staging = each.value["perform_staging"] }
This example defines two different maps: one for the test environment BIG-IP and one for the production environment BIG-IP, because the
bigip_waf_signatures
data source are linked to their BIG-IP for consistency. Unlike the parameters and URLs data sources, which are just “json payload generators”, the attack signature data sources must first read the existence of the signature IDs and their status on the BIG-IP, before applying a configuration change.Update the main.tf file:
resource "bigip_waf_policy" "s4_qa" { provider = bigip.qa application_language = "utf-8" partition = "Common" name = "scenario4" template_name = "POLICY_TEMPLATE_FUNDAMENTAL" type = "security" policy_import_json = data.http.scenario4.body signatures = [ for k,v in data.bigip_waf_signatures.map_qa: v.json ] } resource "bigip_waf_policy" "s4_prod" { provider = bigip.prod application_language = "utf-8" partition = "Common" name = "scenario4" template_name = "POLICY_TEMPLATE_FUNDAMENTAL" type = "security" policy_import_json = data.http.scenario4.body }
Plan and apply:
foo@bar:~$ terraform plan -out scenario4 > output_scenario4.2 foo@bar:~$ more output_scenario4.2 foo@bar:~$ terraform apply "scenario4"
Verify the changes
You can verify that the 5 attack signatures have been enabled and enforced on the scenario4 WAF Policy on the QA BIG-IP (first 5 lines in the attack signatures list of the Advanced WAF Policy).
In the application, identify that these last changes on the test device have introduced some false positives. Using the log events on the F5 BIG-IP Advanced WAF GUI, identify the following:
The attack signature “200101558” should be disabled globally
The attack signature “200003068” should be disabled for the “/U1” URL
The attack signaure “200003067” should be enabled globally but disabled specifically for the parameter “P1”.
Finalize changes in the test environment
Do the following, to final changes before moving into a production environment:
inputs.auto.tfvars¶1signatures = { 2 200101559 = { 3 signature_id = 200101559 4 description = "src http: (Header)" 5 enabled = true 6 perform_staging = false 7 } 8 200101558 = { 9 signature_id = 200101558 10 description = "src http: (Parameter)" 11 enabled = false 12 perform_staging = false 13 } 14 200003067 = { 15 signature_id = 200003067 16 description = "\"/..namedfork/data\" execution attempt (Headers)" 17 enabled = true 18 perform_staging = false 19 } 20 200003066 = { 21 signature_id = 200003066 22 description = "\"/..namedfork/data\" execution attempt (Parameters)" 23 enabled = true 24 perform_staging = false 25 } 26 200003068 = { 27 signature_id = 200003068 28 description = "\"/..namedfork/data\" execution attempt (URI)" 29 enabled = true 30 perform_staging = false 31 } 32}
parameters.tf¶1data "bigip_waf_entity_parameter" "P1" { 2 name = "P1" 3 type = "explicit" 4 data_type = "alpha-numeric" 5 perform_staging = true 6 signature_overrides_disable = [200003067] 7 //url = data.bigip_waf_entity_url.U1 8}
urls.tf¶1data "bigip_waf_entity_url" "U1" { 2 name = "/U1" 3 type = "explicit" 4 perform_staging = false 5 signature_overrides_disable = [200003068] 6}
Update the main.tf file:
main.tf¶1resource "bigip_waf_policy" "s4_qa" { 2 provider = bigip.qa 3 application_language = "utf-8" 4 partition = "Common" 5 name = "scenario4" 6 template_name = "POLICY_TEMPLATE_FUNDAMENTAL" 7 type = "security" 8 policy_import_json = data.http.scenario4.body 9 signatures = [ for k,v in data.bigip_waf_signatures.map_qa: v.json ] 10 parameters = [data.bigip_waf_entity_parameter.P1.json] 11 urls = [data.bigip_waf_entity_url.U1.json] 12} 13 14resource "bigip_waf_policy" "s4_prod" { 15 provider = bigip.prod 16 application_language = "utf-8" 17 partition = "Common" 18 name = "scenario4" 19 template_name = "POLICY_TEMPLATE_FUNDAMENTAL" 20 type = "security" 21 policy_import_json = data.http.scenario4.body 22 signatures = [ for k,v in data.bigip_waf_signatures.map_prod: v.json ] 23 parameters = [data.bigip_waf_entity_parameter.P1.json] 24 urls = [data.bigip_waf_entity_url.U1.json] 25}
Plan and apply:
foo@bar:~$ terraform plan -out scenario4 > output_scenario4.3 foo@bar:~$ more output_scenario4.3 foo@bar:~$ terraform apply "scenario4"
What’s Next?