[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

Advertisements
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

[Powershell]Submitting access request to tufin

Background
So here is a real life requirement that when vRealize Orchestrator created a new virtual machine, the ip addresses of the new VM are passed over to a list, another script will be triggered weekly to read from the list, base on the list create the firewall rules so that new servers can access common service.

The common services are these servers 192.168.0.1 – .4, the source address is dynamic, as it depends on your new virtual machine’s ip addresses.

Here’s a sample code to be sent over to Tufin SecureChange using its Ticket API. The workflow element depends on your environment, the workflow id and name need to be changed according to what you set. You can use Postman to test the api and see what is your workflow id. Do not copy the below wholesale, this is just for learning only.

<# Author: Cyrus
Unix server common services script. This is to build a standard firewall rules for new servers
Need to do exception handling so that troubleshooting is easier if the script fails. #>

#ignore certificate validation
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

#script integration. If there is an existing script that returns ip address, remove the Read-Host command and use the function you have.
#$new_server = Read-Host("New Server IP address ")

#to consolidate new servers from a list
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"
}
#store the iteration in an array
$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)
 
    }

$body = @"
    <ticket>
    <subject>Firewall rule for new server</subject>
    <priority>Normal</priority>
    <domain_name>Default</domain_name>
    <workflow>
         <id>154</id>
         <name>Submit a Change Request</name>
         <uses_topology>true</uses_topology>
    </workflow>
    <steps>
         <step>
             <name>Submit Access Request</name>
             <tasks>
                 <task>
                     <fields>
                         <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="multi_access_request">
                             <name>Required Access</name>
                             <access_request>
                                 <users>
                                     <user>Any</user>
                                 </users>
                                 <sources>
                                     $src_ip
                                 </sources>
                                 <destinations>
                                     <destination type="IP">
                                         <ip_address>192.168.0.1</ip_address>
                                         <netmask>255.255.255.255</netmask>
                                     </destination>
                                 </destinations>
                                 <services>
                                     <service type="PROTOCOL">
                                         <protocol>TCP</protocol>
                                         <port>8140</port>
                                     </service>
                                     <service type="PROTOCOL">
                                        <protocol>TCP</protocol>
                                        <port>61613</port>
                                     </service>
                                     <service type="PROTOCOL">
                                        <protocol>TCP</protocol>
                                        <port>8142</port>
                                     </service>
                                 </services>
                            	 <action>Accept</action>
                                 <labels/>
                             </access_request>
                             <access_request>
                                 <users>
                                     <user>Any</user>
                                 </users>
                                 <sources>
                                     $src_ip
                                 </sources>
                                 <destinations>
                                     <destination type="IP">
                                         <ip_address>192.168.0.2</ip_address>
                                         <netmask>255.255.255.255</netmask>
                                     </destination>
                                 </destinations>
                                 <services>
                                     <service type="PROTOCOL">
                                     	<protocol>TCP</protocol>
                                     	<port>111</port>
                                     </service>
                                     <service type="PROTOCOL">
                                        <protocol>TCP</protocol>
                                     	<port>300</port>
                                     </service>
                                     <service type="PROTOCOL">
                                        <protocol>TCP</protocol>
                                     	<port>302</port>
                                     </service>
                                     <service type="PROTOCOL">
                                        <protocol>TCP</protocol>
                                     	<port>304</port>
                                     </service>
                                     <service type="PROTOCOL">
                                        <protocol>TCP</protocol>
                                     	<port>2049</port>
                                     </service>
                                     <service type="PROTOCOL">
                                        <protocol>UDP</protocol>
                                     	<port>2049</port>
                                     </service>
                                     <service type="PROTOCOL">
                                        <protocol>UDP</protocol>
                                     	<port>111</port>
                                     </service>
                                     <service type="PROTOCOL">
                                        <protocol>UDP</protocol>
                                     	<port>300</port>
                                     </service>
                                     <service type="PROTOCOL">
                                        <protocol>UDP</protocol>
                                     	<port>302</port>
                                     </service>
                                     <service type="PROTOCOL">
                                        <protocol>UDP</protocol>
                                     	<port>304</port>
                                     </service>
                                 </services>
                                 <action>Accept</action>
                                 <labels/>
                             </access_request>
                              <access_request>
                                 <users>
                                     <user>Any</user>
                                 </users>
                                 <sources>
                                     $src_ip
                                 </sources>
                                 <destinations>
                                     <destination type="IP">
                                         <ip_address>192.168.0.3</ip_address>
                                         <netmask>255.255.255.255</netmask>
                                     </destination>
                                     <destination type="IP">
                                         <ip_address>192.168.0.4</ip_address>
                                         <netmask>255.255.255.255</netmask>
                                     </destination>
                                 </destinations>
                                 <services>
                                     <service type="PROTOCOL">
                                     	<protocol>UDP</protocol>
                                     	<port>123</port>
                                     </service>
                                 </services>
                                 <action>Accept</action>
                                 <labels/>
                             </access_request>
                         </field>
                       </fields>
                 </task>
             </tasks>
        </step>
    </steps>
    <comments/>
</ticket>
"@

#preparing the header
$usr = "your_username_here"
$pwd = "your_password_here"
$cred = "${usr}:${pwd}"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($cred)
$base64 = [System.Convert]::ToBase64String($bytes)
$basicAuthValue = "Basic $base64"
$headers = @{ Authorization = $basicAuthValue }

Invoke-RestMethod -Uri "https://tufinsecurechange_hostname/securechangeworkflow/api/securechange/tickets" -Method Post -Headers $headers -ContentType "application/xml" -Body $body
Posted in Powershell, Scripting | Tagged , , | Leave a comment

Compare-Object: Find item in file 1 that is not in file2

The objective is to compare two files, one reference list the other is the testing list. My colleague helped me wrote a script to do the comparison, there were many false positives, so here’s his script:

$output = Compare-Object $file1 $file2 -Property ip -IncludeEqual | Where-Object {$_.SideIndicator -eq "<="}
$output | select ip | Export-Csv .\logs\xxx_ip_diff.csv  -NoTypeInformation

The output always give me items i have on both files and some items from file1.

From stackoverflow i have found a solution to this problem.
See here

So based on the solution, I modified the commands like this:

function find_discrepancy($devices) {
    foreach($device in $devices.Keys) {
        $diff = (Import-Csv "C:\Temp\${device}_ip.csv").IP
        Compare-Object $base $diff -IncludeEqual | Where-Object { $_.SideIndicator -eq '<=' } | ForEach-Object { $_.InputObject } | Set-Content "C:\temp\diff_${device}_ip.csv"
        
        }
        
    }

So actually to properly enumerate the objects that has "<=" I need to use the command foreach-object.

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