BIG-IP ASM Automation with Ansible
My Background
Back in September I started my Ansible journey, coming from no knowledge about Ansible and its automation capabilities I was asked to develop some code/playbooks to automate some of the BIG-IP's ASM functions for AnsibleFest 2019.
I was pleasantly surprised on how easy it was to install Ansible, build playbooks and deliver the correct end-state for the BIG-IP. The playbooks and automation took me back down memory lane to when I was creating a universal network bootable Norton Ghost CD in DOS for all of the different models of PCs my work owned.
The team I work for (Business Development) has been working hard at making sure our code is easily accessible to customers through GitHub. Our goal is to provide the necessary tools such as F5 Automation Sandbox and use-cases so that even if you are new to Ansible, or a die-hard coder with Ansible that there is a place for you to test, consume and bring life to the code.
What is BIG-IP ASM?
F5 BIG-IP® Application Security Manager™ (ASM) is a flexible web application firewall that secures web applications in traditional, virtual, and private cloud environments. BIG-IP ASM helps secure applications against unknown vulnerabilities, and enables compliance for key regulatory mandates. BIG-IP ASM is a key part of the F5 application delivery firewall solution, which consolidates traffic management, network firewall, application access, DDoS protection, SSL inspection, and DNS security.
What is Ansible?
Ansible is a radically simple IT automation engine that automates cloud provisioning, configuration management, application deployment, intra-service orchestration, and many other IT needs. Designed for multi-tier deployments since day one, Ansible models your IT infrastructure by describing how all of your systems inter-relate, rather than just managing one system at a time. It uses no agents and no additional custom security infrastructure, so it's easy to deploy - and most importantly, it uses a very simple language (YAML, in the form of Ansible Playbooks) that allow you to describe your automation jobs in a way that approaches plain English.
What does the Code Do?
IP Blocking - In ASM, there is a feature called IP address intelligence that can allow or block IP addresses from being able to access protected applications. This code creates a Virtual IP (VIP) and a blank ASM policy attached to that VIP. After the creation the code exports the ASM Policy into an XML format and is then modified by the code snip-it below to add blocked IP addresses and re-import that policy over the existing one. Prior to this snip-it we have code that checks to see if the IP address already exists for things like re-runs of the code and blocks duplicate IP addresses from being added to the XML.
This is a snip-it of the Code where it modifies the ASM Policy XML File (this was exported in previous steps in the code)
#Import Additional Disallowed IPs - name: Add Disallowed IPs xml: path: "{{ ASM_Policy_File }}" pretty_print: yes input_type: xml insertafter: yes xpath: /policy/geolocation add_children: "<whitelist><ip_address>{{ item.item }}</ip_address><subnet_mask>255.255.255.255</subnet_mask><policy_builder_trusted>false</policy_builder_trusted><ignore_anomalies>false</ignore_anomalies><never_log>false</never_log><block_ip>Always</block_ip><never_learn>false</never_learn><description>blocked</description><ignore_ip_reputation>false</ignore_ip_reputation></whitelist>" with_items: "{{ Blocked_IP_Valid.results }}" when: Blocked_IPs is defined and item.rc == 1
Here is a demonstration of an IP being blocked and unblocked by the BIG-IP ASM Policy.
Disallowed URL Filtering - Another feature of ASM is the ability to disallowed URLs, this can be useful when working internally vs. externally and there are other reasons to why a specific URL would be blocked or protected by BIG-IP ASM. This code can be used independently, cooperatively, or not at all with this playbook. Since this playbook is merged with the IP Blocking code it follows the same flow (exporting/importing XML and error checking) as previously mentioned in the IP Blocking to ensure no duplicates are made in the XML.
This is a snip-it of the Code where it modifies the ASM Policy XML File (this was exported in previous steps in the code)
#Import Additional Disallowed URLs - name: Add Disallowed URLs xml: path: "{{ ASM_Policy_File }}" input_type: xml pretty_print: yes xpath: /policy/urls/disallowed_urls add_children: - "<url protocol=\"HTTP\" type=\"explicit\" name=\"{{ item.item }}\"/>" - "<url protocol=\"HTTPS\" type=\"explicit\" name=\"{{ item.item }}\"/>" with_items: "{{ Blocked_URLs_Valid.results }}" when: Blocked_URLs is defined and item.rc == 1
Here is a demonstration of specific URLs being blocked by the BIG-IP ASM Policy. (Note: the File Name in the repo has been changed but does the same use-case )
Where can you access the Playbook for this integration?
https://github.com/f5devcentral/f5-bd-ansible-usecases/tree/master/03-F5-WAF-Policy-Management
How to get all of the use-cases currently available.
https://github.com/f5devcentral/f5-bd-ansible-usecases
Want to try it out but need a Lab to work in? Try out our F5 Automation Sandbox built for AWS!
https://clouddocs.f5.com/training/automation-sandbox/
- Matt_MabisEmployee
- I have submitted an update for those links, thanks for letting me know 🙂
- amolariCirrostratus
The github links are 404. Could you please update them?
- Matt_BystrzakNimbostratus
Thanks Matt. I am currently using ipaddr via a jinja2 template to try to build the list dynamically every time based on the existing list plus the additional entries submitted by the user.
It's a two part question I suppose. The ipaddr function solves the part where it's determining whether or not the IP entered is a valid IP and is NOT already in say a range that's already defined, so thanks for that. I'll have to get netaddr installed in our production environment to support this function.
The second part to that is maintaining an up to date version of the list in csv format for the AFM IPI function to pull in and parse. I'm attempting to do this all within jinja because of the "advanced" logic but it's becoming unwieldy(as jinja can do) and I was hoping that there was either a different approach to IPI whitelisting / blacklisting based on IP address, or some magical solution via the playbook itself where it might be cleaner. An example might be something like using a data-group on the F5 for the list of whitelisted IP addresses instead of a feedlist(or as the list itself). I think though, because I'm trying to handle file management plus error checking it's getting out of hand. Maybe I have to bust out python and do some pre-processing or something like that. I think I may have taken us down a rabbit hole and for that, I apologize.
Thanks!
- Matt_MabisEmployee
Hey Matt,
To answer your question i didn't build in any integrity type code to validate if it was a real IP, i did build in code however to determine if the IP or URL had already been inputted into the XML this was to prevent duplication's which would break the XML import.
What i might recommend if you are looking to validate like if its an IPv4 Address or IPv6 you could use the "ipaddr filter (https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters_ipaddr.html) " and register a variable like (is_valid_ip) and run individual checks, might be able to be done in loops and array where your array list of is_valid_ip compares at the same slot as the IP address and only injects when valid.
Hopefully this is what you were asking, feel free to reach out again if this isnt what you are looking for.
Also i have added another set of use-cases to the repository (use-case 6) where Use-case 6 is the same code built into a role (7 is same but with our provisioner uses the juice-shop web front)
Feel free to respond back if i didnt get to your question correctly :)
- Matt_BystrzakNimbostratus
Hey Matt,
Thanks for the article. This is a great example of how to work with the ASM policies via Ansible.
I have a couple questions pertaining to managing whitelist IP addresses. I'm currently working on presenting a front end through a ticketing system so that application folks can request an IP address be whitelisted as sort of a self service type of thing. That system will signal Ansible to start the magic show. The struggle I'm currently having is how I handle sanitizing input and assuring that what is being requested isn't an invalid entry.
My first example is for IPI whitelist or blacklist. I'm managing a CSV file essentially and it's hosted by a web server that the F5 is pulling in as a feed list. Straight forward stuff. But I'm trying to update the contents of the CSV file in a way that maintains the integrity of the function. I'm open to approaching this problem differently, but I'm kind of stuck at the moment.
I know this isn't exactly what you're talking about here, but I think that the same issue ends up showing up. What is your mechanism for maintaining integrity, or how might you approach managing the state of your list? I'm still relatively new to all the Ansible-isms, so forgive my ignorance if you're somehow handling that in this example and I'm just missing it.
Any insights you could provide are greatly appreciated.
- Matt_MabisEmployee
Plenty of more to come!!! Glad you liked it :)
- AndreiaCirrus
"The playbooks and automation took me back down memory lane to when I was creating a universal network bootable Norton Ghost CD in DOS for all of the different models of PCs my work owned." This part moved me a lot! thanks for the article!