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.

Python Object Oriented Programming (OOP) - with examples
As you start your journey in Python programming, you might wonder, “If we already have functions in Python for bundling and reusing code, why do we even need Objects and Classes?”

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.

Palo Alto Create Bulk Address Objects using Pan-OS Python SDK
In this blog post, we’ll see how to configure bulk Address-Objects at once and then add them to an Address-Group using the pan-os-python Library.

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.

  1. 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.
  2. Rulebase Object - The Rulebase object can be seen as a chapter within the book but a smaller section inside the book.
  3. 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.

💡
Please note that this step doesn't create the rules on the live firewall yet.

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.

💡
At this point, you might be noticing that we're using the 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}
💡
The 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()
Palo Alto PAN-OS SDK for Python - Working with Panorama
In this blog post, we’ll explore how to use the pan-os-python library with Panorama. Working with Panorama is a bit different because of device-groups and templates.

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.

References

Palo Alto Networks PAN-OS SDK for Python — Palo Alto Networks PAN-OS SDK for Python 1.0.0.b1 documentation
pan-os-python SDK for Palo Alto Networks: Harnessing Object-Oriented Programming
Welcome back to our “Python for Cybersecurity” series. In this second installment, we delve into the principles of Object-Oriented Programming (OOP) and how…