3 min read

Palo Alto Firewall configuration backups using the XML API via Python

Palo Alto Firewall configuration backups using the XML API via Python
Photo by Taylor Vick / Unsplash

Apart from monitoring and configuring the firewalls, PAN-OS XML API is also very useful for other day to day tasks initiated on tens or hundreds of firewalls. One of these quick and easy tasks is taking configuration backups via the XML API using Python.

Prerequisites

I'm running all the software on Ubuntu 20.04 server and Python 3.10.2. Lesser versions should work also, though Python < 3 has not been tested (and should not be used anymore anyways).

  • Palo Alto firewalls (PAN-OS 9.0, 9.1, 10.0 and 10.1 tested with these scripts)

Getting Palo Alto configuration backup using Python script

The basics of this script is really simple. Connect to the device, request the configuration backup as XML and save it into a file. More advanced part is the use of threading which allows you to get backups from tens of firewalls simultaneously. Remember to create the backup directory beforehand, or modify the script with OS module to create directories when writing.

Note: You can easily modify the script to save the data anywhere you like. Also getting other data via the XML API can be easily implemented.

PAN-OS Login and API call functions

#!/usr/bin/env python
import json
import logging
import requests
import urllib3
import xml.etree.ElementTree as ET

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

PAApiLoginUrl = "https://{}/api/?type=keygen&user={}&password={}"
PAApiCallUrl = "https://{}/api/?type={}&action=get&xpath={}&key={}"
PAOpApiCallUrl = "https://{}/api/?type={}&cmd={}&key={}"
PAExportApiCallUrl = "https://{}/api/?type={}&category={}&key={}"

# Palo Alto API login function to get the API-key
def pa_login(fwip, fwname, username, password):
    logging.debug('Logging into %s as %s', fwname, username)

    # Login to Palo Alto API and get session cookie
    try:
        r = requests.get(PAApiLoginUrl.format(fwip, username, password), verify=False, timeout=3)
    except:
        logging.warning("Connect to firewall failed: %s", fwname)
        return("error")

    # Check that login results in proper response code
    if r.status_code != 200:
        logging.warning("Login failed to %s - status code: %i", fwname, r.status_code)
        return("error")
    else:
        # If login successful, parse the XML tree and return the apikey
        tree = ET.fromstring(r.content)
        finder = tree.find('.//result')
        apikey = finder[0].text
        return apikey

# Palo Alto API call function
def pa_apicall(fwip, fwname, calltype, cmd, key):
    logging.debug('Querying API on %s', fwname)

    # Call API on proper URL based on calltype
    if calltype == 'config':
        r = requests.get(PAApiCallUrl.format(fwip, calltype, cmd, key), verify=False, timeout=10)
    elif calltype == 'export':
        r = requests.get(PAExportApiCallUrl.format(fwip, calltype, cmd, key), verify=False, timeout=10)
    else:
        r = requests.get(PAOpApiCallUrl.format(fwip, calltype, cmd, key), verify=False, timeout=10)

    if r.status_code != 200:
        logging.info("API call failed at %s - status code: %i", fwname, r.status_code)
        return("error")
    else:
        return r

PAN-OS Backup configuration script

#!/usr/bin/env python
import os
import logging
import requests
import json
import urllib3
import time
import sys
import threading
# Import functions from pa_functions
from pa_functions import pa_login, pa_apicall

logging.basicConfig(level=logging.INFO, format='[%(asctime)s] [%(levelname)s] (%(threadName)-10s) %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
sem = threading.Semaphore()

# Parse JSON function
def parse_json(json_path):
    with open(json_path, 'r') as json_file:
        json_data = json.load(json_file)
    return json_data

# Get Firewall backups function
def getBackup(fwname, fwip, username, password):
    # Get the API key
    key = pa_login(fwip, fwname, username, password)

    # Check that the login was properly done and API key is retrieved
    if "error" in key:
        logging.warning('Error backing up configuration on %s', fwname)
    else:
        # Get the configuration output via API call
        logging.debug('Backing up configuration on %s', fwname)
        output = pa_apicall(fwip, fwname, "export", "configuration", key)

        # Write the data to the backups/ directory with name suffix '-conf'
        sem.acquire()
        with open("backups/" + fwname + "-conf", 'wb') as outfile:
            outfile.write(output.content)
        sem.release()

def main():
    logging.info('Starting Palo Alto configuration backup script...')

    # File to read the firewalls from and parse the JSON
    firewallSource = "firewalls.json"
    fws = parse_json(firewallSource)

    # File to read the config from and parse the JSON
    configSource = "config.json"
    config = parse_json(configSource)

    # Initiate jobs list
    jobs = []

    for fw in fws["firewalls"]:
        # Append backup function calls to threading jobs list
        thread_apicall = threading.Thread(target=getBackup, args=(fw["name"], fw["ip"], config["username"], config["password"]))
        jobs.append(thread_apicall)

    # Start the jobs in list
    for j in jobs:
        j.start()

    # Join the jobs in list
    for j in jobs:
        j.join()

if __name__ == "__main__":
    main()

Example config JSON

{
  "username": "USER",
  "password": "PASS"
}

Example firewall source JSON

{
  "firewalls": [
    {
      "name": "Firewall 1",
      "ip": "192.168.1.1"
    },
    {
      "name": "Firewall 2",
      "ip": "192.168.1.2"
    }
  ]
}

Verify configuration backup

Configuration backup is written (by default) to backups/ directory. Check that the configuration files are being written properly and content is valid.

Conclusion

Following the steps above allows you to grab quick and dirty configuration backupds using the PAN-OS XML API.

All the scripts above can be found in the GIT repository