Last updated on: 2023-10-18 04:01:19.

Scenario #2: Manage an existing WAF policy with Terraform

The goal of this lab is to take an existing F5 BIG-IP Advanced WAF Policy – that has been created and managed on an F5 BIG-IP outside of Terraform – and to import and manage its lifecycle using the F5 BIG-IP Terraform Provider.

If you have multiple WAF policies protecting your applications, then it is likely these WAF policies have evolved over time, complicating ta,king inventory of each policy, each entity, and every attribute. The goal is to import the current policy, making that policy your current baseline. Every new change like, adding server technology, parameters, or attack signatures, you will do using Terraform as an addition or correction of this new baseline policy.

Pre-requisites

On the BIG-IP:

  • Version 16.1 or newer
  • Credentials with REST API access
  • /Common/scenario2 WAF policy (Rapid deployment template) created

On Terraform:

Import a Policy

  1. Create 3 files:

    • variables.tf
    • inputs.tfvars
    • main.tf
    variables.tf
    1
    2
    3
    variable bigip {}
    variable username {}
    variable password {}
    


    inputs.tfvars
    1
    2
    3
    bigip = "10.1.1.9:443"
    username = "admin"
    password = "whatIsYourBigIPPassword?"
    


    main.tf
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    terraform {
      required_providers {
        bigip = {
          source = "F5Networks/bigip"
          version = "1.15"
        }
      }
    }
    provider "bigip" {
      address  = var.bigip
      username = var.username
      password = var.password
    }
    
    resource "bigip_waf_policy" "this" {
      partition            = "Common"
      name                 = "scenario2"
      template_name        = "POLICY_TEMPLATE_RAPID_DEPLOYMENT"
    }
    


    outputs.tf
    1
    2
    3
    4
    5
    6
    7
    8
    output "policyId" {
     value    = bigip_waf_policy.this.policy_id
    }
    
    
    output "policyJSON" {
            value   = bigip_waf_policy.this.policy_export_json
    }
    


  1. Only define the two required attributes of the bigip_waf_policy terraform resource: name and template_name. You MUST provide these attributes in order to manage the resource.

  2. Policy ID is required. Use one of the following ways to get the ID:

    • Check the iControl REST API Endpoint: /mgmt/tm/asm/policies?$filter=name+eq+scenario2&$select=id

    • Get a script example in the lab/scripts/ folder

    • Use a Go code- consult the following Online Go PlayGround example:

      1. Copy the following piece of code in the Go PlayGround.

        // You can edit this code!
        // Click here and start typing.
        package main
        
        import "fmt"
        
        func main() {
          fmt.Println("Hello, 世界")
        }
        
      2. Update the value of the following variables located in the main function:

        var partition string = "Common"
        var policyName string = "scenario2"

      3. Run and get the policy ID.

        package main
        
        import (
            "crypto/md5"
            b64 "encoding/base64"
            "fmt"
            "strings"
        )
        
        func Hasher(policyName string) string {
            hasher := md5.New()
            hasher.Write([]byte(policyName))
            encodedString := b64.StdEncoding.EncodeToString(hasher.Sum(nil))
        
            return strings.TrimRight(encodedString, "=")
        }
        
        func main() {
            var partition string = "Common"
            var policyName string = "scenario2"
        
            fullName := "/" + partition + "/" + policyName
            policyId := Hasher(fullName)
        
            r := strings.NewReplacer("/", "_", "-", "_", "+", "-")
            fmt.Println("Policy Id: ", r.Replace(policyId))
        }
        


Manage the imported policy

Run the following commands to:

  1. Initialize the Terraform Project.
  2. Import the current WAF policy into your state.
  3. Set the JSON WAF Policy as your new baseline.
  4. Configure the lifecycle of your WAF Policy.
foo@bar:~$ terraform init
Initializing the backend...

Initializing provider plugins...
[...]

Terraform has been successfully initialized!

foo@bar:~$ terraform import bigip_waf_policy.this EdchwjSqo9cFtYP-iWUJmw
bigip_waf_policy.this: Importing from ID "EdchwjSqo9cFtYP-iWUJmw"...
bigip_waf_policy.this: Import prepared!
  Prepared bigip_waf_policy for import
bigip_waf_policy.this: Refreshing state... [id=EdchwjSqo9cFtYP-iWUJmw]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.


  1. Update your Terraform main.tf file with the outputs from the following two commands:

    foo@bar:~$ terraform show -json | jq '.values.root_module.resources[].values.policy_export_json | fromjson' > importedWAFPolicy.json
    
    foo@bar:~$ terraform show -no-color
    # bigip_waf_policy.this:
    resource "bigip_waf_policy" "this" {
        application_language = "utf-8"
        id                   = "EdchwjSqo9cFtYP-iWUJmw"
        name                 = "scenario2"
        partition            = "Common"
        policy_export_json   = jsonencode(
            {
                [...]
            }
        )
        policy_id            = "EdchwjSqo9cFtYP-iWUJmw"
        template_name        = "POLICY_TEMPLATE_FUNDAMENTAL"
        type                 = "security"
    }
    
  2. Using the collected data from the Terraform import, you can now update your main.tf file:

    resource "bigip_waf_policy" "this" {
        application_language = "utf-8"
        name                 = "scenario2"
        partition            = "Common"
        policy_id            = "EdchwjSqo9cFtYP-iWUJmw"
        template_name        = "POLICY_TEMPLATE_FUNDAMENTAL"
        type                 = "security"
        policy_import_json   = file("${path.module}/importedWAFPolicy.json")
    }
    

    Note

    The policy_export_json argument was replaced with policy_import_json, pointing to the imported WAF Policy JSON file.

  3. Plan and apply your new project.

    foo@bar:~$ terraform plan -out scenario2
    bigip_waf_policy.this: Refreshing state... [id=EdchwjSqo9cFtYP-iWUJmw]
    
    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
      ~ update in-place
    [...]
    ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    
    Saved the plan to: scenario2
    
    To perform exactly these actions, run the following command to apply:
        terraform apply "scenario2"
    
    foo@bar:~$ terraform apply "scenario2"
    bigip_waf_policy.this: Modifying... [id=EdchwjSqo9cFtYP-iWUJmw]
    bigip_waf_policy.this: Still modifying... [id=EdchwjSqo9cFtYP-iWUJmw, 10s elapsed]
    bigip_waf_policy.this: Modifications complete after 16s [id=EdchwjSqo9cFtYP-iWUJmw]
    
    Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
    
    Outputs:
    
    policyId = "EdchwjSqo9cFtYP-iWUJmw"
    policyJSON = "{[...]}"`
    


Policy lifecycle management

Manage your WAF Policy as done in Scenario #1: Create a WAF Policy.

You can check your WAF Policy on your BIG-IP after each Terraform apply.

Define parameters

  1. 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]
    }
    
  2. Add references to these parameters in the bigip_waf_policy Terraform resource in the main.tf file:

    resource "bigip_waf_policy" "this" {
      [...]
      parameters           = [data.bigip_waf_entity_parameter.P1.json]
    }
    
    foo@bar:~$ terraform plan -out scenario2
    foo@bar:~$ terraform apply "scenario2"
    

Define URLs

  1. Create a urls.tf file:

    data "bigip_waf_entity_url" "U1" {
      name                          = "/URL1"
      description                 = "this is a test for URL1"
      type                        = "explicit"
      protocol                    = "http"
      perform_staging             = true
      signature_overrides_disable = [12345678, 87654321]
      method_overrides {
        allow  = false
        method = "BCOPY"
      }
      method_overrides {
        allow  = true
        method = "BDELETE"
      }
    }
    
    data "bigip_waf_entity_url" "U2" {
      name                        = "/URL2"
    }
    
  2. Add references to this URL in the bigip_waf_policy Terraform resource in the main.tf file:

    resource "bigip_waf_policy" "this" {
      [...]
      urls                 = [data.bigip_waf_entity_url.U1.json, data.bigip_waf_entity_url.U2.json]
    }
    
  3. Run the plan:

    foo@bar:~$ terraform plan -out scenario2
    foo@bar:~$ terraform apply "scenario2"
    


Define attack signatures

  1. Create a signatures.tf file:

    data "bigip_waf_signatures" "S1" {
      signature_id     = 200104004
      description      = "Java Code Execution"
      enabled          = true
      perform_staging  = true
    }
    
    data "bigip_waf_signatures" "S2" {
      signature_id     = 200104005
      enabled          = false
    }
    
  2. Add references to this URL in the bigip_waf_policy Terraform resource in the main.tf file:

    resource "bigip_waf_policy" "this" {
      [...]
      signatures       = [data.bigip_waf_signatures.S1.json, data.bigip_waf_signatures.S2.json]
    }
    
  3. Run the plan:

    foo@bar:~$ terraform plan -out scenario2
    foo@bar:~$ terraform apply "scenario2"
    


What’s Next?