Zero-touch configuration of secure apps across BIG-IP tenants using CTS

Deploying application workloads by developers or scaling operations traditionally involves submitting tickets, the network team assigning IP addresses, and the security team configuring policies. This siloed approach is time and resource intensive. Today, there are tools and automation to simplify processes and help developers become more independent to deploy and scale applications securely.

In today's blog, we will talk about how we can deploy secure applications with an Advanced Web Application Firewall (AWAF) Policy on a F5 BIG-IP using automation.  We will also look at a scenario where an existing application can be scaled up or scaled down, without any manual interventions. First, let us look at the tools involved.

 

What are the Tools used?

We will be using F5 Application Services Templates (FAST) to deploy application objects and AWAF policy on the BIG-IP. For more details about FAST, refer to https://clouddocs.f5.com/products/extensions/f5-appsvcs-extension/latest/ We will also be using HashiCorp Consul Terraform Sync, Consul HCP, and Terraform Cloud to automate our application management workflows. 

 

How does the solution work?

 

We will use Consul Terraform Sync (CTS) to define the tasks. The tasks in CTS refer to the BIG-IP-Terraform module that has the FAST terraform resource. This is used to push the necessary configurations object whenever services are added or removed on a BIG-IP. The back end application nodes are running the consul agent and are registered to HCP Consul. CTS continuously monitors the HCP Consul catalog for changes.

Let's talk about the CTS file, below is an example CTS file which you can customize and use it.


# Consul Block
 consul {
  address = "localhost:8500"
  service_registration {
    enabled = true
    service_name = "CTS Event AS3 WAF"
    default_check {
      enabled = true
      address = "http://localhost:8558"
   }
}
token = "${consul_acl_token}"
}
# Driver block
driver "terraform-cloud" {
  hostname     = "https://app.terraform.io"
  organization = "SCStest"
  token        = "<token>"
required_providers {
    bigip = {
      source = "F5Networks/bigip"
    }
  }
}

terraform_provider "bigip" {
  address  = "5.2.2.29:8443"
  username = "admin"
  password = "s8!"
}

 task {
  name = "AS3-tenent_AppA"
  description = "BIG-IP example"
  source = "scshitole/consul-sync-multi-tenant/bigip"
  providers = ["bigip"]
  services = ["appA"]
  variable_files = ["tenantA_AppA.tfvars"]
}
 
 task {
  name = "AS3-tenent_AppB"
  description = "BIG-IP example"
  source = "scshitole/consul-sync-multi-tenant/bigip"
  providers = ["bigip"]
  services = ["appB"]
  variable_files = ["tenantB_AppB.tfvars"]
}

Consul Block contains Consul agent address with the port, health check and HCP Consul token information. Driver block consists of terraform cloud details like org name, HCP terraform token and BIG-IP provider. You can also pass the address, username & password for BIG-IP provider using env variables. You can see we have a ‘task’ block in the configuration, this block defines what will be configured on the BIG-IP whenever a service is registered to the HCP Consul Catalog. In the above example, we have service or app ‘AppA’ so whenever CTS sees that service on the HCP Consul catalog it will push the configuration on BIG-IP using the terraform module provided in the source in the task block.

The source in the task block uses terraform module registered at https://registry.terraform.io/modules/scshitole/consul-sync-multi-tenant/bigip/latest and the repository is  at https://registry.terraform.io/modules/scshitole/consul-sync-multi-tenant/bigip/latest

  1. The terraform module uploads the FAST template file into the BIG-IP along with the virtual server and pool configuration.
  2. It also attaches the AWAF policy to the virtual server.
  3. Enables BIG-IP to do service discovery for Service Events on HCP Consul
  4. Creates a new example API endpoint like /mgmt/shared/service-discovery/task/~Consul_SD~Nginx~nginx_pool
  5. Which can be used to configure the pool member using the terraform resource "bigip_event_service_discovery” to add or remove the pool members

Below is the example Fast template YAML

title: Consul Service Discovery
description: This template will create a virtual server that will use event driven service discovery
definitions:
  tenant:
    title: Name of tenant
    description: give a unique name for this tenant
  app:
    title: Application
    description: give a unique name for this application
  defpool:
    title: pool defination
    description: should follow format poolname_pool only
  virtualAddress:
    title: Virtual Address
    description: IP addresses of virtual addresses (will create 80/443)
  virtualPort:
    title: Virtual Port
    description: Port that will be used
    type: integer
parameters:
  virtualAddress: 10.0.0.200
  virtualPort: 8080
  tenant: "Consul_SD"
  app: "Nginx"
  defpool: "nginx_pool"
template: |
  {
    "class": "AS3",
    "action": "deploy",
    "persist": true,
    "declaration": {
        "class": "ADC",
        "schemaVersion": "3.0.0",
        "id": "urn:uuid:940bdb69-9bcd-4c5c-9a34-62777210b581",
        "label": "Consul Webinar",
        "remark": "Consul Webinar",
        "{{tenant}}": {
          "class": "Tenant",
          "{{app}}": {
              "class": "Application",
              "{{app}}_vs": {
                "class": "Service_HTTP",
                "virtualPort": {{virtualPort}},
                "virtualAddresses": [
                    "{{virtualAddress}}"
                ],
                "pool": "{{defpool}}",
                "policyWAF": {
                        "use": "Arcadia_WAF_API_policy"
                    },
                "persistenceMethods": [],
                "profileMultiplex": {
                "bigip": "/Common/oneconnect"
              }
              },
              "Arcadia_WAF_API_policy": {
                    "class": "WAF_Policy",
                    "url": "https://gist.githubusercontent.com/scshitole/7b7cdcfbd48797d90769ae587324cc9b/raw/6f7a9be072230685956f84652312b3c7e153c6cf/WAFpolicy.json",
                    "ignoreChanges": true
                },
              "{{defpool}}": {
                "class": "Pool",
                "monitors": [
                    "http"
                ],
                "members": [{
              "servicePort": 80,
              "addressDiscovery": "event"
            }]
              }
          }
        }
    }
  }

and here is the module main.tf file


terraform {
  required_providers {
    bigip = {
      source  = "f5networks/bigip"
      version = "~> 1.15.0"
    }
  }
}

locals {
  tenant_params = jsonencode({
    "tenant": var.tenant,
    "app": var.app,
    "virtualAddress": var.virtualAddress,
      "defpool": var.defpool,
      "virtualPort": var.virtualPort
  })
}

# generate zip file

data "archive_file" "template_zip" {
  type        = "zip"
 source_file = "${path.module}/template/ConsulWebinar.yaml"
  output_path = "${path.module}/template/ConsulWebinar.zip"
}

# deploy fast template

resource "bigip_fast_template" "consul-webinar" {
  name = "ConsulWebinar"
  source = "${path.module}/template/ConsulWebinar.zip"
  md5_hash = filemd5("${path.module}/template/ConsulWebinar.zip")
  depends_on = [data.archive_file.template_zip]
}

resource "bigip_fast_application" "nginx-webserver" {
  template        = "ConsulWebinar/ConsulWebinar"
  fast_json   =  local.tenant_params
  depends_on = [bigip_fast_template.consul-webinar]
}


locals {

  # Create a map of service names to instance IDs
  service_ids = transpose({
    for id, s in var.services : id => [s.name]
  })

  # Group service instances by name
  grouped = { for name, ids in local.service_ids :
    name => [
      for id in ids : var.services[id]
    ]
  }

}
resource "bigip_event_service_discovery" "event_pools" {
  for_each = local.service_ids
  taskid   = "~${var.tenant}~${var.app}~${each.key}_pool"
  dynamic "node" {
    for_each = local.grouped[each.key]
    content {
      id   = node.value.node_address
      ip   = node.value.node_address
      port = node.value.port
    }
  }
depends_on = [bigip_fast_application.nginx-webserver]
}


 

Configuring Multi-tenancy

You can configure multiple tenants with the AWAF policy on the virtual server for each tenant by just updating the f5nia.hcl file with additional tasks and defining new app.tfvars file for each of the tasks. The app.tfvars file will define parameters like the name for the tenant on BIG-IP, application name, Virtual Server IP, Pool name, and BIG-IP credentials. You can configure multiple tenants with AWAF policy attached to the Virtual server by defining a new task in the Consul terraform Sync file.

tenant="tenant_AppB"
app="AppB"
virtualAddress="10.0.0.202"
virtualPort=8080
defpool="appB_pool"
address="5.7.14.4"
username="admin"
password="PxxxxayJ"
port=8443

“tenant” is the name of the partition on BIG-IP where the application objects are residing

“app” name of the application which is running

“virtualAddress” is the virtual server address

“defpool” is the name of the Pool, it must follow app name and “_pool”

 address, username, password, and port to access BIG-IP

You can also customize the AWAF policy as per your needs and package it into the FAST Yamal template, the example AWAF policy is shown below

{
    "policy": {
        "name": "policy-api-arcadia",
        "description": "Arcadia API",
        "template": {
            "name": "POLICY_TEMPLATE_API_SECURITY"
        },
        "enforcementMode": "blocking",
        "server-technologies": [
            {
                "serverTechnologyName": "MySQL"
            },
            {
                "serverTechnologyName": "Unix/Linux"
            },
            {
                "serverTechnologyName": "MongoDB"
            }
        ],
        "signature-settings": {
            "signatureStaging": false
        },
        "policy-builder": {
            "learnOnlyFromNonBotTraffic": false
        }
    }
}

 

How to use the repository?

  1. Git clone https://github.com/f5businessdevelopment/f5_hcp_consul.git
  2. Change dir to  cd f5_hcp_consul
  3. Copy terraform.tfvars.example to terraform.tfvars  and customize as per your setup
  4. Issue terraform plan & apply
  5.  F5 BIG-IP, backend applications AppA, AppB as well as jump instance is created on AWS
  6. You can see the services on HCP Consul
  7. Update BIG-IP with latest AS3 app services rpm, Fast template and enable ASM
  8. ssh to the jump box and change dir to “cts” also issue sudo su
  9. Update the *.tfvars file and f5nia.hcl as per your setup
  10. Start the CTS service by issuing “consul-terraform-sync config-file=f5nia.hcl”
  11. The above step will upload the FAST template and configure BIG-IP for Service discovery events and monitor HCP Consul for the Apps.
  12. The assumption is you are using HCP Consul, Terraform Cloud, and Enterprise Consul Terraform Sync

Detail video is available at https://youtu.be/11ssWNyVjF0.

You can find additonal integrations of F5 BIG-IP and  HashiCorp Tools  at this link

 

Conclusion:

In dynamic, multi-tenant application environments, change management can become cumbersome and error prone. FAST templates on the BIG-IP provide a fantastic way to abstract configuration objects and enable simpler automation – which can help ops teams and developers to cut down application deployment times. Using FAST with service discovery and automation, can facilitate zero-touch configuration management for your applications workloads.

 

 

 

 

 

 

 

 

 

 

 

 

Updated Oct 17, 2022
Version 3.0
No CommentsBe the first to comment