Manage F5 BIG-IP Advanced WAF Policies with Terraform (Part 4 - Policy Lifecycle management)

The goal of this article is to present how to best manage your F5 BIG-IP Advanced WAF policy entities and attack signatures.

 

Table of Content

 

Introduction

Once a policy is created, imported or migrated you’re not done. The F5 BIG-IP Advanced WAF Policy lives with the Application, should evolve with it and adapt to its potential attack surface changes.

Many changes can be done on the F5 BIG-IP Advanced WAF Policy

  • adding a parameter with its attribute
  • adding a URL
  • adding a FileType
  • adding Attack Signatures


Adding a parameter

here is the link to the terraform data source documentation.
I always recommend splitting resources into easy to find and easy to manage files. Here, we will put all the parameters attributes into a dedicated “parameters.tf” file.

Create a parameters.tf file:

data "bigip_waf_entity_parameter" "P1" {
	name = "Parameter1"
	type = "explicit"
	data_type = "alpha-numeric"
	perform_staging = true
	signature_overrides_disable = [200001494, 200001472]
}

This P1 parameter is an alphanumeric explicit parameter positioned at the global level with two attack signatures disabled on it.

To apply it to the F5 BIG-IP Advanced WAF Policy, you only have to reference it in the “parameters” list of the bigip_waf_policy resource.

resource "bigip_waf_policy" "this" {
  [...]
  parameters = [data.bigip_waf_entity_parameter.P1.json]
}

Because each entity is decoupled from the F5 BIG-IP Advanced WAF Policy and explicitly referenced into it makes it a very easy way to add, roll-back, remove or update parameters definitions in your F5 BIG-IP Advanced WAF Policy.
That’s also a very handy way to declare a parameter, attach it to a testing F5 BIG-IP Advanced WAF Policy before moving it to production or attach it to multiple F5 BIG-IP Advanced WAF Policies.

Now, once you have a large number of parameters, it could become difficult to maintain a long list of “data.bigip_waf_entity_parameter.*.json » references.

So, if you want to make it easier to read and manage, you can create a “parameters.auto.tfvars” with the following content:

Parameters = {
	P1 = {
		name = "Parameter1"
		type = "explicit"
		data_type = "alpha-numeric"
		perform_staging = true
	}
	P2 = {
		name = "Parameter2"
		type = "wildcard"
		data_type = "alpha-numeric"
		perform_staging = false
		signature_overrides_disable = [200001494, 200001472]
	}
	P3 = {
		name = "Parameter3"
		type = "explicit"
		data_type = "alpha-numeric"
		is_header = true
		sensitive_parameter = true
		perform_staging = true
	}
}

Your parameters.tf file should look like:

variable "parameters" {
	type = map(object({
		name = string
		type = string
		data_type = string
		is_header = bool
		sensitive_parameter = bool
		perform_staging = bool
		signature_overrides_disable = list(number)
		description = (optional(string))
	}))
}


data "bigip_waf_entity_parameter" "map_params" {
	for_each = var.parameters
		name = each.value["name"]
		type = each.value["type"]
		data_type = each.value["data_type"]
		is_header = each.value["is_header"]
		sensitive_parameter = each.value["sensitive_parameter"]
		perform_staging = each.value["perform_staging"]
		signature_overrides_disable = each.value["signature_override_disable"]
		description = each.value["description"]
}

And your F5 BIG-IP Advanced WAF Policy terraform resource is :

resource "bigip_waf_policy" "s4_qa" {
  …
  parameters = [for k,v in data.bigip_waf_entity_parameter.map_params: v.json]
  …
}

And it does not change regardless of the number of parameters listed in your parameters.auto.tfvars.

 


Adding an URL

here is the link to the terraform data source documentation.

Same as with the parameters, we will use a dedicated "urls.tf" file.

Create a urls.tf file:

 

data "bigip_waf_entity_url" "url1" {
  name                        = "/url1"
  description                 = "managed by terraform"
  type                        = "explicit"
  protocol                    = "HTTP"
  perform_staging             = true
  signature_overrides_disable = [12345678, 87654321]
  method_overrides {
    allow  = false
    method = "BCOPY"
  }
  method_overrides {
    allow  = true
    method = "BDELETE"
  }
}

 

This is an explicit HTTP url with attack signatures exceptions (disabling  123445678 and 87654321) and with overriding on BCPOY and BDELETE methods.

To apply it to the WAF Policy, you only have to reference it in the “urls” list of the bigip_waf_policy resource.

 

resource "bigip_waf_policy" "this" {
  [...]
  urls = [data.bigip_waf_entity_url.url1.json]
  [...]
}

 

Same way we did with the parameters, we can ease the insertion of the urls in the F5 BIG-IP Advanced WAF Policy by creating an automatic input file for the URLs:

 

urls = {
  url1 = {
     name                        = "/url1"
     type                        = "explicit"
     protocol                    = "HTTPS"
     description                 = "login page"
     perform_staging             = true
  }
  url2 = {
     name                        = "url2"
     type                        = "wildcard"
     description                 = "logout page"
     perform_staging             = false
     protocol                    = HTTPS
     method                      = "*"
     signature_overrides_disable = [200001494, 200001472]
  }
  url3 = {
     name                        = "/url3"
     type                        = "explicit"
     description                 = "this is URL3"
     is_header                   = true
     protocol                    = HTTPS
     perform_staging             = false
     signature_overrides_disable = [12345678, 87654321]
     method_overrides {
        allow  = false
        method = "DELETE"
     }
  }
}

 

Your urls.tf file should look like:

 

data "bigip_waf_entity_url" "map_urls" {
  for_each = var.parameters
    name                        = each.value["name"]
    description                 = each.value["description"]
    type                        = each.value["type"]
    protocol                    = each.value["protocol"]
    method                      = each.value["method"]
    perform_staging             = each.value["perform_staging"]
    signature_overrides_disable = each.value["signature_override_disable"]
    method_overrides            = each.value["method_overrides"]
}

 

And your F5 BIG-IP Advanced WAF Policy terraform resource is :

resource "bigip_waf_policy" "this" {
  …
  urls = [for k,v in data.bigip_waf_entity_url.map_urls: v.json]
  …
}

And it does not change regardless of the number of parameters listed in your parameters.auto.tfvars.

 

Adding attack signatures

signatures.auto.tfvars

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
    }
}

 

 Now, we should create the terraform maps for the signatures.

please note that, unlike the "parameters" and "urls" data sources, the "signatures" data sources are "connected", which means that we need here to specify the terraform provider (the BIG-IP address and the credentials). This is because, the terraform needs to read the BIG-IP configuration, to check the attack signature presence and to collect the documentation from it. 

variable "signatures" {
  type = map(object({
        signature_id    = number
        enable          = bool
        perform_staging	= bool
        description     = string
  }))
}

data "bigip_waf_signatures" "map_signatures" {
  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"]
}

finally, we need to update the bigip_waf_policy terraform resource:

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 ]
}

 

Demo Video

 

Manage F5 BIG-IP Advanced WAF Policies with Terraform (Part 4 - Policy Lifecycle management)
 

 

Published Sep 23, 2022
Version 1.0

Was this article helpful?

No CommentsBe the first to comment