on
24-Jul-2022
16:00
- edited on
01-Aug-2022
21:31
by
JRahm
The continued rise of cyber attacks is demanding more from security teams within organizations. Zero-day exploits that affected the likes of Log4j and Spring4Shell have left operation teams scrambling to ensure their WAF policies are up-to-date and capable of mitigating attacks targeting those exploits. To be better prepared for future incidents, one avenue is to automate the process of deploying and updating WAF policies, both time-consuming processes for organizations with a large number of applications and WAF appliances.
With the introduction of F5 OneWAF, organizations have additional justification to invest in automating WAF policy configuration. OneWAF's aim to have a unified WAF policy definition across the F5 WAF portfolio helps ensure much of the automation efforts can be reused as the WAF delivery mechanism evolves along with your applications, be it through BIG-IP Advanced WAF (AWAF), F5 NGINX App Protect WAF or F5 Distributed Cloud.
In this article, I will provide some technical guidance on how to get started with automating WAF policy configurations on BIG-IP AWAF.
I will be discussing the use of Ansible to automate the WAF policy configurations, as F5 has Ansible collections f5_modules and f5_bigip to provide abstraction to the underlying REST APIs for configuring BIG-IPs. Ansible also supports the Jinja2 templating language which can be used to dynamically generate the content for every WAF policy.
Starting from BIG-IP AWAF v15, WAF or Application Security policies can be defined declaratively in JSON format, which is much more human-readable compared to the XML format used in prior versions. That said, the rest of the article should also be applicable to the XML definition of the WAF policies.
Lastly, from BIG-IP v16 onwards, any WAF policy configured can be exported in JSON format from BIG-IP AWAF Web GUI, should you need a base policy to work with:
In the context of securing the applications with WAF policies, the points above can be represented in the form of WAF policy templates that define much of the required core rule sets, and support customizations for additional rules and behaviours as required.
For organizations already using BIG-IP AWAFs, the existing WAF policies on the BIG-IP AWAFs likely have some common configurations, due to company policies dictating core rule sets. The common configurations can be used as the basis of the WAF policy templates.
For those configuring their first WAF policy, the policy below (credit to Shain Singh) is a good starting point to protect applications from bots and targeted attacks (using Threat Campaign), without generating too many false positives as the broader attack signatures (All Signatures signature set) have been disabled:
{
"policy": {
"name": "api_gateway_policy",
"template": {
"name": "POLICY_TEMPLATE_NGINX_BASE"
},
"applicationLanguage": "utf-8",
"enforcementMode": "blocking",
"blocking-settings": {
"violations": [{
"name": "VIOL_THREAT_CAMPAIGN",
"alarm": true,
"block": true
}
]
},
"signature-sets": [{
"name": "All Signatures",
"block": false,
"alarm": false
}
],
"bot-defense": {
"settings": {
"isEnabled": true
},
"mitigations": {
"classes": [{
"name": "trusted-bot",
"action": "alarm"
}, {
"name": "untrusted-bot",
"action": "block"
}, {
"name": "malicious-bot",
"action": "block"
}
]
}
}
}
}
Whilst the F5 WAF policy schema supports a large number of protection mechanisms, we should aim to reduce the number of variables to ensure the WAF policies are manageable. The sample policy above can be broken down to three sections:
Not every application requires all three protection mechanisms, nor does each mechanism need to behave in the same way for every application. With that in mind, here is an example of a template written in Jinja2:
{
"policy": {
"name": "{{ policy.name }}",
"template": {
"name": "POLICY_TEMPLATE_FUNDAMENTAL"
},
"general": {
"trustXff": "true"
},
{% if policy.threat_campaign %}
"blocking-settings": {
"violations": [{
"name": "VIOL_THREAT_CAMPAIGN",
"alarm": true,
"block": true
}
]
},
{% endif %}
"signature-sets": [{
"name": "All Signatures",
"block": {{ 'block' in policy.all_sigs_actions }},
"alarm": {{ 'alarm' in policy.all_sigs_actions }}
}
],
"bot-defense": {
"settings": {
"isEnabled": true
},
"mitigations": {
"classes": [{
"name": "trusted-bot",
"action": "alarm"
}, {
"name": "untrusted-bot",
"action": "{% if policy.allow_untrusted_bots %}alarm{% else %}block{% endif %}"
}, {
"name": "malicious-bot",
"action": "block"
}
]
}
},
"applicationLanguage": "utf-8",
"enforcementMode": "blocking"
}
}
The protection mechanisms are configurable on a per-policy level with the following Ansible variables:
Below is an example of an Ansible host variable in the inventory, defining two WAF policies with different values for the Ansible variables, to meet different security requirements:
# inventory/bigip-01.yaml
app_sec_policies:
- name: shopping_site_policy
all_sigs_actions: ["alarm", "block"]
threat_campaign: true
allow_untrusted_bots: false
- name: info_api_policy
all_sigs_actions: ["alarm"]
threat_campaign: false
allow_untrusted_bots: true
With the WAF policy templates and inventory defined for every BIG-IP AWAF, the WAF policies can be generated and applied on BIG-IP AWAFs using the bigip_asm_policy_import module in the Ansible playbook:
# Ansible Task looping through app_sec_policies variable, with loop_var="policy"
- name: Create policy JSON file on AWAF
copy:
dest: "/tmp/{{ policy.name }}.json"
content: "{{ policy_template }}"
delegate_to: localhost
- name: Import policy from JSON file
bigip_asm_policy_import:
name: "{{ policy.name }}"
force: true
source: "/tmp/{{ policy.name }}.json"
provider:
server: bigip01.local.net
user: admin
password: secret
delegate_to: localhost
Once the policies have been created on the BIG-IP AWAF, they can then be attached to Virtual Servers. Whenever changes need to be made on the WAF policies, the Ansible playbook can be re-run, and the declarative nature of the API will ensure the configuration on BIG-IP AWAF is updated accordingly.
I hope the article has given you some confidence to take your first steps in introducing automation to securing your application with WAF policies and enabling your organization to react to new threats more effectively.
More importantly, remember to involve all stakeholders along your journey to garner support for when the time comes to roll out the solution, and that your work is aligned with the overarching strategy to stand the test of time.
The "provider" is no longer an option as F5 wants us to use "connection: httpapi" that has predefined variables.
Can I use refence a local file on the Ansible station and push it with bigip_asm_policy_import? I seem to get some errors as I think that the inline: option does not support json files but just XML and this limits the options .
tasks:
- name: Import ASM policy inline
bigip_asm_policy_import:
name: foo-policy4
inline: "{{ lookup('file','asm_policy.json') }}"