Table of contents
This is a continuation of Part 2 you can find here:
Intro
In the previous episode of the series (Part 2) we covered how to identify and replace duplicated objects. This time we will focus our attention on detecting duplicated address groups.
Case Study Overview
For the Part 3, I’ve slightly modified test configuration. In Address Groups section, three new groups appeared; ‘Apple‘, ‘Pizza’ and ‘BigBang’:
‘Apple‘ and ‘Banana‘ groups are duplicated. They contain different objects, but those objects have the same IP assigned, so after running the script both ‘Apple‘ and ‘Banana‘ will have the same object as a member.
‘BigBang’ and ‘Pizza‘ are groups containing other address group as a member, along with some address objects.
New Address Groups are used within Two new security Rules; ‘Jandoo’ and ‘Astraeus Cloud‘:
Script Building
Here is the code developed in Part 1 & Part 2. I’ve highlighted lines 46, 47 and 48 – there, we will put our code developed in this blog post.
# Import Section
from panos.firewall import Firewall
from panos.objects import AddressObject, AddressGroup
from panos.policies import Rulebase, SecurityRule
import datetime
start = datetime.datetime.now()
# Login Details
HOSTNAME = '10.20.30.94'
USERNAME = 'bot'
PASSWORD = 'BotPassword123'
# Initiate firewall object
fw = Firewall(HOSTNAME, USERNAME, PASSWORD)
# Retrieve Data from the firewall
original_objects = AddressObject.refreshall(fw, add=False)
rulebase = fw.add(Rulebase())
security_rules = SecurityRule.refreshall(rulebase)
original_groups = AddressGroup.refreshall(fw, add=False)
# Cleaning Section
unique_objects = original_objects.copy()
duplicates = []
duplicates_replacement = {}
# Find Duplicated Address Objects
for a in unique_objects:
for b in unique_objects:
if (a.value == b.value) and (a.name != b.name) and (a not in duplicates):
duplicates.append(b)
duplicates_replacement[b.name] = {"Value": b.value, "ReplaceWith": a.name}
fw.add(b)
unique_objects.remove(b)
# Fix for a problem in duplicates_replacement
for k, v in duplicates_replacement.items():
if a.value == v['Value'] and a.name != v['ReplaceWith']:
v['ReplaceWith'] = a.name
# Find Duplicated Address Objects in Address Groups
for og in original_groups:
for duplicate in duplicates:
if duplicate.name in og.static_value:
og.static_value[og.static_value.index(duplicate.name)] = duplicates_replacement[duplicate.name]['ReplaceWith']
og.apply()
# Start of Part 3 Section Here
# End of Part 3 Section Here
# Check rules for duplicates usage
modified_rules = []
for rule in security_rules:
for duplicate in duplicates:
if duplicate.name in rule.source:
print("Duplicate '{}' detected as a source in the rule '{}'".format(duplicate.name, rule.name))
if duplicates_replacement[duplicate.name]['ReplaceWith'] in rule.source:
rule.source.remove(duplicate.name)
else:
rule.source[rule.source.index(duplicate.name)] = duplicates_replacement[duplicate.name]['ReplaceWith']
if rule not in modified_rules:
modified_rules.append(rule)
if duplicate.name in rule.destination:
print("Duplicate '{}' detected as a destination in the rule '{}'".format(duplicate.name, rule.name))
if duplicates_replacement[duplicate.name]['ReplaceWith'] in rule.destination:
rule.destination.remove(duplicate.name)
else:
rule.destination[rule.destination.index(duplicate.name)] = duplicates_replacement[duplicate.name]['ReplaceWith']
if rule not in modified_rules:
modified_rules.append(rule)
# Apply Section
if len(modified_rules) > 0:
modified_rules[0].apply_similar()
print("*** Applied Changes to the Security Rules. ***")
if len(duplicates) > 0:
duplicates[0].delete_similar()
print("*** Removed duplicated address objects. ***")
print("### Cleaning took: {} ###".format(datetime.datetime.now() - start))
Finding Duplicated Address Groups
Here we will detect all address groups with the same values. The mentioned values are basically the group members. We need to take two groups, compare their members and if group members are the same we need to remove duplicate from the list of original groups and put it into duplictates list.
Before running any comparision code, we need to have our ‘storage’ prepared, our list and dictionary were information about duplicates will be kept.
# Part 3
group_duplicates = []
group_duplicates_rep = {}
We are ready to take group_a and group_b from original_groups list do something with them. Maybe let’s begin with a simple print() that will print out group names to the screen. Let’s see it in action in ipython:
Now let’s embeed if statement into our for loop to printout only groups that have the same members. Before we build if statement logic, we need to check how a particular address group object is build in python. In ipython run:
We know how the address group is structured so now we are able to build a logical statement that will check if the address groups have the same value.
First of all we need to make sure to not compare the same groups. For example: at the beginning of for loop runtime, Banana address group will be assigned to variable group_a and the same Banana address group will be assigned to another variable group_b. If this will happen our if statement must return False, because otherwise it will detect a duplicate – a false-positive duplicate. Because address group names must have unique values, we can use them to check if variables group_a and group_b are the same objects. First part of if statement must check if name of the group_a is diffrenet from name of the group_b. So the first part of if statement will be:
(group_a.name != group_b.name)
In the next part of the if statement logic we must check if group_a and group_b values (group members) are the same. We’ve seen already that address group has a static_value field. So we need to take group_a.static_value and compare it with group_b.static_value. The second part of if statement will be:
(group_a.static_value == group_b.static_value)
The last part of the if statement is to check if the group has been already detected as duplicate. Essentialy we need to check if group_b isn’t in group_duplicates list. So the third part of if statement will be:
(group_b not in group_duplicates)
If we combine above and put logical and between them, we will get:
(group_a.name != group_b.name) and (group_a.static_value == group_b.static_value) and (group_b not in group_duplicates)
If all 3 logical groups returns True, our if statement is also True – this means we have detected a duplicate. Let’s run python code and printout group names only when there is a duplicate in group_a and group_b pair:
That is better, our output is much smaller. We detected duplicates in Banana – Apple pair and in Apple – Banana pair… Yes, there are the same pairs, just in different configuration. To prevent this we need to remove a duplicate from a original_groups list. Adding one line of code will result with the following:
Perfect! Now all we have to do is remove print() function and replace it with 3 lines of code that will do the following:
- add duplicate address groups to the group_duplicates lists
- add information about the duplicate and the replacetent to group_duplicates_rep dictionary
- add duplicate address group to the firewall object (this will let us remove duplicate from the firewall config)
Implementation of above actions in python should look like this:
Below I will post Part 3 code we developed so far. It inclueds a fix for duplicates replacement that I covered already in Part 1.
# Part 3
group_duplicates = []
group_duplicates_rep = {}
for group_a in original_groups:
for group_b in original_groups:
if (
(group_a.name != group_b.name) and \
(group_a.static_value == group_b.static_value) and \
(group_b not in group_duplicates)):
group_duplicates.append(group_b)
group_duplicates_rep[group_b.name] = {"Value": group_b.static_value, "ReplaceWith": group_a.name}
fw.add(group_b)
original_groups.remove(group_b)
# Fix for a problem in duplicates_replacement
for k, v in group_duplicates_rep.items():
if (group_a.static_value == v['Value']) and (group_a.name != v['ReplaceWith']):
v['ReplaceWith'] = group_a.name
Detecting Duplicated Groups As Other Group Members
The task here is to take our original_groups and check if duplicated groups, we identified previously, exists as the members. Again we need to iterate over original_groups and group_duplicates and check if duplicate.name is in original_groups.static_value.
for og in original_groups:
for duplicate in group_duplicates:
if duplicate.name in og.static_value:
# do something
Let’s run above code in ipython and if if statement will return true, we will print a message to a screen.
Oh, we have something. When duplicate is detected we need to perform few actions (instead of just printing a message to a console):
- replace duplicate group with a replacement object
- add modified group to the firewall object
- apply changes to the candidate config
- remove modified (and applied) group from the firewall object
In python code it will look like
# Part 3
group_duplicates = []
group_duplicates_rep = {}
for group_a in original_groups:
for group_b in original_groups:
if (
(group_a.name != group_b.name) and \
(group_a.static_value == group_b.static_value) and \
(group_b not in group_duplicates)):
group_duplicates.append(group_b)
group_duplicates_rep[group_b.name] = {"Value": group_b.static_value, "ReplaceWith": group_a.name}
fw.add(group_b)
original_groups.remove(group_b)
# Fix for a problem in duplicates_replacement
for k, v in group_duplicates_rep.items():
if (group_a.static_value == v['Value']) and (group_a.name != v['ReplaceWith']):
v['ReplaceWith'] = group_a.name
# Find Duplicated Address Groups in Address Groups
for og in original_groups:
for duplicate in group_duplicates:
if duplicate.name in og.static_value:
og.static_value[og.static_value.index(duplicate.name)] = group_duplicates_rep[duplicate.name]['ReplaceWith']
fw.add(og)
og.apply()
fw.remove(og)
Update Security Rules
So now that we have identified our duplicated address groups, we need to replace them in security policy rules. Because this action is very similar to the previously performed in the Part 1, I will simply put updated “# Check rules for duplicates usage” section below:
# Check rules for duplicates usage
modified_rules = []
for rule in security_rules:
# Change duplicated address objects in the security rules
for duplicate in duplicates:
if duplicate.name in rule.source:
print("Duplicate '{}' detected as a source in the rule '{}'".format(duplicate.name, rule.name))
if duplicates_replacement[duplicate.name]['ReplaceWith'] in rule.source:
rule.source.remove(duplicate.name)
else:
rule.source[rule.source.index(duplicate.name)] = duplicates_replacement[duplicate.name]['ReplaceWith']
if rule not in modified_rules:
modified_rules.append(rule)
if duplicate.name in rule.destination:
print("Duplicate '{}' detected as a destination in the rule '{}'".format(duplicate.name, rule.name))
if duplicates_replacement[duplicate.name]['ReplaceWith'] in rule.destination:
rule.destination.remove(duplicate.name)
else:
rule.destination[rule.destination.index(duplicate.name)] = duplicates_replacement[duplicate.name]['ReplaceWith']
if rule not in modified_rules:
modified_rules.append(rule)
# Change duplicated address groups in the security rules
for grp_duplicate in group_duplicates:
if grp_duplicate.name in rule.source:
print("Duplicate '{}' detected as a source in the rule '{}'".format(grp_duplicate.name, rule.name))
if group_duplicates_rep[grp_duplicate.name]['ReplaceWith'] in rule.source:
rule.source.remove(grp_duplicate)
else:
rule.source[rule.source.index(grp_duplicate.name)] = group_duplicates_rep[grp_duplicate.name]['ReplaceWith']
if rule not in modified_rules:
modified_rules.append(rule)
if grp_duplicate.name in rule.destination:
print("Duplicate '{}' detected as a destination in the rule '{}'".format(grp_duplicate.name, rule.name))
if group_duplicates_rep[grp_duplicate.name]['ReplaceWith'] in rule.destination:
rule.destination.remove(grp_duplicate)
else:
rule.destination[rule.destination.index(grp_duplicate.name)] = group_duplicates_rep[grp_duplicate.name]['ReplaceWith']
if rule not in modified_rules:
modified_rules.append(rule)
Update Live Device
And finally we have to update our live device candidate configuration with modifications we introduced.
# Apply Section
if len(modified_rules) > 0:
modified_rules[0].apply_similar()
print("*** Applied Changes to the {} Security Rules. ***".format(len(modified_rules)))
if len(group_duplicates) > 0:
group_duplicates[0].delete_similar()
print("*** Removed {} duplicated address groups ***".format(len(group_duplicates)))
if len(duplicates) > 0:
duplicates[0].delete_similar()
print("*** Removed {} duplicated address objects. ***".format(len(duplicates)))
Test
Before running any tests, let’s post complete code we created so far, with highlighted lines we created/updated in this Part:
# Import Section
from panos.firewall import Firewall
from panos.objects import AddressObject, AddressGroup
from panos.policies import Rulebase, SecurityRule
import datetime
start = datetime.datetime.now()
# Login Details
HOSTNAME = '10.20.30.94'
USERNAME = 'bot'
PASSWORD = 'BotPassword123'
# Initiate firewall object
fw = Firewall(HOSTNAME, USERNAME, PASSWORD)
# Retrieve Data from the firewall
original_objects = AddressObject.refreshall(fw, add=False)
rulebase = fw.add(Rulebase())
security_rules = SecurityRule.refreshall(rulebase)
original_groups = AddressGroup.refreshall(fw, add=False)
# Cleaning Section
unique_objects = original_objects.copy()
duplicates = []
duplicates_replacement = {}
# Find Duplicated Address Objects
for a in unique_objects:
for b in unique_objects:
if (a.value == b.value) and (a.name != b.name) and (a not in duplicates):
duplicates.append(b)
duplicates_replacement[b.name] = {"Value": b.value, "ReplaceWith": a.name}
fw.add(b)
unique_objects.remove(b)
# Fix for a problem in duplicates_replacement
for k, v in duplicates_replacement.items():
if a.value == v['Value'] and a.name != v['ReplaceWith']:
v['ReplaceWith'] = a.name
# Find Duplicated Address Objects in Address Groups
for og in original_groups:
for duplicate in duplicates:
if duplicate.name in og.static_value:
og.static_value[og.static_value.index(duplicate.name)] = duplicates_replacement[duplicate.name]['ReplaceWith']
og.apply()
# Start of Part 3 Section Here
# Part 3
group_duplicates = []
group_duplicates_rep = {}
for group_a in original_groups:
for group_b in original_groups:
if (
(group_a.name != group_b.name) and \
(group_a.static_value == group_b.static_value) and \
(group_b not in group_duplicates)):
group_duplicates.append(group_b)
group_duplicates_rep[group_b.name] = {"Value": group_b.static_value, "ReplaceWith": group_a.name}
fw.add(group_b)
original_groups.remove(group_b)
# Fix for a problem in duplicates_replacement
for k, v in group_duplicates_rep.items():
if (group_a.static_value == v['Value']) and (group_a.name != v['ReplaceWith']):
v['ReplaceWith'] = group_a.name
# Find Duplicated Address Groups in Address Groups
for og in original_groups:
for duplicate in group_duplicates:
if duplicate.name in og.static_value:
og.static_value[og.static_value.index(duplicate.name)] = group_duplicates_rep[duplicate.name]['ReplaceWith']
fw.add(og)
og.apply()
fw.remove(og)
# End of Part 3 Section Here
# Check rules for duplicates usage
modified_rules = []
for rule in security_rules:
# Change duplicated address objects in the security rules
for duplicate in duplicates:
if duplicate.name in rule.source:
print("Duplicate '{}' detected as a source in the rule '{}'".format(duplicate.name, rule.name))
if duplicates_replacement[duplicate.name]['ReplaceWith'] in rule.source:
rule.source.remove(duplicate.name)
else:
rule.source[rule.source.index(duplicate.name)] = duplicates_replacement[duplicate.name]['ReplaceWith']
if rule not in modified_rules:
modified_rules.append(rule)
if duplicate.name in rule.destination:
print("Duplicate '{}' detected as a destination in the rule '{}'".format(duplicate.name, rule.name))
if duplicates_replacement[duplicate.name]['ReplaceWith'] in rule.destination:
rule.destination.remove(duplicate.name)
else:
rule.destination[rule.destination.index(duplicate.name)] = duplicates_replacement[duplicate.name]['ReplaceWith']
if rule not in modified_rules:
modified_rules.append(rule)
# Change duplicated address groups in the security rules
for grp_duplicate in group_duplicates:
if grp_duplicate.name in rule.source:
print("Duplicate '{}' detected as a source in the rule '{}'".format(grp_duplicate.name, rule.name))
if group_duplicates_rep[grp_duplicate.name]['ReplaceWith'] in rule.source:
rule.source.remove(grp_duplicate)
else:
rule.source[rule.source.index(grp_duplicate.name)] = group_duplicates_rep[grp_duplicate.name]['ReplaceWith']
if rule not in modified_rules:
modified_rules.append(rule)
if grp_duplicate.name in rule.destination:
print("Duplicate '{}' detected as a destination in the rule '{}'".format(grp_duplicate.name, rule.name))
if group_duplicates_rep[grp_duplicate.name]['ReplaceWith'] in rule.destination:
rule.destination.remove(grp_duplicate)
else:
rule.destination[rule.destination.index(grp_duplicate.name)] = group_duplicates_rep[grp_duplicate.name]['ReplaceWith']
if rule not in modified_rules:
modified_rules.append(rule)
# Apply Section
if len(modified_rules) > 0:
modified_rules[0].apply_similar()
print("*** Applied Changes to the {} Security Rules. ***".format(len(modified_rules)))
if len(group_duplicates) > 0:
group_duplicates[0].delete_similar()
print("*** Removed {} duplicated address groups ***".format(len(group_duplicates)))
if len(duplicates) > 0:
duplicates[0].delete_similar()
print("*** Removed {} duplicated address objects. ***".format(len(duplicates)))
print("### Cleaning took: {} ###".format(datetime.datetime.now() - start))
Firewall configuration is reverted to the running – so we are in initial state, ready for a test. Let’s run our code in ipython:
Here are the results:
- Apple group was removed from the configuration
- Apple group was replaced by Banana group in Pizza Group
- Apple group was replaced by Banana group in Astraeus Cloud
Summary
That’s it for this blog post. Not sure what will be in the next Part, maybe service objects & groups and NAT rules.
If you are reading this sentence, you probably reached to the end of this post. Thank you for your attention! Leave a comment if you like 😉
Follow us on Twitter and Instagram.
Krzysztof.
Leave a Reply