Palo Alto PAN-OS SDK for Python
At first glance, the pan-os-python library seemed tricky to me. I was more comfortable using Palo Alto's REST-API for its simplicity. However, as I continued to learn more about Object-Oriented Programming, the workings of the pan-os-python library became clearer. What once seemed complex and hard to understand, began to make sense.
Funny how things change - I'm now writing a blog post about this very library that I used to avoid. In this post, I'll share my experiences with the pan-os-python library and how it can be used to automate Palo Alto firewalls.
Prerequisite
Let's start learning the pan-os-python
library with a simple script. This script will be our foundation, and it's as straightforward as it gets, creating a basic firewall rule on a Palo Alto firewall.
However, before we delve into the scripting aspect, it's important to have a solid understanding of Object-Oriented Programming (OOP). For anyone new to OOP or needing a quick refresher, I recommend checking out my other blog post linked below. It provides a detailed yet easy-to-understand explanation of OOP and its working principles.
Having this knowledge under your belt will make understanding the pan-os-python library and its interaction with Palo Alto firewalls much simpler.
pan-os-python - What does it do?
The pan-os-python
library is a Python tool for automating the management of Palo Alto firewalls. It's a way to programmatically control and configure your firewall's settings without needing to do it manually.
Think of this library as a bridge between your Python code and your Palo Alto Networks firewall. It converts your code into commands that the firewall can understand and execute.
You can use it to create or delete rules, modify the firewall's configuration, retrieve information about the current settings, and much more.
One of the key things to remember about pan-os-python
is that it's designed around the principles of Object-Oriented Programming (OOP). This means that everything in pan-os-python
- whether it's a firewall, a rule, or an address - is represented as an object in Python. This design makes it easier to work with the components of a firewall, as you can manipulate them as Python objects rather than needing to understand the underlying API calls.
Installing the library is as easy as running the pip install
command.
pip install pan-os-python
Collecting pan-os-python
Using cached pan_os_python-1.11.0-py2.py3-none-any.whl (146 kB)
Collecting pan-python<0.18.0,>=0.17.0
Using cached pan_python-0.17.0-py2.py3-none-any.whl (59 kB)
Installing collected packages: pan-python, pan-os-python
Successfully installed pan-os-python-1.11.0 pan-python-0.17.0
Create a Firewall Rule
In this section, we're going to use the pan-os-python
library to create a simple firewall rule. We'll start with a basic script, then go over it line by line to understand exactly what it does.
from panos.firewall import Firewall
from panos.policies import Rulebase, SecurityRule
fw_object = Firewall('my-firewall', 'admin', 'admin')
rules_object = fw_object.add(Rulebase())
new_rule_object = SecurityRule(
name='Allow DNS',
fromzone=['any'],
tozone=['any'],
source=['any'],
destination=['8.8.8.8'],
application=['dns'],
service=['application-default'],
action='allow'
)
rules_object.add(new_rule_object)
new_rule_object.create()
1. Importing necessary modules
from panos.firewall import Firewall
from panos.policies import Rulebase, SecurityRule
This section of the script imports the required classes from the pan-os-python
library. Firewall
Class is used to establish a connection to the Palo Alto firewall. Rulebase
and SecurityRule
are classes that represent a rulebase and a security rule on the firewall, respectively.
2. Creating a Firewall object
fw_object = Firewall('my-firewall', 'admin', 'admin')
Here, an instance of the Firewall
class is created, named fw_object
. The arguments 'my-firewall'
, 'admin'
, 'admin'
correspond to the firewall's hostname/IP address and the username and password for the firewall, respectively. The Firewall
object will be used to communicate with the actual firewall.
3. Creating a Rulebase object and associating it with the Firewall object
rules_object = fw_object.add(Rulebase())
This line first creates an instance of the Rulebase
class, and then associates it with the Firewall
object using the add()
method. The add()
method establishes a parent-child relationship, with fw_object
being the parent and the new Rulebase
object being the child.
4. Creating a SecurityRule object
new_rule_object = SecurityRule(
name='Allow DNS',
fromzone=['any'],
tozone=['any'],
source=['any'],
destination=['8.8.8.8'],
application=['dns'],
service=['application-default'],
action='allow'
)
Here, a SecurityRule
object named new_rule_object
is created. The parameters for SecurityRule
are used to define the properties of the new rule, such as its name, source, destination, and action.
Please note that Rulebase
is used to represent the rulebase of the firewall and SecurityRule
is used to create a new security rule.
5. Associating the SecurityRule object with the Rulebase object
rules_object.add(new_rule_object)
This line associates the SecurityRule
object with the Rulebase
object using the add()
method. Just like how the rulebase was added to the firewall, the rule is added to the rulebase. This reflects that a security rule is a part of a rulebase on a firewall.
There are a lot of add()
we've seen so far. To simplify this, let's use an analogy using a book.
- Firewall Object - Consider the Firewall object as the book itself. This book is an entity that contains various chapters. It's the main structure that houses everything.
- Rulebase Object - The Rulebase object can be seen as a chapter within the book but a smaller section inside the book.
- SecurityRule Object - The SecurityRule object can be compared to a sentence or paragraph within a chapter. It's a specific piece of information or a rule inside the chapter (Rulebase). Just as a chapter consists of many sentences or paragraphs, a Rulebase consists of many SecurityRules.
So, to sum up this analogy,
- The book (Firewall object) contains various chapters (Rulebase objects).
- A chapter (Rulebase object) in the book (Firewall object) consists of many sentences or paragraphs (SecurityRule objects).
- Each sentence or paragraph (SecurityRule object) is part of a chapter (Rulebase object), which in turn is a part of the book (Firewall object).
In the context of your Python script, creating and adding a Firewall object, Rulebase object, and SecurityRule object mimics this book-chapter-sentence structure. The add()
function establishes this hierarchical relationship.
6. Create the SecurityRule object to the firewall
new_rule_object.create()
The create()
method sends an API request to the actual firewall to create the new rule in the rulebase. This is where the SecurityRule
object defined in your script is translated into an actual security rule on the firewall.
View Firewall Rules
Now that we know how to create a new rule, in this example, let's see how to view an existing rule.
from panos.firewall import Firewall
from panos.policies import Rulebase, SecurityRule
import pprint
fw_object = Firewall('my-firewall', 'admin', 'admin')
rules_object = fw_object.add(Rulebase())
one_rule = rules_object.add(SecurityRule('Allow DNS'))
one_rule.refresh()
pprint.pprint(one_rule.about())
Similar to the previous example, we first create a Firewall
object that represents our firewall. Then we add a Rulebase
object to our Firewall
object.
We then add a SecurityRule
object to our Rulebase
object using the rule name 'Allow DNS'. This is like saying, "Within our rulebase, we want to look at the 'Allow DNS' rule". If the 'Allow DNS' rule exists in the rulebase, the SecurityRule
object will represent that rule.
To get the details of this rule from the actual firewall, we call the refresh()
method on our SecurityRule
object. The refresh()
method sends a request to the firewall to retrieve the current settings of the 'Allow DNS' rule. If the rule exists in the firewall's rulebase, the SecurityRule
object (representing this rule in our script) is updated with the rule's current settings.
SecurityRule
class differently from how we used it earlier. When we created a new firewall rule, we used the add()
method on a SecurityRule
object. But in this section, where we're fetching information about an existing rule, we're using the refresh()
method instead.Lastly, we use pprint.pprint(one_rule.about())
to print out the details of the 'Allow DNS' rule. The about()
method returns a dictionary containing all the properties of the SecurityRule
object. By passing this dictionary to pprint.pprint()
, we get a well-formatted, easy-to-read output of the rule's details.
{'action': 'allow',
'application': ['dns'],
'category': ['any'],
'data_filtering': None,
'description': None,
'destination': ['8.8.8.8'],
'destination_devices': ['any'],
'disable_server_response_inspection': None,
'disabled': None,
'file_blocking': None,
'fromzone': ['any'],
'group': None,
'group_tag': None,
'hip_profiles': None,
'icmp_unreachable': None,
'log_end': None,
'log_setting': None,
'log_start': None,
'name': 'Allow DNS',
'negate_destination': None,
'negate_source': None,
'negate_target': None,
'schedule': None,
'service': ['application-default'],
'source': ['any'],
'source_devices': ['any'],
'source_user': ['any'],
'spyware': None,
'tag': None,
'target': None,
'tozone': ['any'],
'type': 'universal',
'url_filtering': None,
'uuid': '6768ec9c-e25b-4163-88ad-c3d796d0221c',
'virus': None,
'vulnerability': None,
'wildfire_analysis': None}
about()
method returns a dictionary that provides the details about the specific object instance on which it is called. In the context of a SecurityRule
object, when we call the about()
method, it returns a dictionary with all the properties of the rule such as its name, source, destination, action, etc.Create Address Objects
In this next section, we're shifting gears to create address objects. We'll walk through a simple example, breaking down each step to show how easy it is to automate this task using the pan-os-python
library.
from panos.firewall import Firewall
from panos.objects import AddressObject
fw_object = Firewall('my-firewall', 'admin', 'admin')
new_address = AddressObject(
name='server_1',
value='192.168.10.10/32',
type='ip-netmask',
description='This is server_1 IP'
)
fw_object.add(new_address)
new_address.create()
The AddressObject
class from the panos.objects
module is used to create a new address object. In this case, an object named server_1
is created with the value 192.168.10.10/32
. The type
parameter is set to ip-netmask
, indicating that the value is an IP address with a netmask (type can also be an fqdn
), and a description
is also added.
Once the AddressObject
is defined, it's added to the Firewall
object using fw_object.add(new_address)
. This line connects the address object we defined in our script with the actual firewall.
Finally, new_address.create()
is called to send a request to the firewall to create the new address object in its configuration.
Using pan-os-python with Panorama
Working with Panorama involves an additional level of management compared to a standalone Palo Alto Networks firewall. In Panorama, you typically specify a Device Group or Template to work with before creating or modifying resources.
Let's examine an example script using the pan-os-python
library with Panorama
from panos.panorama import Panorama, DeviceGroup
from panos.objects import AddressObject
panorama_object = Panorama('my-panorama', 'admin', 'admin' )
dg_object = DeviceGroup("test_dg")
panorama_object.add(dg_object)
new_object = AddressObject(
name='apple_fqdn',
value='apple.com',
type='fqdn',
description='Apple FQDN'
)
dg_object.add(new_object)
new_object.create()
In this script, we start by importing the necessary modules, including Panorama
and DeviceGroup
from panos.panorama
, and AddressObject
from panos.objects
.
Next, we create a Panorama
object that represents our Panorama. We then create a DeviceGroup
object for the device group test_dg
that we want to work with. This DeviceGroup
object is added to the Panorama
object.
Then we define a new AddressObject
and add this AddressObject
to our DeviceGroup
object, indicating that we want to create this address object in the test_dg
device group.
Finally, we call create()
on our AddressObject
to send a request to Panorama to create the new address object in the specified device group.
This script illustrates how to use the pan-os-python
library with Panorama. The overall concept is still the same as working with a standalone firewall, but with an added layer of specifying a Device Group or Template before creating or modifying resources.
Conclusion
In conclusion, the pan-os-python
is a useful library for managing Palo Alto firewalls. Through examples, we've seen how it simplifies the creation of rules and address-objects. While it may seem complex at first, with some patience and practice, it becomes a lot easier to use.
So far we've only covered add()
, refresh()
and create()
methods. I will try to cover the rest in the upcoming posts. If you want to learn more, feel free to check out the official guide below.