[python]Working with Palo Alto firewall API with pan-python module

This is another demonstration on the use of pan-python module. The usage documentation can be found in github.

This is a simple demonstration on using the pan.xapi module from pan-python. Only changes the device configuration time settings. The demo uses the module to set the time zone and ntp primary and secondary addresses. To reduce the number of xml tags required, the xpath is used. The commit is used after the set command is issued, this code sample can be improved with try and except loop to handle exceptions.

My personal opinion this module is actually not required, requests module can give the same result without a lot of coding, this pan-python module would be useful if it has list of xml commands and list of xpaths.

Code sample:

import pan.xapi,time
#documentation on github https://github.com/kevinsteves/pan-python/blob/master/doc/pan.xapi.rst
#xpath can be navigated on PAN OS on this path https://firewall_ip/api/
deviceconfig_system_xpath = "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system"

#define the timezone, can read from a list and enumerate in dictionary
tz = {}
tz['SG'] = 'Asia/Singapore'
tz['JP'] = 'Asia/Tokyo'
tz['US'] = 'US/Pacific'

#Palo Alto credential, can be modified by using an external encrypted list
def get_pan_credentials(username,password):
    cred = {}
    cred['api_username'] = username
    cred['api_password'] = password
    cred['hostname'] = '192.168.1.104'
    return cred

#device configuration setting only time
def set_time(timezone,ntp_primary,ntp_secondary):
    deviceconfig = """
    <timezone>{}</timezone>
    <ntp-servers>
        <primary-ntp-server>
            <ntp-server-address>{}</ntp-server-address>
        </primary-ntp-server>
        <secondary-ntp-server>
            <ntp-server-address>{}</ntp-server-address>
        </secondary-ntp-server>
    </ntp-servers>
""".format(timezone,ntp_primary,ntp_secondary)
    return deviceconfig
                
config = set_time(tz['SG'],'203.123.48.219','128.199.169.185')

#shoud use try and except, if status code is error then break exit with error code.
#this is a demo hence not as good on error handling.
xapi = pan.xapi.PanXapi(**get_pan_credentials('admin','admin'))
xapi.set(xpath=deviceconfig_system_xpath,element=config)
time.sleep(3)
print(xapi.status)
time.sleep(3)
xapi.commit(cmd="<commit></commit>",timeout=10)
print(xapi.status)

Here’s the result:
Snip20171112_8.png

Snip20171112_9

Advertisements
Posted in Python, Scripting | Tagged , , | Leave a comment

[python]working with pan-python module. Module that calls Palo Alto API

I found the example of usage in here. The code will not work on python3. In fact i am not sure why the print(credentials) in the code is necessary, there is no arg passed into the PanXapi which puzzles me why the code will work.

To pass dictionary of arguments into the PanXapi method you will need double asterix.

So here’s the modification for python3, actually i have not tried on python2.7 yet, but i dun think it mattered to me since python3 will be de facto standard henceforth.

import pan.xapi


def get_pan_credentials():
 cred = {}
 cred['api_username'] = "admin"
 cred['api_password'] = "admin"
 cred['hostname'] = "192.168.1.104"
 return cred

credentials = get_pan_credentials()
xapi = pan.xapi.PanXapi(**credentials)
xapi.op(cmd='show system info', cmd_xml=True)
print(xapi.xml_result())

The output is the same as using the requests module to call the REST API directly. the difference with Pan-Python is that passing arguments is made easy.
So here’s the complete output:

<system><hostname>PA-VM</hostname><ip-address>192.168.1.104</ip-address><netmask>255.255.255.0</netmask><default-gateway>192.168.1.1</default-gateway><is-dhcp>yes</is-dhcp><ipv6-address>unknown</ipv6-address><ipv6-link-local-address>fe80::5200:ff:fe01:0/64</ipv6-link-local-address><ipv6-default-gateway /><mac-address>50:00:00:01:00:00</mac-address><time>Sun Nov 12 00:48:47 2017
</time>
<uptime>0 days, 21:49:26</uptime>
<devicename>PA-VM</devicename>
<family>vm</family><model>PA-VM</model><serial>unknown</serial><vm-mac-base>BA:DB:EE:FB:AD:00</vm-mac-base><vm-mac-count>255</vm-mac-count><vm-uuid>3E304129-D98B-4BD4-A3EE-8C74A586B7BA</vm-uuid><vm-cpuid>KVM:23060000FDFB8B07</vm-cpuid><vm-license>none</vm-license><vm-mode>KVM</vm-mode>
<sw-version>8.0.0</sw-version>
<global-protect-client-package-version>0.0.0</global-protect-client-package-version>
<app-version>655-3816</app-version>
<app-release-date>unknown</app-release-date>
<av-version>0</av-version>
<av-release-date>unknown</av-release-date>
<threat-version>0</threat-version>
<threat-release-date>unknown</threat-release-date>
<wf-private-version>0</wf-private-version>
<wf-private-release-date>unknown</wf-private-release-date>
<url-db>paloaltonetworks</url-db>
<wildfire-version>0</wildfire-version>
<wildfire-release-date>unknown</wildfire-release-date>
<url-filtering-version>0000.00.00.000</url-filtering-version>
<global-protect-datafile-version>unknown</global-protect-datafile-version>
<global-protect-datafile-release-date>unknown</global-protect-datafile-release-date><global-protect-clientless-vpn-version>0</global-protect-clientless-vpn-version>
<global-protect-clientless-vpn-release-date>unknown</global-protect-clientless-vpn-release-date>
<logdb-version>8.0.15</logdb-version>
<platform-family>vm</platform-family>
<vpn-disable-mode>off</vpn-disable-mode>
<multi-vsys>off</multi-vsys>
<operational-mode>normal</operational-mode>
</system>
Posted in Python, Scripting | Tagged | Leave a comment

[python]Create firewall objects with Palo Alto API

This is a code example to demonstrate the use of Palo Alto API. Suppose I need to create pool.ntp.org firewall objects. Here’s the code sample:

import dns.resolver, requests
from bs4 import BeautifulSoup as BS

address_group_xpath = "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/address-group"
address_xpath = "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/address"


def create_object(hostname,ip_address):
    create_address = """
    <entry name="{}">
        <ip-netmask>{}</ip-netmask>
    </entry>
    """.format(hostname,ip_address)
    return create_address


def get_key(username,password):
    response = requests.get('https://192.168.1.104/api/?type=keygen&user={}&password={}'.format(username,password), verify=False)
    soup = BS(response.content, 'html.parser')
    # store the data inside the <key>element
    pa_key = soup.find('key').text
    return pa_key


key = get_key("admin","admin")
answers = dns.resolver.query("pool.ntp.org", "A")
fw_objects = []
for rdata in answers:
    hostname = "pool.ntp.org" + "-" + str(rdata)
    fw_objects.append(create_object(hostname,rdata))

for i in range(0,fw_objects.__len__()):
    #print(fw_objects[i])
    requests.post('https://192.168.1.104/api/?type=config&action=set&key={}&xpath={}&element={}'.format(key,address_xpath,fw_objects[i]),verify=False)

The result look like this:
Snip20171112_5.png

Posted in Python, Scripting | Tagged , , | Leave a comment

[python]Create and update object groups

I am experimenting a real life example, when server is created the hostname and ip address are assigned by an orchestrator. The orchestrator will then call the python script and pass the hostname to be object entry name and ip address.

This is the code sample. I referred to this link to understand the xml structure, the PA documentation only mention an example for creating a new rule.

The configuration set api required xpath. To find out the xpath use the https://your_PA_firewall/api/ to navigate Configuration Commands > devices > entry[@name='localhost.localdomain'] > vsys > entry[@name='vsys1'] > address

Here’s the code sample, can modify this to be more dynamic.

import requests, time
from bs4 import BeautifulSoup as bs

address_group_xpath = "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/address-group"
address_xpath = "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/address"

create_address = """
<entry name="member4">
    <ip-netmask>10.0.0.4</ip-netmask>
</entry>
    <entry name="member5">
    <ip-netmask>10.0.0.5</ip-netmask>
</entry>
<entry name="member6">
    <ip-netmask>10.0.0.6</ip-netmask>
</entry>
"""

create_address_group = """
<entry name="group1">
    <static>
        <member>member4</member>
        <member>member5</member>
        <member>member6</member>
    </static>
</entry>
"""

def get_key(username,password):
    response = requests.get('https://192.168.1.104/api/?type=keygen&user={}&password={}'.format(username,password), verify=False)
    soup = bs(response.content, 'html.parser')
    # store the data inside the <key>element
    pa_key = soup.find('key').text
    return pa_key

key = get_key('admin','admin')

requests.post("https://192.168.1.104/api?type=config&action=set&key={}&xpath={}&element={}".format(key,address_xpath,create_address), verify=False)
time.sleep(3)
requests.post("https://192.168.1.104/api/?type=config&action=set&key={}&xpath={}&element={}".format(key,address_group_xpath,create_address_group), verify=False)

You need to commit on the PAN OS UI, either on PA firewall locally or through Panorama, depending the destination of the rest api. For this code sample the destination is directly to firewall.

How it looks like:
Snip20171112_1.png

Snip20171112_4

Posted in Python, Scripting | Tagged , | Leave a comment

[python]Trying the operation command api in Palo Alto Firewall

This code sample uses requests and beautifulsoup4 modules to manipulate data extracted with PA’s REST API. PA only supports xml though.

The requests is to use the GET method when calling REST API, the beautifulsoup4 is to easily extract the content of a xml element.

I encounter some issues when working with xml element that has hypen, example below:

<response status="success">
<result>
<system>
<hostname>PA-VM</hostname>
<ip-address>192.168.1.104</ip-address>
<netmask>255.255.255.0</netmask>
<default-gateway>192.168.1.1</default-gateway>
<is-dhcp>yes</is-dhcp>
<ipv6-address>unknown</ipv6-address>
<ipv6-link-local-address>fe80::5200:ff:fe01:0/64</ipv6-link-local-address>
<ipv6-default-gateway/>
<mac-address>50:00:00:01:00:00</mac-address>
<time>Sat Nov 11 06:25:20 2017</time>
<uptime>0 days, 3:25:59</uptime>
<devicename>PA-VM</devicename>
<family>vm</family>
<model>PA-VM</model>
<serial>unknown</serial>
<vm-mac-base>BA:DB:EE:FB:AD:00</vm-mac-base>
<vm-mac-count>255</vm-mac-count>
<vm-uuid>3E304129-D98B-4BD4-A3EE-8C74A586B7BA</vm-uuid>
<vm-cpuid>KVM:23060000FDFB8B07</vm-cpuid>
<vm-license>none</vm-license>
<vm-mode>KVM</vm-mode>
<sw-version>8.0.0</sw-version>

Notice the ip-address,default-gateway,sw-version, this will be a problem when executing the python code, i could not use a backslash to escape the hypen, fortunately i found beautifulsoup4 has a find method which allows me to get the data of the xml element i want.
Below is the python code sample:

import requests
#use beautifulsoup to parse xml tags easily and effortlessly. Much easier than lxml module
from bs4 import BeautifulSoup as bs

#get the authentication key from PA
response = requests.get('https://192.168.1.104/api/?type=keygen&user=admin&password=admin',verify=False)
soup = bs(response.content,'html.parser')
#store the data inside the <key>element
key = soup.find('key').text
cmd = """
<show><system><info></info></system></show>
"""

r = requests.get("https://192.168.1.104/api/?type=op&cmd={}&key={}".format(cmd,key),verify=False)
output = bs(r.content,'html.parser')
print("The hostname is " + (output.response.result.system.hostname).text)
print("The management ip address and mask is " + (output.find('ip-address')).text + " " + (output.response.result.system.netmask).text)
print("The default gateway is " + (output.find('default-gateway')).text)
print("PAN OS version is " + (output.find('sw-version')).text)

So here’s the output:

The hostname is PA-VM
The management ip address and mask is 192.168.1.104 255.255.255.0
The default gateway is 192.168.1.1
PAN OS version is 8.0.0

Posted in Python, Scripting | Tagged , , | Leave a comment

[Powershell]Obfuscate password

This is a sample script to create a password file, the file is encrypted, however when you need to read from the files you can use it for your script to authenticate so that your script do not need to hardcode the password in plaintext.

$username = "Your_Username"
if ((Test-Path .\password.do) -eq $true) {
    $password = Get-Content ".\password.do" | ConvertTo-SecureString
    $credential = New-Object System.Management.Automation.PSCredential($username,$password)
    } else {
        Read-Host $username -AsSecureString | ConvertFrom-SecureString | Out-File .\password.do
        $password = Get-Content .\password.do | ConvertTo-SecureString
        $credential = New-Object System.Management.Automation.PSCredential($username,$password)
        }
    #$credential.GetNetworkCredential().Password

The credential.GetNetworkCredential().Password will display your encrypted password in plaintext.

This is how password.do looks like:
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000beaf1d362eb1554e96ec3bb2ef9eb7370000000002000000000003660000c000000010000000776a63452de2b5e91bc47f520c2afd1c0000000004800000a000000010000000f971fb271a0c8cf18462794bd9ea709e180000002bf88317c82ecac4c3cbb3c41a7d6f9a4859f67bd65cc56f1400000009a7a391fb283c2829032351cf51f8988801fac8

Posted in Powershell, Scripting | Tagged , | Leave a comment

[Powershell]Concatenate XML blocks base on a list

So on previous post i have posted an entire code to build standard firewalls for new servers. So basically there is a small function that does the trick.

function GatherNewServers($new_server){
$src_addr = @" 
<source type="IP">
    <ip_address>$new_server</ip_address>
    <netmask>255.255.255.255</netmask>
</source>
"@
return $src_addr + "`r"
}
$src_ip = @()

$src_addr = Get-Content "$env:HOMEDRIVE\Powershell\test_ip.txt"
for($i=0; $i -lt $src_addr.Count; $i++) {
    $src = $src_addr.item($i)
    $src_ip += GatherNewServers($src)
 
    }

$src_ip

The idea is that I do not want the orchestrator to send request on every servers built, this will generate a lot of access requests in tufin securechange. so I was trying out a code snippet to see if i can replicate the same xml blocks with different addresses from a list of ip addresses.

Posted in Powershell, Scripting | Tagged | Leave a comment