├── README.md ├── pan-qb.py ├── pan-rcli-nopass.py ├── pan-rcli.py ├── srrm-monitor-public.py └── url-check.py /README.md: -------------------------------------------------------------------------------- 1 | # Palo Alto Networks Toolbox 2 | 3 | This will be a repository of scripts written to handle some checks and other things on 4 | on the Palo Alto Networks firewalls. 5 | 6 | ## url-check.py 7 | This script will query the PANDB cloud with data from a flat text file and return the 8 | categories in a CSV. This is a CLI executed script that utilizes argparse, requests, 9 | and the lxml library. 10 | 11 | ## pan-rcli.py 12 | This script will allow you to execute commands on a remote Palo Alto Networks firewall 13 | and then read back the responses. You can feed it either a single command or a text 14 | file of commands. It should be noted that the commands are executed as is, there is no 15 | logic in place to check the validity of the commands. 16 | 17 | ## pan-qb.py 18 | This script will perform a quick backup of a firewall where there is a known API key to 19 | make life easier. If anyone wants to update it to auto-gen an API key from a fed username 20 | and password. 21 | 22 | ## srrm-monitor-public.py 23 | This script will hit the API to pull stats around the "show running resource-monitor" command 24 | and pull data from the DP's of firewalls. 25 | -------------------------------------------------------------------------------- /pan-qb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import requests 4 | import argparse 5 | requests.packages.urllib3.disable_warnings() 6 | 7 | 8 | ''' 9 | This is a quick script to grab a backup a firewall with a known API key. Next iteration will pull a username and 10 | password as needed to gen the API key on it's own for use. This was a quick script though used out of a private 11 | server with cron to pull these. 12 | 13 | - Tighe Schlottog tschlottog@paloaltonetworks.com 14 | ''' 15 | 16 | 17 | def parse_args(): 18 | parser = argparse.ArgumentParser(description='Quick Backup of onboard firewall configuration') 19 | parser.add_argument('-fw', type=str, help='IP Address of Firewall') 20 | parser.add_argument('-k', '--api_key', type=str, help='API Key with access to Firewall') 21 | parser.add_argument('-out', type=str, help='Output file where the configuration should be written') 22 | parser.add_argument('-fwlist', type=str, help='CSV file of firewalls to backup') 23 | args = parser.parse_args() 24 | return parser, args 25 | 26 | 27 | def pull_backup(fw, api_key, outfile): 28 | config_out = open(outfile, 'w') 29 | backup_headers = {'type': 'export', 'key': api_key, 'category': 'configuration'} 30 | backup_req = requests.get('https://%s/api' % fw, params=backup_headers, verify=False) 31 | config_out.write(backup_req.content) 32 | config_out.close() 33 | 34 | 35 | def pull_firewalllist(fwlist): 36 | with open(fwlist, 'r') as fwdata: 37 | for line in fwdata: 38 | (fw, apikey) = line.split(',') 39 | pull_backup(fw, apikey, '%s.cfg' % fw.replace('.', '_')) 40 | 41 | 42 | def control(): 43 | backup_parser, backup_args = parse_args() 44 | if backup_args.fw and backup_args.api_key and backup_args.out: 45 | pull_backup(backup_args.fw, backup_args.api_key, backup_args.out) 46 | elif backup_args.fwlist: 47 | pull_firewalllist(backup_args.fwlist) 48 | else: 49 | backup_parser.print_help() 50 | 51 | 52 | if __name__ == '__main__': 53 | control() -------------------------------------------------------------------------------- /pan-rcli-nopass.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import paramiko 3 | import sys 4 | from datetime import datetime 5 | import argparse 6 | 7 | __author__ = 'Tighe Schlottog || workapegmailcom' 8 | 9 | def parse_args(): 10 | ''' 11 | This function will parse the command line arguments passed in via ARGV. 12 | :return: 13 | ''' 14 | parser = argparse.ArgumentParser(description='Script to remotely execute CLI commands on Palo Alto Networks firewalls') 15 | parser.add_argument('-fw', '--firewall', type=str, help='IP address of remote Firewall') 16 | parser.add_argument('-u', '--user', type=str, help='Remote Firewall Username') 17 | parser.add_argument('-p', '--password', type=str, help='Remote Firewall Password') 18 | parser.add_argument('-cmd', type=str, help='Remote Command to be run surrounded by double quotes. For example "show system info".') 19 | parser.add_argument('-cf', '--command_file', type=str, help='Read commands to be executed remotely from a defined file') 20 | parser.add_argument('-stdout', dest='stdout', action='store_true', help='Output command data to STDOUT') 21 | parser.add_argument('-debug', dest='debug', action='store_true', help='Enable additional debugging') 22 | parser.set_defaults(stdout=False, debug=False) 23 | parser.add_argument('-f', '--file', type=str, help='Output command data to named file') 24 | args = parser.parse_args() 25 | 26 | return parser, args 27 | 28 | 29 | def parse_command_file(command_file): 30 | ''' 31 | This function will parse the lines of the command file, excluding the comment lines starting with #, based on the 32 | newline character and return a list for use in other functions. 33 | :param command_file: This is the name/location of the command file pulled in from the CLI. 34 | :return: Returns a dict of the non-comment command lines from the file. 35 | ''' 36 | commands = [] 37 | with open(command_file, 'r') as cf: 38 | for line in cf.read().split('\n'): 39 | if not line.startswith('#'): 40 | commands.append(line) 41 | return commands 42 | 43 | 44 | def setup_conn(fw_ip, username, password): 45 | ''' 46 | This function will set up the SSH connection via Paramiko's SSH Client. 47 | :param fw_ip: IP address of the firewall that is being connected too. 48 | :param username: Username used to log into the firewall. 49 | :param password: Password used to log into the firewall. 50 | :return: Returns the connection handler for use in other functions. 51 | ''' 52 | print fw_ip, username, password 53 | test_conn = paramiko.SSHClient() 54 | test_conn.load_system_host_keys() 55 | test_conn.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 56 | try: 57 | print 'Connecting to firewall' 58 | test_conn.connect(fw_ip, username=username, password=password) 59 | except: 60 | print 'Unable to connect to firewall, please check your settings' 61 | sys.exit(1) 62 | 63 | return test_conn # Paramiko SSH Handler 64 | 65 | 66 | def execute_remote_command(ssh_con, cmds, cmd_args): 67 | ''' 68 | This function will invoke a Paramiko shell and execute commands in a remote manner. 69 | 70 | It will search for two ending prompt conditions the '>' which is the standard prompt and the ':' 71 | which is the prompt used for passwords. 72 | 73 | :param ssh_con: This is the Paramiko SSH connection handler which is handed into the system. 74 | :param cmd: This is the command which will be executed in the Paramiko SSH Shell 75 | :param cmd_args: These are the CLI arguments to cehck for debugging, file destination, etc. 76 | :return: It will return a string of the data that is pulled back from the results of the command. 77 | ''' 78 | if cmd_args.file: 79 | cmd_out = open(cmd_args.file, 'a+') 80 | ssh_shell = ssh_con.invoke_shell() 81 | while not ssh_shell.recv_ready(): 82 | pass 83 | ssh_shell.send('set cli pager off\n') 84 | while not ssh_shell.recv_ready(): 85 | pass 86 | for cmd in cmds: 87 | if len(cmd) > 0: 88 | if cmd[-1] != '\n': 89 | cmd += '\n' 90 | if cmd_args.debug: 91 | print 'Sending "%s" to remote firewall' % cmd 92 | ssh_shell.send(cmd) 93 | prompt_search = '' 94 | results_data = '' 95 | while prompt_search not in ['>', ':']: 96 | results_data += ssh_shell.recv(4096).replace('\r', '') 97 | prompt_search = results_data.strip()[-1] 98 | if cmd_args.file: 99 | cmd_out.write(results_data) 100 | if cmd_args.stdout: 101 | print results_data 102 | if cmd_args.debug: 103 | print 'All commands executed' 104 | 105 | def main(): 106 | cmd_parser, cmd_args = parse_args() 107 | 108 | if cmd_args.file: 109 | cmd_out = open(cmd_args.file, 'a+') 110 | cmd_out.write('---------> %s <---------\n' % datetime.now().strftime('%Y/%m/%d@%H:%M:%S')) 111 | cmd_out.close() 112 | if cmd_args.command_file: 113 | cmds = parse_command_file(cmd_args.command_file) 114 | else: 115 | cmds = [ cmd_args.cmd ] 116 | if cmd_args.debug: 117 | print 'Setting up SSH connection' 118 | cmd_handler = setup_conn(cmd_args.firewall, cmd_args.user, cmd_args.password) 119 | if cmd_args.debug: 120 | print 'Successfully set up SSH connection and received connection handler back' 121 | 122 | execute_report_command(cmd_handler, cmds, cmd_args) 123 | 124 | 125 | if __name__ == '__main__': 126 | main() 127 | 128 | -------------------------------------------------------------------------------- /pan-rcli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import paramiko 3 | import sys 4 | from datetime import datetime 5 | import argparse 6 | 7 | __author__ = 'Tighe Schlottog || workapegmailcom' 8 | 9 | 10 | def parse_args(): 11 | ''' 12 | This function will parse the command line arguments passed in via ARGV. 13 | :return: 14 | ''' 15 | parser = argparse.ArgumentParser(description='Script to remotely execute CLI commands on Palo Alto Networks firewalls') 16 | parser.add_argument('-fw', '--firewall', type=str, help='IP address of remote Firewall') 17 | parser.add_argument('-u', '--user', type=str, help='Remote Firewall Username') 18 | parser.add_argument('-cmd', type=str, help='Remote Command to be run surrounded by double quotes. For example "show system info".') 19 | parser.add_argument('-cf', '--command_file', type=str, help='Read commands to be executed remotely from a defined file') 20 | parser.add_argument('-stdout', dest='stdout', action='store_true', help='Output command data to STDOUT') 21 | parser.set_defaults(stdout=False) 22 | parser.add_argument('-f', '--file', type=str, help='Output command data to named file') 23 | fw_password = raw_input('Enter Remote Firewall Password: ') 24 | args = parser.parse_args() 25 | return parser, args, fw_password 26 | 27 | 28 | def parse_command_file(command_file): 29 | ''' 30 | This function will parse the lines of the command file, excluding the comment lines starting with #, based on the 31 | newline character and return a list for use in other functions. 32 | :param command_file: This is the name/location of the command file pulled in from the CLI. 33 | :return: Returns a dict of the non-comment command lines from the file. 34 | ''' 35 | commands = [] 36 | with open(command_file, 'r') as cf: 37 | for line in cf.read().split('\n'): 38 | if not line.startswith('#'): 39 | commands.append(line) 40 | return commands 41 | 42 | 43 | def setup_conn(fw_ip, username, password): 44 | ''' 45 | This function will set up the SSH connection via Paramiko's SSH Client. 46 | :param fw_ip: IP address of the firewall that is being connected too. 47 | :param username: Username used to log into the firewall. 48 | :param password: Password used to log into the firewall. 49 | :return: Returns the connection handler for use in other functions. 50 | ''' 51 | print fw_ip, username, password 52 | test_conn = paramiko.SSHClient() 53 | test_conn.load_system_host_keys() 54 | test_conn.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 55 | try: 56 | print 'Connecting to firewall' 57 | test_conn.connect(fw_ip, username=username, password=password) 58 | except: 59 | print 'Unable to connect to firewall, please check your settings' 60 | sys.exit(1) 61 | 62 | return test_conn # Paramiko SSH Handler 63 | 64 | 65 | def execute_remote_command(ssh_con, cmd): 66 | ''' 67 | This function will invoke a Paramiko shell and execute commands in a remote manner. 68 | :param ssh_con: This is the Paramiko SSH connection handler which is handed into the system. 69 | :param cmd: This is the command which will be executed in the Paramiko SSH Shell 70 | :return: It will return a string of the data that is pulled back from the results of the command. 71 | ''' 72 | ssh_shell = ssh_con.invoke_shell() 73 | while not ssh_shell.recv_ready(): 74 | pass 75 | ssh_shell.send('set cli pager off\n') 76 | while not ssh_shell.recv_ready(): 77 | pass 78 | if cmd[-1] != '\n': 79 | cmd += '\n' 80 | ssh_shell.send(cmd) 81 | prompt_search = '' 82 | results_data = '' 83 | while prompt_search != '>': 84 | results_data += ssh_shell.recv(4096).replace('\r', '') 85 | prompt_search = results_data.strip()[-1] 86 | return results_data 87 | 88 | 89 | def main(): 90 | cmd_parser, cmd_args, fw_pass = parse_args() 91 | 92 | if cmd_args.file: 93 | cmd_out = open(cmd_args.file, 'a+') 94 | cmd_out.write('---------> %s <---------\n' % datetime.now().strftime('%Y/%m/%d@%H:%M:%S')) 95 | if cmd_args.command_file: 96 | cmds = parse_command_file(cmd_args.command_file) 97 | else: 98 | cmds = [ cmd_args.cmd ] 99 | 100 | cmd_handler = setup_conn(cmd_args.firewall, cmd_args.user, fw_pass) 101 | 102 | for fw_cmd in cmds: 103 | cmd_data = execute_remote_command(cmd_handler, fw_cmd) 104 | if cmd_args.file: 105 | cmd_out.write(cmd_data) 106 | if cmd_args.stdout: 107 | print cmd_data 108 | 109 | 110 | if __name__ == '__main__': 111 | main() 112 | 113 | -------------------------------------------------------------------------------- /srrm-monitor-public.py: -------------------------------------------------------------------------------- 1 | import lxml.etree as etree 2 | import requests 3 | import datetime 4 | 5 | requests.packages.urllib3.disable_warnings() 6 | 7 | __author__ = 'Tighe Schlottog || tschlottog@paloaltonetworks.com' 8 | __description__ = 'Pull the stats from the show running resource-monitor on the firewall in a structured manner' 9 | __version__ = '1.0' 10 | 11 | # These are the configurable variables 12 | MAX_CPU = 3 # Max CPU percent, this is your mark to start throwing errors 13 | time_window = 15 # Numeric value of length time slices wanted for the measurement 14 | time_measure = 'minute' # This is either second, minute, hour, day, week 15 | fw_host = '' # This is the Management (or in-path Management) IP of the firewall 16 | CSV_OUT = True # This is used to define whether or not to dump the data out to a csv formated document 17 | csv_file = 'srrm-stats.csv' 18 | api_key = '' # This is the API Key used to access the firewall 19 | 20 | 21 | # Nothing past here needs to be configured 22 | srrm_params = {'type': 'op', 'cmd': '<%s>%d' % (time_measure, time_window, time_measure), 'key': api_key} 23 | srrm = requests.get('https://%s/api' % fw_host, params=srrm_params, verify=False) 24 | print srrm.content 25 | srrm_stats = {} 26 | time_now = datetime.datetime.now().strftime('%Y/%m/%d@%H:%M:%S') 27 | 28 | if srrm.status_code == 200: 29 | root = etree.fromstring(srrm.content) 30 | dp_xpath = root.xpath('.//data-processors') 31 | dp_list = [] 32 | for dps in dp_xpath: 33 | for dp in dps: 34 | dp_list.append(dp.tag) 35 | 36 | for dp in dp_list: 37 | if dp not in srrm_stats: 38 | srrm_stats[dp] = {} 39 | srrm_stats[dp]['cpu_avgs'] = {} 40 | srrm_stats[dp]['cpu_maxs'] = {} 41 | cpu_avgs = root.xpath('.//data-processors/%s/%s/cpu-load-average/entry' % (dp, time_measure)) 42 | for entry in cpu_avgs: 43 | for item in entry: 44 | if item.tag == 'coreid': 45 | core_id = item.text 46 | if item.tag == 'value': 47 | core_data = item.text 48 | srrm_stats[dp]['cpu_avgs'][core_id] = core_data 49 | cpu_maxs = root.xpath('.//data-processors/%s/%s/cpu-load-maximum/entry' % (dp, time_measure)) 50 | for entry in cpu_maxs: 51 | for item in entry: 52 | if item.tag == 'coreid': 53 | core_id = item.text 54 | if item.tag == 'value': 55 | core_data = item.text 56 | srrm_stats[dp]['cpu_maxs'][core_id] = core_data 57 | 58 | if CSV_OUT: 59 | csv_out = open(csv_file, 'a') 60 | 61 | for DP in srrm_stats: 62 | for type in srrm_stats[DP]: 63 | for core in srrm_stats[DP][type]: 64 | cpu_data = [int(x) for x in srrm_stats[DP][type][core].split(',')] 65 | for idx, val in enumerate(cpu_data): 66 | if CSV_OUT: 67 | csv_out.write('%s,%s %s,%s,%s,%s,%s\n' % (time_now, time_window, time_measure, type, DP, core, srrm_stats[DP][type][core])) 68 | if val > MAX_CPU: 69 | print '%s | %s | Core %s | %d CPU Utilization | %d minutes ago' % (DP, type, core, val, idx) 70 | -------------------------------------------------------------------------------- /url-check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import requests 4 | import lxml.etree 5 | import argparse 6 | import sys 7 | requests.packages.urllib3.disable_warnings() 8 | 9 | ''' 10 | url-check.py - 2017 Tighe Schlottog 11 | 12 | This script will pull in data from a text file and then utilize it querying the URL Cloud w/PANDB. 13 | ''' 14 | 15 | 16 | def parse_args(): 17 | parser = argparse.ArgumentParser(description='Query URL Cloud to determine the category of URLs.') 18 | parser.add_argument('-fw', type=str, help='IP Address of Firewall for querying', required=True) 19 | parser.add_argument('-k', '--api_key', type=str, help='API Key with access to Firewall', required=True) 20 | parser.add_argument('-ui', '--urls_in', type=str, help='Input file containing a list of URLS for querying', required=True) 21 | parser.add_argument('-uo', '--urls_out', type=str, help='Output file where the URL and category are written in CSV', required=True) 22 | parser.add_argument('-d', '--debug', type=bool, default=False, help='Enable Debugging for additional Outputs') 23 | parser.add_argument('-c', '--cname', type=bool, default=False, help='Enable CNAME/Recursion on domain') 24 | args = parser.parse_args() 25 | return parser, args 26 | 27 | 28 | def url_query(fw, api_key, urls_in, urls_out, debug, cname): 29 | resp_out = open(urls_out, 'a') 30 | with open(urls_in, 'r') as urls_file: 31 | for url in urls_file: 32 | if url.strip() != '': 33 | test_headers = {'type': 'op', 'key': api_key, 'cmd': '' + requests.utils.requote_uri(str(url.strip())) + ''} 34 | if debug: 35 | print '***Request to API***' 36 | print 'URL: https://%s/api' % fw 37 | print 'Parameters:' 38 | print test_headers 39 | print '***End API Request\n\n' 40 | url_req = requests.get('https://%s/api' % fw, params=test_headers, verify=False) 41 | if debug: 42 | print '***Response from API***' 43 | print url_req.content 44 | print '***End API Response\n\n' 45 | if url_req.status_code is requests.codes.ok: 46 | resp_root = lxml.etree.fromstring(url_req.content) 47 | if resp_root.xpath('//result') is not None: 48 | for x in resp_root.xpath('//result')[0].text.split(): 49 | if not x.startswith('BM:'): 50 | if cname: 51 | if debug: 52 | print '***Data in //result xpath: %s' % x 53 | if ',' in x: 54 | y = x.split(',') 55 | print y[0], y[-1] 56 | resp_out.write('%s,%s\n' % (y[0], y[-1])) 57 | else: 58 | if debug: 59 | print '***Data in //result xpath: %s' % x 60 | if ',' in x: 61 | y = x.split(',') 62 | print url, y[-1] 63 | resp_out.write('%s,%s\n' % (url.strip(), y[-1])) 64 | break 65 | else: 66 | print 'Caught an error ' 67 | else: 68 | sys.exit('API Connection to %s returned %s' % (fw, url_req.status_code)) 69 | 70 | 71 | def control(): 72 | url_parser, url_args = parse_args() 73 | url_query(url_args.fw, url_args.api_key, url_args.urls_in, url_args.urls_out, url_args.debug, url_args.cname) 74 | 75 | 76 | if __name__ == '__main__': 77 | control() 78 | --------------------------------------------------------------------------------