Export F5 BIG-IP Advanced Firewall Manager rules to Excel with iControl REST
A couple weeks back lttarvina asked in the forums if it was possible to take F5 BIG-IP Advanced Firewall Manager (BIG-IP AFM) rules and export them to a Microsoft Excel workbook with a browser plugin. Possible perhaps with all the advances taking place with WebAssembly , but no current solution that I'm aware of. In this tech tip, I take a stab at an alternative solution using the iControl REST interface with the python BIGREST module.
The iControl REST methods
There are a lot of methods under the larger security umbrella, but for this exercise we'll stick with policy rules, starting with the policy definitions themselves.
https://<big-ip host address>/mgmt/tm/security/firewall/policy
On my system, I created two policies, one for rules in the global context and one for rules in the virtual server context. A request to the iControl REST endpoint above results in this returned data:
{
"kind": "tm:security:firewall:policy:policycollectionstate",
"selfLink": "https://localhost/mgmt/tm/security/firewall/policy?ver=15.1.6.1",
"items": [
{
"kind": "tm:security:firewall:policy:policystate",
"name": "global_policy",
"partition": "Common",
"fullPath": "/Common/global_policy",
"generation": 138,
"selfLink": "https://localhost/mgmt/tm/security/firewall/policy/~Common~global_policy?ver=15.1.6.1",
"rulesReference": {
"link": "https://localhost/mgmt/tm/security/firewall/policy/~Common~global_policy/rules?ver=15.1.6.1",
"isSubcollection": true
}
},
{
"kind": "tm:security:firewall:policy:policystate",
"name": "nerdlife-policy",
"partition": "Common",
"fullPath": "/Common/nerdlife-policy",
"generation": 118,
"selfLink": "https://localhost/mgmt/tm/security/firewall/policy/~Common~nerdlife-policy?ver=15.1.6.1",
"rulesReference": {
"link": "https://localhost/mgmt/tm/security/firewall/policy/~Common~nerdlife-policy/rules?ver=15.1.6.1",
"isSubcollection": true
}
}
]
}
Now that we have the policy names (global_policy and nerdlife-policy) we can query the rules within each policy with a slight change to the method call:
https://<big-ip host address>/mgmt/tm/security/firewall/policy/<global policy name>/rules
A request to this endpoint results in a list of rules in that policy:
{
"kind": "tm:security:firewall:policy:rules:rulescollectionstate",
"selfLink": "https://localhost/mgmt/tm/security/firewall/policy/global_policy/rules?ver=15.1.6.1",
"items": [
{
"kind": "tm:security:firewall:policy:rules:rulesstate",
"name": "no_172_16_4_net",
"fullPath": "no_172_16_4_net",
"generation": 138,
"selfLink": "https://localhost/mgmt/tm/security/firewall/policy/global_policy/rules/no_172_16_4_net?ver=15.1.6.1",
"action": "reject",
"ipProtocol": "any",
"iruleSampleRate": 1,
"log": "yes",
"ruleNumber": "1",
"status": "enabled",
"destination": {},
"source": {
"identity": {},
"addresses": [
{
"name": "172.16.4.0/24"
}
]
}
},
{
"kind": "tm:security:firewall:policy:rules:rulesstate",
"name": "no_192_168_net",
"fullPath": "no_192_168_net",
"generation": 133,
"selfLink": "https://localhost/mgmt/tm/security/firewall/policy/global_policy/rules/no_192_168_net?ver=15.1.6.1",
"action": "reject",
"ipProtocol": "any",
"iruleSampleRate": 1,
"log": "yes",
"ruleNumber": "2",
"status": "enabled",
"destination": {},
"source": {
"identity": {},
"addresses": [
{
"name": "192.168.0.0/16"
}
]
}
}
]
}
In my case it's two rules in the global context. If you have a lot of rules in any policy, you might want to meter your queries with the $top and $select query parameters to avoid issues with the REST interface.
Rinse and repeat for each policy you want to retrieve rules for, and now you have the data you need for the next step: formatting it and exporting to Excel.
The python function then, is shown below:
def get_policy_rules(br, policy):
policy_rules = {}
if policy == 'ALL':
policy_list = [x.properties.get('name') for x in br.load('/mgmt/tm/security/firewall/policy')]
for pol in policy_list:
policy_rules[pol] = [r.properties for r in br.load(f'/mgmt/tm/security/firewall/policy/{pol}/rules')]
else:
policy_rules[policy] = [r.properties for r in br.load(f'/mgmt/tm/security/firewall/policy/{policy}/rules')]
return policy_rules
The two arguments sent to the function are the BIGREST BIG-IP instantiation object and the command-line argument provided by the user for which policy to export. This defaults to all if the argument isn't provided at the command-line. When called, this function queries the appropriate BIG-IP for the appropriate policy rules and returns that data.
The Excel Formatting
This part is pretty straightforward thanks to the awesome XLSXWriter module I've used before for iRules performance testing.
def export_policy_rules(host, rules):
f = f'{host}_Firewall_Rules.xlsx'
workbook = xlsxwriter.Workbook(f)
for pol in rules.items():
worksheet = workbook.add_worksheet(pol[0])
worksheet.write_row(0, 0, ['kind', 'name', 'fullPath', 'generation', 'selfLink', 'action', 'ipProtocol',
'iruleSampleRate', 'log', 'ruleNumber', 'status', 'destination', 'source'])
for row, rule in enumerate(pol[1]):
rule = {key: str(rule[key]) for key in rule.keys()}
worksheet.write_row(row + 1, 0, list(rule.values()), )
worksheet.set_column('A:A', None, None, {'hidden': True})
worksheet.set_column('C:E', None, None, {'hidden': True})
workbook.close()
The process is simple. Name the file with the BIG-IP hostname included and create the workbook, then as I iterate through the policies I write a worksheet in that workbook, starting with the header values from the data (I cheated as I knew all these and did it manually early in the process, but I could have also used refactored to use the native object keys as I did the values in the following steps) and then writing a row for each rule in the policy. Each policy gets its own worksheet, each rule gets its own row. A few of the fields returned in the json data didn't look that important to the rule's functionality, so I hid that in the output, but those columns are there if you need them. One nuance with XLSXWriter I had to work around was the issue with the source and destination fields that had their own curly brackets. These json blobs are natively dictionaries to python, and XLSXWriter doesn't handle dictionaries in source fields very well. So I had to rewrite the data as strings so they wouldn' get interpreted.
The Results
It could be beautified with the XLSXWriter formatting options, but the results turned out great.
Full script is available in this gist. How might you modify this to flesh out the rest of the functionality in a BIG-IP Advanced Firewall Manager configuration? Let me know down in the comments!