From 0e1d63805214e69182b31104eb4f42882df13a62 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Thu, 27 Jun 2013 15:10:45 -0700 Subject: [PATCH 01/27] Cleaned up indents in python scripts. --- bin/panChange.py | 283 +++++++++++++------------- bin/panStandAloneChange.py | 261 ++++++++++++------------ bin/panorama.py | 156 ++++++++------- bin/panoramaStandAlone.py | 392 ++++++++++++++++++------------------- bin/panoramaUserUpdate.py | 368 +++++++++++++++++----------------- 5 files changed, 726 insertions(+), 734 deletions(-) diff --git a/bin/panChange.py b/bin/panChange.py index 6af3f969..d9e276d7 100644 --- a/bin/panChange.py +++ b/bin/panChange.py @@ -1,21 +1,21 @@ ########################################### -# Version 0.1 +# Version 0.1 # Extremely Experimental # author: monzy@splunk.com # About this script: # Given an IP address, adds or removes the IP from an address group # The script assumes that you have firewall policy setup that blocks # traffic for a given group, e.g. a badActors group. -# It is important to recognize that this script does NOT modify a firewall rule. +# It is important to recognize that this script does NOT modify a firewall rule. # It only adds/removes address objects. # So if a rule operates on an Address Group, this scripts add/remove will impact -# that rule/policy. +# that rule/policy. ############################################ ############################################ # How to Use this script # in the example below, we are blocking all ip's returned by the search # example1: index=pan_logs 1.1.1.1 | stats dc(dst_ip) by dst_ip | panblock action="add" group="badboys" -# Adds the IP 1.1.1.1 +# Adds the IP 1.1.1.1 # example2: index=pan_logs wine | stats dc(dst_hostname) by dst_hostname | panblock action="rem" group="badboys" device="sales-fw" # Removes all dst_hostnames returned by the search from the sales firewall from the badboys group ########################################### @@ -29,13 +29,13 @@ ############################# # Change the values below to suit your PAN configuration -# WARNING!!!! Password is stored in clear text. +# WARNING!!!! Password is stored in clear text. ############################# # firewall IP. you can provide this via the device parameter PAN = '192.168.4.100' # admin account for the PAN device #PANUSER = 'admin' -# password for the admin user. +# password for the admin user. # any special characters in the password must be URL/percent-encoded. #PANPASS = 'admin' # Defaults to vsys1. vsys substition is not supported at this time @@ -49,7 +49,7 @@ HTTP_PROXY = {} ######################################################### -# Do NOT modify anything below this line unless you are +# Do NOT modify anything below this line unless you are # certain of the ramifications of the changes ######################################################### @@ -62,139 +62,139 @@ logger = dcu.getLogger() -## Major props to Ledion. copying his function, verbatim and then adding comments and traceback and logging +## Major props to Ledion. copying his function, verbatim and then adding comments and traceback and logging ## http://blogs.splunk.com/2011/03/15/storing-encrypted-credentials/ ## access the credentials in /servicesNS/nobody//admin/passwords def getCredentials(sessionKey): - '''Given a splunk sesionKey returns a clear text user name and password from a splunk password container''' - # this is the folder name for the app and not the app's common name - myapp = 'SplunkforPaloAltoNetworks' - try: - # list all credentials - entities = entity.getEntities(['admin', 'passwords'], namespace=myapp, owner='nobody', sessionKey=sessionKey) - except Exception, e: - stack = traceback.format_exc() - logger.warn(stack) - logger.warn("entity exception") - raise Exception("Could not get %s credentials from splunk. Error: %s" % (myapp, str(e))) - # return first set of credentials - for i, c in entities.items(): - return c['username'], c['clear_password'] - logger.warn("No credentials") - raise Exception("No credentials have been found") + '''Given a splunk sesionKey returns a clear text user name and password from a splunk password container''' + # this is the folder name for the app and not the app's common name + myapp = 'SplunkforPaloAltoNetworks' + try: + # list all credentials + entities = entity.getEntities(['admin', 'passwords'], namespace=myapp, owner='nobody', sessionKey=sessionKey) + except Exception, e: + stack = traceback.format_exc() + logger.warn(stack) + logger.warn("entity exception") + raise Exception("Could not get %s credentials from splunk. Error: %s" % (myapp, str(e))) + # return first set of credentials + for i, c in entities.items(): + return c['username'], c['clear_password'] + logger.warn("No credentials") + raise Exception("No credentials have been found") def createOpener(): - '''Create a generic opener for http. This is particularly helpful when there is a proxy server in line''' - # Thanks to: http://www.decalage.info/en/python/urllib2noproxy - proxy_handler = urllib2.ProxyHandler(HTTP_PROXY) - opener = urllib2.build_opener(proxy_handler) - urllib2.install_opener(opener) - return opener + '''Create a generic opener for http. This is particularly helpful when there is a proxy server in line''' + # Thanks to: http://www.decalage.info/en/python/urllib2noproxy + proxy_handler = urllib2.ProxyHandler(HTTP_PROXY) + opener = urllib2.build_opener(proxy_handler) + urllib2.install_opener(opener) + return opener def getKey(PAN, PANUSER, PANPASS): - ''' Logs into the PAN firewall and obtains a PAN session key''' - # create an opener object - opener = createOpener() - try: - # the url for the PAN - panReq = urllib2.Request('https://'+PAN+'/api/?type=keygen&user='+PANUSER+'&password='+PANPASS) - # make the request - req = opener.open(panReq) - except: - sys.exit(-1) - # the result of the URL request - result = req.read() - # get the status of the result - try: - sm = re.search(r"success",result).group(0) - if sm == 'success' : - status = 'success' - except: - sys.exit(-1) - # parse the key from the result - key = result.split("")[0].split("")[1] - return key + ''' Logs into the PAN firewall and obtains a PAN session key''' + # create an opener object + opener = createOpener() + try: + # the url for the PAN + panReq = urllib2.Request('https://'+PAN+'/api/?type=keygen&user='+PANUSER+'&password='+PANPASS) + # make the request + req = opener.open(panReq) + except: + sys.exit(-1) + # the result of the URL request + result = req.read() + # get the status of the result + try: + sm = re.search(r"success",result).group(0) + if sm == 'success' : + status = 'success' + except: + sys.exit(-1) + # parse the key from the result + key = result.split("")[0].split("")[1] + return key def checkAddr(address): - '''Check if an address is a FQDN or IP with some netmask. Return the appropriate uri for PAN api''' - # element is everything after the element= field in the PAN api - # re pattern for IP address - ippat = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' - # re pattern for fully qualified domain name (could be stricter) - fqdnpat = '\w+[.-]' - # is element an IP address - if re.match(ippat, address) != None : - ipuri = ''+address+'/32' - return ipuri - if re.match(fqdnpat, address) != None : - fqdnuri = ''+address+'' - return fqdnuri + '''Check if an address is a FQDN or IP with some netmask. Return the appropriate uri for PAN api''' + # element is everything after the element= field in the PAN api + # re pattern for IP address + ippat = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' + # re pattern for fully qualified domain name (could be stricter) + fqdnpat = '\w+[.-]' + # is element an IP address + if re.match(ippat, address) != None : + ipuri = ''+address+'/32' + return ipuri + if re.match(fqdnpat, address) != None : + fqdnuri = ''+address+'' + return fqdnuri def panorama(): - ''' Interact with PANorama''' - #Set dynamic address object (with LinkID) at Panorama level, commit - #https://pm-panorama/api/?type=config&action=set&xpath=/config/devices/entry[@name='localhost.localdomain']/device-group/entry[@name='splunktastic']/address/entry[@name='test-add']&element=test1&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09 - #https://pm-panorama/api/?type=commit&action=all&cmd=splunktastic&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09 - #Map IPs to correct UserID - #https://pm-panorama/api/?type=user-id&action=set&cmd=1.0update&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09&target=0006C107916 - return 0 + ''' Interact with PANorama''' + #Set dynamic address object (with LinkID) at Panorama level, commit + #https://pm-panorama/api/?type=config&action=set&xpath=/config/devices/entry[@name='localhost.localdomain']/device-group/entry[@name='splunktastic']/address/entry[@name='test-add']&element=test1&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09 + #https://pm-panorama/api/?type=commit&action=all&cmd=splunktastic&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09 + #Map IPs to correct UserID + #https://pm-panorama/api/?type=user-id&action=set&cmd=1.0update&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09&target=0006C107916 + return 0 def addActor(PAN, key, VSYS, ACTOR, BADACTORS): - '''Creates an address object then add the object to an Address group''' - # create an opener object - opener = createOpener() - # create the address object - panReq = urllib2.Request('https://'+PAN+'//api/?type=config&action=set&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address/entry[@name='+"'"+ACTOR+"'"+']&element='+checkAddr(ACTOR)) - req = opener.open(panReq) - # add the address object to the BADACTORS group - panReq = urllib2.Request('https://'+PAN+'//api/?type=config&action=set&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address-group/entry[@name='+"'"+BADACTORS+"'"+']&element='+ACTOR+'') - req = opener.open(panReq) - return 0 + '''Creates an address object then add the object to an Address group''' + # create an opener object + opener = createOpener() + # create the address object + panReq = urllib2.Request('https://'+PAN+'//api/?type=config&action=set&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address/entry[@name='+"'"+ACTOR+"'"+']&element='+checkAddr(ACTOR)) + req = opener.open(panReq) + # add the address object to the BADACTORS group + panReq = urllib2.Request('https://'+PAN+'//api/?type=config&action=set&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address-group/entry[@name='+"'"+BADACTORS+"'"+']&element='+ACTOR+'') + req = opener.open(panReq) + return 0 def remActor(PAN, key, VSYS, ACTOR, BADACTORS): - '''Remove an address object from the address-group then remove the addres object ''' - # create an opener object - opener = createOpener() - # first we remove him from the badactors group - #panReq = urllib2.Request('https://'+PAN+'/api/?type=config&action=delete&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address-group/entry[@name='+"'"+BADACTORS+"'"+']&element='+ACTOR+'') - #req = opener.open(panReq) - # then we remove him all together - #panReq = urllib2.Request('https://'+PAN+'/api/?type=config&action=delete&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address/entry[@name='+"'"+ACTOR+"'"+']') - #req = opener.open(panReq) - panReq = urllib2.Request('https://'+PAN+'/api/?type=config&action=delete&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address-group/entry[@name='+"'"+BADACTORS+"'"+']/member[text()='+"'"+ACTOR+"'"+']') - req = opener.open(panReq) - panReq = urllib2.Request('https://'+PAN+'/api/?type=config&action=delete&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address/entry[@name='+"'"+ACTOR+"'"+']') - req = opener.open(panReq) - return 0 + '''Remove an address object from the address-group then remove the addres object ''' + # create an opener object + opener = createOpener() + # first we remove him from the badactors group + #panReq = urllib2.Request('https://'+PAN+'/api/?type=config&action=delete&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address-group/entry[@name='+"'"+BADACTORS+"'"+']&element='+ACTOR+'') + #req = opener.open(panReq) + # then we remove him all together + #panReq = urllib2.Request('https://'+PAN+'/api/?type=config&action=delete&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address/entry[@name='+"'"+ACTOR+"'"+']') + #req = opener.open(panReq) + panReq = urllib2.Request('https://'+PAN+'/api/?type=config&action=delete&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address-group/entry[@name='+"'"+BADACTORS+"'"+']/member[text()='+"'"+ACTOR+"'"+']') + req = opener.open(panReq) + panReq = urllib2.Request('https://'+PAN+'/api/?type=config&action=delete&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address/entry[@name='+"'"+ACTOR+"'"+']') + req = opener.open(panReq) + return 0 def commitConfig(PAN, key): - '''Save the changes made to the address objects''' - # create an opener object - opener = createOpener() - panReq = urllib2.Request('https://'+PAN+'//api/?type=commit&cmd=&key='+key) - req = opener.open(panReq) - return 0 + '''Save the changes made to the address objects''' + # create an opener object + opener = createOpener() + panReq = urllib2.Request('https://'+PAN+'//api/?type=commit&cmd=&key='+key) + req = opener.open(panReq) + return 0 def block(result): - key = getKey(PAN, PANUSER, PANPASS) - if ACTION == 'add': - addActor(PAN, key, VSYS, str(result[result.keys()[0]]), BADACTORS) - elif ACTION == 'rem': - remActor(PAN, key, VSYS, str(result[result.keys()[0]]), BADACTORS) - else: - return ['bad action', key] - return ["action submitted", key] - + key = getKey(PAN, PANUSER, PANPASS) + if ACTION == 'add': + addActor(PAN, key, VSYS, str(result[result.keys()[0]]), BADACTORS) + elif ACTION == 'rem': + remActor(PAN, key, VSYS, str(result[result.keys()[0]]), BADACTORS) + else: + return ['bad action', key] + return ["action submitted", key] + args, kwargs = splunk.Intersplunk.getKeywordsAndOptions() #parse the kwargs for ACTION, VSYS, PAN if kwargs.has_key('action'): - ACTION = kwargs['action'] + ACTION = kwargs['action'] if kwargs.has_key('device'): - PAN = kwargs['device'] + PAN = kwargs['device'] if kwargs.has_key('vsys'): - VSYS = kwargs['vsys'] + VSYS = kwargs['vsys'] if kwargs.has_key('group'): - BADACTORS = kwargs['group'] + BADACTORS = kwargs['group'] # an empty dictionary. it will be used to hold system values settings = dict() @@ -206,35 +206,34 @@ def block(result): PANUSER, PANPASS = getCredentials(sessionKey) try: - for result in results: - if (result.has_key('src_ip')\ - or result.has_key('dst_ip') \ - or result.has_key('ip') \ - or result.has_key('hostname') \ - or result.has_key('dst_hostname') \ - or result.has_key('dest_host') \ - or result.has_key('domain') \ - or result.has_key('clientip') ): - blockresult = block(result) - result["status"] = blockresult[0] - key = blockresult[1] - # for test purposes - elif (len(result) == 2) and result.has_key('test'): - result["status"] = ["this is the sessionKey. not printing the password :p " + sessionKey] - else: - result["status"] = 'empty or invalid field vlue' - - #after all the addresses have been processed, commit the config - commitConfig(PAN, key) + for result in results: + if (result.has_key('src_ip')\ + or result.has_key('dst_ip') \ + or result.has_key('ip') \ + or result.has_key('hostname') \ + or result.has_key('dst_hostname') \ + or result.has_key('dest_host') \ + or result.has_key('domain') \ + or result.has_key('clientip') ): + blockresult = block(result) + result["status"] = blockresult[0] + key = blockresult[1] + # for test purposes + elif (len(result) == 2) and result.has_key('test'): + result["status"] = ["this is the sessionKey. not printing the password :p " + sessionKey] + else: + result["status"] = 'empty or invalid field vlue' + + #after all the addresses have been processed, commit the config + commitConfig(PAN, key) except Exception, e: - import traceback - stack = traceback.format_exc() - if isgetinfo: - splunk.Intersplunk.parseError(str(e)) + import traceback + stack = traceback.format_exc() + if isgetinfo: + splunk.Intersplunk.parseError(str(e)) - results = splunk.Intersplunk.generateErrorResults(str(e)) - logger.warn(stack) + results = splunk.Intersplunk.generateErrorResults(str(e)) + logger.warn(stack) # output results splunk.Intersplunk.outputResults(results) - diff --git a/bin/panStandAloneChange.py b/bin/panStandAloneChange.py index 589fbe85..ea95a0ff 100644 --- a/bin/panStandAloneChange.py +++ b/bin/panStandAloneChange.py @@ -6,7 +6,7 @@ # traffic for the badActors group. or otherwise acts on the address group # We dont' actually modify a firewall rule. We only add/remove address objects. # So if a rule operates on an Address Group, this scripts add/remove will impact -# that policy. +# that policy. ########################################### ########################################### @@ -22,7 +22,7 @@ PAN = '192.168.4.100' # admin account for the PAN device PANUSER = 'admin' -# password for the admin user. +# password for the admin user. # any special characters in the password must be URL/percent-encoded. PANPASS = 'admin' # Defaults to vsys1. vsys substition is not supported at this time @@ -37,7 +37,7 @@ ######################################################### -# Do NOT modify anything below this line unless you are +# Do NOT modify anything below this line unless you are # certain of the ramifications of the changes ######################################################### import urllib2 # make http requests to PAN firewall @@ -47,147 +47,146 @@ import optparse # for option parsing def createOpener(): - # Create a generic opener for http - # This is particularly helpful when there is a proxy server in line - # Thanks to: http://www.decalage.info/en/python/urllib2noproxy - proxy_handler = urllib2.ProxyHandler(HTTP_PROXY) - opener = urllib2.build_opener(proxy_handler) - urllib2.install_opener(opener) - return opener + # Create a generic opener for http + # This is particularly helpful when there is a proxy server in line + # Thanks to: http://www.decalage.info/en/python/urllib2noproxy + proxy_handler = urllib2.ProxyHandler(HTTP_PROXY) + opener = urllib2.build_opener(proxy_handler) + urllib2.install_opener(opener) + return opener def getKey(PAN, PANUSER, PANPASS): - ''' Logs into the PAN firewall and obtains a session key''' - # create an opener object - opener = createOpener() - try: - # the url for the PAN - panReq = urllib2.Request('https://'+PAN+'/api/?type=keygen&user='+PANUSER+'&password='+PANPASS) - # make the request - req = opener.open(panReq) - except: - sys.exit(-1) - # the result of the URL request - result = req.read() - # get the status of the result - try: - sm = re.search(r"success",result).group(0) - if sm == 'success' : - status = 'success' - except: - sys.exit(-1) - # parse the key from the result - key = result.split("")[0].split("")[1] - return key + ''' Logs into the PAN firewall and obtains a session key''' + # create an opener object + opener = createOpener() + try: + # the url for the PAN + panReq = urllib2.Request('https://'+PAN+'/api/?type=keygen&user='+PANUSER+'&password='+PANPASS) + # make the request + req = opener.open(panReq) + except: + sys.exit(-1) + # the result of the URL request + result = req.read() + # get the status of the result + try: + sm = re.search(r"success",result).group(0) + if sm == 'success' : + status = 'success' + except: + sys.exit(-1) + # parse the key from the result + key = result.split("")[0].split("")[1] + return key def addActor(PAN, key, VSYS, ACTOR, BADACTORS): - '''Creates an address object then add the object to an Address group''' - # create an opener object - opener = createOpener() - # create the address object - panReq = urllib2.Request('https://'+PAN+'//api/?type=config&action=set&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address/entry[@name='+"'"+ACTOR+"'"+']&element='+ACTOR+'/32') - req = opener.open(panReq) - # add the address object to the BADACTORS group - panReq = urllib2.Request('https://'+PAN+'//api/?type=config&action=set&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address-group/entry[@name='+"'"+BADACTORS+"'"+']&element='+ACTOR+'') - req = opener.open(panReq) - return 0 + '''Creates an address object then add the object to an Address group''' + # create an opener object + opener = createOpener() + # create the address object + panReq = urllib2.Request('https://'+PAN+'//api/?type=config&action=set&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address/entry[@name='+"'"+ACTOR+"'"+']&element='+ACTOR+'/32') + req = opener.open(panReq) + # add the address object to the BADACTORS group + panReq = urllib2.Request('https://'+PAN+'//api/?type=config&action=set&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address-group/entry[@name='+"'"+BADACTORS+"'"+']&element='+ACTOR+'') + req = opener.open(panReq) + return 0 def remActor(PAN, key, VSYS, ACTOR, BADACTORS): - '''Remove an address object from the address-group then remove the addres object ''' - # create an opener object - opener = createOpener() - # first we remove him from the badactors group - panReq = urllib2.Request('https://'+PAN+'//api/?type=config&action=delete&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address-group/entry[@name='+"'"+BADACTORS+"'"+']&element='+ACTOR+'') - req = opener.open(panReq) - # then we remove him all together - panReq = urllib2.Request('https://'+PAN+'//api/?type=config&action=delete&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address/entry[@name='+"'"+ACTOR+"'"+']') - req = opener.open(panReq) - return 0 + '''Remove an address object from the address-group then remove the addres object ''' + # create an opener object + opener = createOpener() + # first we remove him from the badactors group + panReq = urllib2.Request('https://'+PAN+'//api/?type=config&action=delete&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address-group/entry[@name='+"'"+BADACTORS+"'"+']&element='+ACTOR+'') + req = opener.open(panReq) + # then we remove him all together + panReq = urllib2.Request('https://'+PAN+'//api/?type=config&action=delete&key='+key+'&xpath=/config/devices/entry/vsys/entry[@name='+"'"+VSYS+"'"+']/address/entry[@name='+"'"+ACTOR+"'"+']') + req = opener.open(panReq) + return 0 def commitConfig(PAN, key): - '''Save the changes made to the address objects''' - # create an opener object - opener = createOpener() - panReq = urllib2.Request('https://'+PAN+'//api/?type=commit&cmd=&key='+key) - req = opener.open(panReq) - return 0 + '''Save the changes made to the address objects''' + # create an opener object + opener = createOpener() + panReq = urllib2.Request('https://'+PAN+'//api/?type=commit&cmd=&key='+key) + req = opener.open(panReq) + return 0 def panChange(result): - '''Handles the input from Splunk and starts the change process''' - # Result may contain other info about configs. e.g. PAN device or Vsys etc. - PAN = PANUSER = PANPASS = VSYS = 0 - if result.has_key('PAN') : - PAN = result['PAN'] - if result.has_key('PANUSER') : - PANUSER = result['PANUSER'] - if result.has_key('VSYS') : - VSYS = result['VSYS'] - if result.has_key('PANPASS'): - PANPASS = result['PANPASS'] - key = getKey(PAN, PANUSER, PANPASS) - # check for the action - if result['panAction'] == 'add' : - addActor(PAN, key, VSYS, str(result[result.keys()[0]]), BADACTORS) - elif result['panAction'] == 'rem': - remActor(PAN, key, VSYS, str(result[result.keys()[0]]), BADACTORS) - else: - return 'bad action' - #commit the changes - commitConfig(PAN, key) - return 'action completed' + '''Handles the input from Splunk and starts the change process''' + # Result may contain other info about configs. e.g. PAN device or Vsys etc. + PAN = PANUSER = PANPASS = VSYS = 0 + if result.has_key('PAN') : + PAN = result['PAN'] + if result.has_key('PANUSER') : + PANUSER = result['PANUSER'] + if result.has_key('VSYS') : + VSYS = result['VSYS'] + if result.has_key('PANPASS'): + PANPASS = result['PANPASS'] + key = getKey(PAN, PANUSER, PANPASS) + # check for the action + if result['panAction'] == 'add' : + addActor(PAN, key, VSYS, str(result[result.keys()[0]]), BADACTORS) + elif result['panAction'] == 'rem': + remActor(PAN, key, VSYS, str(result[result.keys()[0]]), BADACTORS) + else: + return 'bad action' + #commit the changes + commitConfig(PAN, key) + return 'action completed' def main(argv = sys.argv): - '''Received parameters from the command line''' - # setup the option parser - parser = optparse.OptionParser() - parser.add_option('-a', '--add', dest="ACTION", default="0", help="Adding or removing an address object. Acceptable options are add or rem") - parser.add_option('-v', '--vsys', dest="VSYS", default=VSYS, help="vsys of the address address object") - parser.add_option('-u', '--user', dest="PANUSER", default=PANUSER, help="Name of the admin user") - parser.add_option('-p', '--pass', dest="PANPASS", default=PANPASS, help="Password for the admin user") - parser.add_option('-d', '--device', dest="PAN", default=PAN, help="IP address of the PAN device") - parser.add_option('-b', '--bad', dest="ACTOR", default=ACTOR, help="Address of the bad actor.") - parser.add_option('-g', '--group', dest="BADACTORS", default=BADACTORS, help="Address group of Bad Actors") - parser.add_option('-t', '--test', dest="test", default="0", help="This is just a dummy run command") - - - options, remainder = parser.parse_args() - - # get an authentication key - key = getKey(options.PAN, options.PANUSER, options.PANPASS) - # add an address object - if options.ACTION == 'add': - status = addActor(options.PAN, - key, - options.VSYS, - options.ACTOR, - options.BADACTORS) - #remove an address object - elif options.ACTION == 'rem': - status = remActor(options.PAN, - key, - options.VSYS, - options.ACTOR, - options.BADACTORS) - elif (options.test == 1): - print "So you just want to see the test mode" - print "PAN Device: " + options.PAN - print "PAN User: " + options.PANUSER - print "PANPASS: " + options.PANPASS - print "VSYS: " + options.VSYS - print "Action: " + options.ACTION - print "Actor: " + options.ACTOR - print "Group: " + options.BADACTORS - print "Thats all i got. Have a nice day!" - else: - print "Please specify an action of add or rem. e.g. to add an address" - print "panChange.py -a add -b 1.1.1.1" - print "Action must be either 'add' or 'rem' " - sys.exit(-1) - # save the config changes - commitConfig(options.PAN, key) - return 0 + '''Received parameters from the command line''' + # setup the option parser + parser = optparse.OptionParser() + parser.add_option('-a', '--add', dest="ACTION", default="0", help="Adding or removing an address object. Acceptable options are add or rem") + parser.add_option('-v', '--vsys', dest="VSYS", default=VSYS, help="vsys of the address address object") + parser.add_option('-u', '--user', dest="PANUSER", default=PANUSER, help="Name of the admin user") + parser.add_option('-p', '--pass', dest="PANPASS", default=PANPASS, help="Password for the admin user") + parser.add_option('-d', '--device', dest="PAN", default=PAN, help="IP address of the PAN device") + parser.add_option('-b', '--bad', dest="ACTOR", default=ACTOR, help="Address of the bad actor.") + parser.add_option('-g', '--group', dest="BADACTORS", default=BADACTORS, help="Address group of Bad Actors") + parser.add_option('-t', '--test', dest="test", default="0", help="This is just a dummy run command") + + + options, remainder = parser.parse_args() + + # get an authentication key + key = getKey(options.PAN, options.PANUSER, options.PANPASS) + # add an address object + if options.ACTION == 'add': + status = addActor(options.PAN, + key, + options.VSYS, + options.ACTOR, + options.BADACTORS) + #remove an address object + elif options.ACTION == 'rem': + status = remActor(options.PAN, + key, + options.VSYS, + options.ACTOR, + options.BADACTORS) + elif (options.test == 1): + print "So you just want to see the test mode" + print "PAN Device: " + options.PAN + print "PAN User: " + options.PANUSER + print "PANPASS: " + options.PANPASS + print "VSYS: " + options.VSYS + print "Action: " + options.ACTION + print "Actor: " + options.ACTOR + print "Group: " + options.BADACTORS + print "Thats all i got. Have a nice day!" + else: + print "Please specify an action of add or rem. e.g. to add an address" + print "panChange.py -a add -b 1.1.1.1" + print "Action must be either 'add' or 'rem' " + sys.exit(-1) + # save the config changes + commitConfig(options.PAN, key) + return 0 if __name__ == "__main__": - main() - + main() diff --git a/bin/panorama.py b/bin/panorama.py index 59cdb98a..268739b7 100644 --- a/bin/panorama.py +++ b/bin/panorama.py @@ -1,21 +1,21 @@ ########################################### -# Version 0.1 +# Version 0.1 # Extremely Experimental # author: monzy@splunk.com # About this script: # Given an IP address, adds or removes the IP from an address group # The script assumes that you have firewall policy setup that blocks # traffic for a given group, e.g. a badActors group. -# It is important to recognize that this script does NOT modify a firewall rule. +# It is important to recognize that this script does NOT modify a firewall rule. # It only adds/removes address objects. # So if a rule operates on an Address Group, this scripts add/remove will impact -# that rule/policy. +# that rule/policy. ############################################ ############################################ # How to Use this script # in the example below, we are blocking all ip's returned by the search # example1: index=pan_logs 1.1.1.1 | stats dc(dst_ip) by dst_ip | panblock action="add" group="badboys" -# Adds the IP 1.1.1.1 +# Adds the IP 1.1.1.1 # example2: index=pan_logs wine | stats dc(dst_hostname) by dst_hostname | panblock action="rem" group="badboys" device="sales-fw" # Removes all dst_hostnames returned by the search from the sales firewall from the badboys group ########################################### @@ -29,13 +29,13 @@ ############################# # Change the values below to suit your PAN configuration -# WARNING!!!! Password is stored in clear text. +# WARNING!!!! Password is stored in clear text. ############################# # firewall IP PAN = '192.168.4.211' # admin account for the PAN device PANUSER = 'admin' -# password for the admin user. +# password for the admin user. # any special characters in the password must be URL/percent-encoded. PANPASS = 'admin' # Defaults to vsys1. vsys substition is not supported at this time @@ -49,7 +49,7 @@ HTTP_PROXY = {} ######################################################### -# Do NOT modify anything below this line unless you are +# Do NOT modify anything below this line unless you are # certain of the ramifications of the changes ######################################################### import splunk.Intersplunk # so you can interact with Splunk @@ -62,94 +62,94 @@ logger = dcu.getLogger() def getCredentials(sessionKey): - '''Given a splunk sesionKey returns a clear text user name and password from a splunk password container''' - # this is the folder name for the app and not the app's common name - myapp = 'SplunkforPaloAltoNetworks' - try: - # list all credentials - entities = entity.getEntities(['admin', 'passwords'], namespace=myapp, owner='nobody', sessionKey=sessionKey) - except Exception, e: - stack = traceback.format_exc() - logger.warn(stack) - logger.warn("entity exception") - raise Exception("Could not get %s credentials from splunk. Error: %s" % (myapp, str(e))) - # return first set of credentials - for i, c in entities.items(): - return c['username'], c['clear_password'] - logger.warn("No credentials") - raise Exception("No credentials have been found") + '''Given a splunk sesionKey returns a clear text user name and password from a splunk password container''' + # this is the folder name for the app and not the app's common name + myapp = 'SplunkforPaloAltoNetworks' + try: + # list all credentials + entities = entity.getEntities(['admin', 'passwords'], namespace=myapp, owner='nobody', sessionKey=sessionKey) + except Exception, e: + stack = traceback.format_exc() + logger.warn(stack) + logger.warn("entity exception") + raise Exception("Could not get %s credentials from splunk. Error: %s" % (myapp, str(e))) + # return first set of credentials + for i, c in entities.items(): + return c['username'], c['clear_password'] + logger.warn("No credentials") + raise Exception("No credentials have been found") def createOpener(): - # Create a generic opener for http - # This is particularly helpful when there is a proxy server in line - # Thanks to: http://www.decalage.info/en/python/urllib2noproxy - proxy_handler = urllib2.ProxyHandler(HTTP_PROXY) - opener = urllib2.build_opener(proxy_handler) - urllib2.install_opener(opener) - return opener + # Create a generic opener for http + # This is particularly helpful when there is a proxy server in line + # Thanks to: http://www.decalage.info/en/python/urllib2noproxy + proxy_handler = urllib2.ProxyHandler(HTTP_PROXY) + opener = urllib2.build_opener(proxy_handler) + urllib2.install_opener(opener) + return opener def getKey(device, panuser, panpass): - ''' Logs into the PAN firewall and obtains a session key''' - # create an opener object - opener = createOpener() - try: - # the url for the PAN - panReq = urllib2.Request('https://'+device+'/api/?type=keygen&user='+panuser+'&password='+panpass) - # make the request - req = opener.open(panReq) - except: - sys.exit(-1) - # the result of the URL request - result = req.read() - # get the status of the result - try: - sm = re.search(r"success",result).group(0) - if sm == 'success' : - status = 'success' - except: - sys.exit(-1) - # parse the key from the result - key = result.split("")[0].split("")[1] - return key + ''' Logs into the PAN firewall and obtains a session key''' + # create an opener object + opener = createOpener() + try: + # the url for the PAN + panReq = urllib2.Request('https://'+device+'/api/?type=keygen&user='+panuser+'&password='+panpass) + # make the request + req = opener.open(panReq) + except: + sys.exit(-1) + # the result of the URL request + result = req.read() + # get the status of the result + try: + sm = re.search(r"success",result).group(0) + if sm == 'success' : + status = 'success' + except: + sys.exit(-1) + # parse the key from the result + key = result.split("")[0].split("")[1] + return key def panorama(): - '''Interact with PANorama''' - #Set dynamic address object (with LinkID) at Panorama level, commit - #https://pm-panorama/api/?type=config&action=set&xpath=/config/devices/entry[@name='localhost.localdomain']/device-group/entry[@name='splunktastic']/address/entry[@name='test-add']&element=test1&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09 - #https://pm-panorama/api/?type=commit&action=all&cmd=splunktastic&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09 - #Map IPs to correct UserID - #https://pm-panorama/api/?type=user-id&action=set&cmd=1.0update&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09&target=0006C107916 - return 0 + '''Interact with PANorama''' + #Set dynamic address object (with LinkID) at Panorama level, commit + #https://pm-panorama/api/?type=config&action=set&xpath=/config/devices/entry[@name='localhost.localdomain']/device-group/entry[@name='splunktastic']/address/entry[@name='test-add']&element=test1&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09 + #https://pm-panorama/api/?type=commit&action=all&cmd=splunktastic&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09 + #Map IPs to correct UserID + #https://pm-panorama/api/?type=user-id&action=set&cmd=1.0update&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09&target=0006C107916 + return 0 def commitConfig(PAN, key): - '''Save the changes made to the address objects''' - # create an opener object - opener = createOpener() - panReq = urllib2.Request('https://'+PAN+'//api/?type=commit&cmd=&key='+key) - req = opener.open(panReq) - return 0 + '''Save the changes made to the address objects''' + # create an opener object + opener = createOpener() + panReq = urllib2.Request('https://'+PAN+'//api/?type=commit&cmd=&key='+key) + req = opener.open(panReq) + return 0 def panorama(result): - key = getKey(PAN, PANUSER, PANPASS) - if ACTION == 'add': - addActor(PAN, key, VSYS, str(result[result.keys()[0]]), BADACTORS) - elif ACTION == 'rem': - remActor(PAN, key, VSYS, str(result[result.keys()[0]]), BADACTORS) - else: - return ['bad action', key] - return ["action submitted", key] + key = getKey(PAN, PANUSER, PANPASS) + if ACTION == 'add': + addActor(PAN, key, VSYS, str(result[result.keys()[0]]), BADACTORS) + elif ACTION == 'rem': + remActor(PAN, key, VSYS, str(result[result.keys()[0]]), BADACTORS) + else: + return ['bad action', key] + return ["action submitted", key] args, kwargs = splunk.Intersplunk.getKeywordsAndOptions() #parse the kwargs for ACTION, VSYS, PAN if kwargs.has_key('action'): - ACTION = kwargs['action'] + ACTION = kwargs['action'] if kwargs.has_key('device'): - PAN = kwargs['device'] + PAN = kwargs['device'] if kwargs.has_key('vsys'): - VSYS = kwargs['vsys'] + VSYS = kwargs['vsys'] if kwargs.has_key('group'): - BADACTORS = kwargs['group'] + BADACTORS = kwargs['group'] # an empty dictionary. it will be used to hold system values settings = dict() @@ -159,5 +159,3 @@ def panorama(result): sessionKey = settings['sessionKey'] # get the user and password using the sessionKey PANUSER, PANPASS = getCredentials(sessionKey) - - \ No newline at end of file diff --git a/bin/panoramaStandAlone.py b/bin/panoramaStandAlone.py index 0797e3e1..1bf31b23 100644 --- a/bin/panoramaStandAlone.py +++ b/bin/panoramaStandAlone.py @@ -1,19 +1,19 @@ ########################################### -# Version 0.1 +# Version 0.1 # author: monzy@splunk.com # About this script: -# +# ############################################ ############################################ # How to Use this script -# +# ########################################### ########################################### # Known issues: # Very limited error checking # Errors may not be reported in the Splunk UI # ONLY works with PANORAMA at this time -# Device group have to be pre-populated +# Device group have to be pre-populated ########################################### ############################# # Change the values below to suit your PAN configuration @@ -25,7 +25,7 @@ PANO = "Panorama" # admin account for the PAN device PANUSER = 'admin' -# password for the admin user. +# password for the admin user. # any special characters in the password must be URL/percent-encoded. PANPASS = 'admin' # this is the device group where changes will take effect @@ -35,7 +35,7 @@ # if you DO want to go through a proxy, e.g., HTTP_PROXY={squid:'2.2.2.2'} HTTP_PROXY = {} ######################################################### -# Do NOT modify anything below this line unless you are +# Do NOT modify anything below this line unless you are # certain of the ramifications of the changes ######################################################### import urllib2 # make http requests to PAN firewall @@ -44,220 +44,216 @@ import xml.etree.ElementTree as ET # for xml parsing def createOpener(): - # Create a generic opener for http - # This is particularly helpful when there is a proxy server in line - # Thanks to: http://www.decalage.info/en/python/urllib2noproxy - proxy_handler = urllib2.ProxyHandler(HTTP_PROXY) - opener = urllib2.build_opener(proxy_handler) - urllib2.install_opener(opener) - return opener + # Create a generic opener for http + # This is particularly helpful when there is a proxy server in line + # Thanks to: http://www.decalage.info/en/python/urllib2noproxy + proxy_handler = urllib2.ProxyHandler(HTTP_PROXY) + opener = urllib2.build_opener(proxy_handler) + urllib2.install_opener(opener) + return opener def getPanSerial(key, PAN): - '''Interact with PANorama''' - #List device groups: List devices by named Device Group, obtain Serial Numbers (for &target=) - panurl = 'https://'+PAN+'/api/?type=config&action=show&xpath=/config/devices/entry[@name='+"'"+"localhost.localdomain"+"'"+']/device-group/entry[@name='+"'"+DEVICEGROUP+"'"+']/devices&key='+key - #http opener - opener = createOpener() - # create a request object - panReq = urllib2.Request(panurl) - # make the request to the PAN - try: - xmlresult = opener.open(panReq) - except: - sys.exit(-1) - # create a root by reading the xml results - # save the raw xml - xmlresult = xmlresult.read() - root = ET.fromstring(xmlresult) - # a list of serial numbers - devicegroup = [] - serial = [] - for node in root.getiterator(): - # for the devices node - if node.tag == 'devices': - for child in node.getiterator(): - # add the entry names to the serial list - if child.tag == 'entry': - serial.append(child.attrib['name']) - return serial + '''Interact with PANorama''' + #List device groups: List devices by named Device Group, obtain Serial Numbers (for &target=) + panurl = 'https://'+PAN+'/api/?type=config&action=show&xpath=/config/devices/entry[@name='+"'"+"localhost.localdomain"+"'"+']/device-group/entry[@name='+"'"+DEVICEGROUP+"'"+']/devices&key='+key + #http opener + opener = createOpener() + # create a request object + panReq = urllib2.Request(panurl) + # make the request to the PAN + try: + xmlresult = opener.open(panReq) + except: + sys.exit(-1) + # create a root by reading the xml results + # save the raw xml + xmlresult = xmlresult.read() + root = ET.fromstring(xmlresult) + # a list of serial numbers + devicegroup = [] + serial = [] + for node in root.getiterator(): + # for the devices node + if node.tag == 'devices': + for child in node.getiterator(): + # add the entry names to the serial list + if child.tag == 'entry': + serial.append(child.attrib['name']) + return serial def getKey(PAN, PANUSER, PANPASS): - ''' Logs into the PAN firewall and obtains a PAN session key''' - # create an opener object - opener = createOpener() - try: - # the url for the PAN - panurl = 'https://'+PAN+'/api/?type=keygen&user='+PANUSER+'&password='+PANPASS - print panurl - panReq = urllib2.Request(panurl) - # make the request - req = opener.open(panReq) - except: - sys.exit(-1) - # the result of the URL request - result = req.read() - # get the status of the result - try: - sm = re.search(r"success",result).group(0) - if sm == 'success' : - status = 'success' - except: - sys.exit(-1) - # parse the key from the result - key = result.split("")[0].split("")[1] - return key + ''' Logs into the PAN firewall and obtains a PAN session key''' + # create an opener object + opener = createOpener() + try: + # the url for the PAN + panurl = 'https://'+PAN+'/api/?type=keygen&user='+PANUSER+'&password='+PANPASS + print panurl + panReq = urllib2.Request(panurl) + # make the request + req = opener.open(panReq) + except: + sys.exit(-1) + # the result of the URL request + result = req.read() + # get the status of the result + try: + sm = re.search(r"success",result).group(0) + if sm == 'success' : + status = 'success' + except: + sys.exit(-1) + # parse the key from the result + key = result.split("")[0].split("")[1] + return key def commitConfig(PAN, key): - '''Save the changes made to the address objects''' - # create an opener object - opener = createOpener() - panReq = urllib2.Request('https://'+PAN+'//api/?type=commit&cmd=&key='+key) - req = opener.open(panReq) - return 0 + '''Save the changes made to the address objects''' + # create an opener object + opener = createOpener() + panReq = urllib2.Request('https://'+PAN+'//api/?type=commit&cmd=&key='+key) + req = opener.open(panReq) + return 0 def panoramaCommit(PAN,key): - panurl = 'https://'+PAN+'/api/?type=commit&action=all&cmd='+DEVICEGROUP+'&key='+key - opener = createOpener() - try: - # the url for the PAN - panReq = urllib2.Request(panurl) - # make the request - req = opener.open(panReq) - except: - return "Failed: Commit to Panarama" - # the result of the URL - return req.read() + panurl = 'https://'+PAN+'/api/?type=commit&action=all&cmd='+DEVICEGROUP+'&key='+key + opener = createOpener() + try: + # the url for the PAN + panReq = urllib2.Request(panurl) + # make the request + req = opener.open(panReq) + except: + return "Failed: Commit to Panarama" + # the result of the URL + return req.read() def panoramaAddLinkId(key, device, devicegroup, addrip, addr): - '''Set dynamic address object (with LinkID) at Panorama level''' - #we will use the same name for linkid as the addrIp - #Set dynamic address object (with LinkID) at Panorama level - panurl = 'https://'+device+'/api/?type=config&action=set&xpath=/config/devices/entry[@name='+"'"+'localhost.localdomain'+"'"+']/device-group/entry[@name='+"'"+devicegroup+"'"+']/address/entry[@name='+"'"+addr+"'"+']&element='+addrip+'&key='+key - print "Adding LinkID using:" - print panurl - opener = createOpener() - try: - # the url for the PAN - panReq = urllib2.Request(panurl) - # make the request - req = opener.open(panReq) - except: - return "Failed: Adding LinkId to Panarama" - # the result of the URL - return req.read() - #Set dynamic address object (with LinkID) at Device level, commit - #https://10.5.172.24/api/?type=config&action=set&xpath=/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/address/entry[@name='dynamo']&element=dyn1&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09 - #https://10.5.172.24/api/?type=commit&cmd=&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09 + '''Set dynamic address object (with LinkID) at Panorama level''' + #we will use the same name for linkid as the addrIp + #Set dynamic address object (with LinkID) at Panorama level + panurl = 'https://'+device+'/api/?type=config&action=set&xpath=/config/devices/entry[@name='+"'"+'localhost.localdomain'+"'"+']/device-group/entry[@name='+"'"+devicegroup+"'"+']/address/entry[@name='+"'"+addr+"'"+']&element='+addrip+'&key='+key + print "Adding LinkID using:" + print panurl + opener = createOpener() + try: + # the url for the PAN + panReq = urllib2.Request(panurl) + # make the request + req = opener.open(panReq) + except: + return "Failed: Adding LinkId to Panarama" + # the result of the URL + return req.read() + #Set dynamic address object (with LinkID) at Device level, commit + #https://10.5.172.24/api/?type=config&action=set&xpath=/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/address/entry[@name='dynamo']&element=dyn1&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09 + #https://10.5.172.24/api/?type=commit&cmd=&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09 - #Note, all additional calls listed below are identical whether or not they are applied to panorama or devices, the only differences are the target IP/FQDN and if performing the call directly against a device please remove the &target=serial# section from the end. + #Note, all additional calls listed below are identical whether or not they are applied to panorama or devices, the only differences are the target IP/FQDN and if performing the call directly against a device please remove the &target=serial# section from the end. def panoramaMapIpToLinkId(key, device, results, serial): - '''Map IPs to correct link ID (No commit required)''' - register = '' - for result in results: - register = register + '' - panurl = 'https://'+device+'/api/?type=user-id&action=set&cmd=1.0update'+register+'&key='+key+'&target='+serial - print "Mapping IP to LinkID using:" - print panurl - opener = createOpener() - try: - # the url for the PAN - panReq = urllib2.Request(panurl) - # make the request - req = opener.open(panReq) - except: - import traceback - stack = traceback.format_exc() - print 2 * '\n' - print "Failed: Mapping IP to LinkId" - print panurl - print stack - return -1 - # the result of the URL - return req.read() + '''Map IPs to correct link ID (No commit required)''' + register = '' + for result in results: + register = register + '' + panurl = 'https://'+device+'/api/?type=user-id&action=set&cmd=1.0update'+register+'&key='+key+'&target='+serial + print "Mapping IP to LinkID using:" + print panurl + opener = createOpener() + try: + # the url for the PAN + panReq = urllib2.Request(panurl) + # make the request + req = opener.open(panReq) + except: + import traceback + stack = traceback.format_exc() + print 2 * '\n' + print "Failed: Mapping IP to LinkId" + print panurl + print stack + return -1 + # the result of the URL + return req.read() - #List All Mappings - #https://pm-panorama/api/?type=op&cmd=&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09&target=0006C107916 - # + #List All Mappings + #https://pm-panorama/api/?type=op&cmd=&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09&target=0006C107916 + # - #List Specific Mapping - #https://pm-panorama/api/?type=op&cmd=test-add&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09&target=0006C107916 - # + #List Specific Mapping + #https://pm-panorama/api/?type=op&cmd=test-add&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09&target=0006C107916 + # - #Remove Specific Mapping - #https://pm-panorama/api/?type=user-id&action=set&cmd=1.0update&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09&target=0006C107916 + #Remove Specific Mapping + #https://pm-panorama/api/?type=user-id&action=set&cmd=1.0update&key=LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09&target=0006C107916 def panoramaMapIpToUser(key, device, results, serial): - '''Map IPs to UserID''' - login = '' - for result in results: - login = login + '' - panurl = 'https://'+device+'/api/?type=user-id&action=set&cmd=1.0update'+login+'&key='+key+'&target='+serial - print "Mapping IP to User using:" - print panurl - opener = createOpener() - try: - # the url for the PAN - panReq = urllib2.Request(panurl) - # make the request - req = opener.open(panReq) - except: - print 2 * '\n' - print "Failed: Map IP to user" - print panurl - import traceback - stack = traceback.format_exc() - print stack - return -1 - # the result of the URL - return req.read() + '''Map IPs to UserID''' + login = '' + for result in results: + login = login + '' + panurl = 'https://'+device+'/api/?type=user-id&action=set&cmd=1.0update'+login+'&key='+key+'&target='+serial + print "Mapping IP to User using:" + print panurl + opener = createOpener() + try: + # the url for the PAN + panReq = urllib2.Request(panurl) + # make the request + req = opener.open(panReq) + except: + print 2 * '\n' + print "Failed: Map IP to user" + print panurl + import traceback + stack = traceback.format_exc() + print stack + return -1 + # the result of the URL + return req.read() def panorama(): - '''test function''' - addrip = '1.1.1.1' - addruser = 'monzy' - action = 'list' - devicetype = 'panorama' - print PAN, PANUSER, PANPASS - key = getKey(PAN, PANUSER, PANPASS) - print key - # if device is a firewall - if devicetype == "pan": - print panAction(key) - # if device type is Panorama - elif devicetype == 'panorama': - #print panoramaAddLinkId(key,action,PAN,devicetype, DEVICEGROUP, addrip, addruser) - #get serial numbers for all devices from Panorama - serial = getPanSerial(key,PAN) - print serial - else: - print "unknown device type" - sys.exit(-1) + '''test function''' + addrip = '1.1.1.1' + addruser = 'monzy' + action = 'list' + devicetype = 'panorama' + print PAN, PANUSER, PANPASS + key = getKey(PAN, PANUSER, PANPASS) + print key + # if device is a firewall + if devicetype == "pan": + print panAction(key) + # if device type is Panorama + elif devicetype == 'panorama': + #print panoramaAddLinkId(key,action,PAN,devicetype, DEVICEGROUP, addrip, addruser) + #get serial numbers for all devices from Panorama + serial = getPanSerial(key,PAN) + print serial + else: + print "unknown device type" + sys.exit(-1) def panoramaUpdate(results, PAN, DEVICEGROUP): - PAN = '192.168.4.211' - key = getKey(PAN, PANUSER, PANPASS) - for result in results: - linkid = panoramaAddLinkId(key, PAN, DEVICEGROUP, result["addrip"], result["addrip"]) - print linkid - #commit - print panoramaCommit(PAN, key) - #get serial numbers for all PAN devices from the Panorama - serialnum = getPanSerial(key,PAN) - # update info for each of the devices - for serial in serialnum: - print panoramaMapIpToLinkId(key, PAN, results, serial) - print panoramaMapIpToUser(key, PAN, results, serial) - -def main(): - #panorama() - results = [{"addruser":"monte", "addrip":"2.2.2.2"},{"addruser":"lostha", "addrip":"9.9.9.9"}] - panoramaUpdate(results, PAN,DEVICEGROUP) - -if __name__ == "__main__": - main() + PAN = '192.168.4.211' + key = getKey(PAN, PANUSER, PANPASS) + for result in results: + linkid = panoramaAddLinkId(key, PAN, DEVICEGROUP, result["addrip"], result["addrip"]) + print linkid + #commit + print panoramaCommit(PAN, key) + #get serial numbers for all PAN devices from the Panorama + serialnum = getPanSerial(key,PAN) + # update info for each of the devices + for serial in serialnum: + print panoramaMapIpToLinkId(key, PAN, results, serial) + print panoramaMapIpToUser(key, PAN, results, serial) - +def main(): + #panorama() + results = [{"addruser":"monte", "addrip":"2.2.2.2"},{"addruser":"lostha", "addrip":"9.9.9.9"}] + panoramaUpdate(results, PAN,DEVICEGROUP) - \ No newline at end of file +if __name__ == "__main__": + main() diff --git a/bin/panoramaUserUpdate.py b/bin/panoramaUserUpdate.py index 5c437640..1fafd2fc 100644 --- a/bin/panoramaUserUpdate.py +++ b/bin/panoramaUserUpdate.py @@ -1,5 +1,5 @@ ########################################### -# Version 0.1 +# Version 0.1 # author: monzy # About this script: # Adds Dynamic Address objects and maps users to thoese objects as a result of a splunk search command @@ -9,7 +9,7 @@ ############################################ # How to Use this script # index=main sourcetype=radius | panupdate device="192.168.4.211" devicegroup="homedev" -# Search radius logs for userid and associated ip's. +# Search radius logs for userid and associated ip's. # Panupdate: the Panorama ip is 192.168.4.211, and we want to update devicegroup called homedev # NOTE: The Panorama's admin user's credentials must be set using the app's setup # NOTE: The devicegroup must exist in the Panorama prior to executing this command @@ -19,9 +19,9 @@ # Very limited error checking # Errors may not be reported in the Splunk UI # ONLY works with PANORAMA at this time -# Device group have to be pre-populated +# Device group have to be pre-populated # The panoramaMapIpToUser function contains a timeout setting (in minutes). It is currently set to 30. This is the duration for which the user-id to ip mapping will exist. Change this value to suit your business requirements. -# '' +# '' ########################################### ############################# # Change the values below to suit your PAN configuration @@ -33,7 +33,7 @@ PANO = "Panorama" # admin account for the PAN device PANUSER = '' -# password for the admin user. +# password for the admin user. # any special characters in the password must be URL/percent-encoded. PANPASS = '' # this is the device group where changes will take effect @@ -43,7 +43,7 @@ # if you DO want to go through a proxy, e.g., HTTP_PROXY={squid:'2.2.2.2'} HTTP_PROXY = {} ######################################################### -# Do NOT modify anything below this line unless you are +# Do NOT modify anything below this line unless you are # certain of the ramifications of the changes ######################################################### import splunk.Intersplunk # so you can interact with Splunk @@ -56,191 +56,191 @@ import traceback def createOpener(): - '''Create a generic opener for http - This is particularly helpful when there is a proxy server in line''' - # Thanks to: http://www.decalage.info/en/python/urllib2noproxy - proxy_handler = urllib2.ProxyHandler(HTTP_PROXY) - opener = urllib2.build_opener(proxy_handler) - urllib2.install_opener(opener) - return opener + '''Create a generic opener for http + This is particularly helpful when there is a proxy server in line''' + # Thanks to: http://www.decalage.info/en/python/urllib2noproxy + proxy_handler = urllib2.ProxyHandler(HTTP_PROXY) + opener = urllib2.build_opener(proxy_handler) + urllib2.install_opener(opener) + return opener def getCredentials(sessionKey): - '''Given a splunk sesionKey returns a clear text user name and password from a splunk password container''' - # this is the folder name for the app and not the app's common name - myapp = 'SplunkforPaloAltoNetworks' - try: - # list all credentials - entities = entity.getEntities(['admin', 'passwords'], namespace=myapp, owner='nobody', sessionKey=sessionKey) - except Exception, e: - stack = traceback.format_exc() - logger.warn(stack) - logger.warn("entity exception") - raise Exception("Could not get %s credentials from splunk. Error: %s" % (myapp, str(e))) - # return first set of credentials - for i, c in entities.items(): - return c['username'], c['clear_password'] - logger.warn("No credentials") - raise Exception("No credentials have been found") + '''Given a splunk sesionKey returns a clear text user name and password from a splunk password container''' + # this is the folder name for the app and not the app's common name + myapp = 'SplunkforPaloAltoNetworks' + try: + # list all credentials + entities = entity.getEntities(['admin', 'passwords'], namespace=myapp, owner='nobody', sessionKey=sessionKey) + except Exception, e: + stack = traceback.format_exc() + logger.warn(stack) + logger.warn("entity exception") + raise Exception("Could not get %s credentials from splunk. Error: %s" % (myapp, str(e))) + # return first set of credentials + for i, c in entities.items(): + return c['username'], c['clear_password'] + logger.warn("No credentials") + raise Exception("No credentials have been found") def getPanSerial(key, PAN): - '''Get the Serial Numbers from the Panorama''' - #List device groups: List devices by named Device Group, obtain Serial Numbers (for &target=) - panurl = 'https://'+PAN+'/api/?type=config&action=show&xpath=/config/devices/entry[@name='+"'"+"localhost.localdomain"+"'"+']/device-group/entry[@name='+"'"+DEVICEGROUP+"'"+']/devices&key='+key - # create a request object - panReq = urllib2.Request(panurl) - # make the request to the PAN - try: - xmlresult = opener.open(panReq) - except: - sys.exit(-1) - # create a root by reading the xml results - # save the raw xml - xmlresult = xmlresult.read() - root = ET.fromstring(xmlresult) - # a list of serial numbers - devicegroup = [] - serial = [] - for node in root.getiterator(): - # for the devices node - if node.tag == 'devices': - for child in node.getiterator(): - # add the entry names to the serial list - if child.tag == 'entry': - serial.append(child.attrib['name']) - return serial + '''Get the Serial Numbers from the Panorama''' + #List device groups: List devices by named Device Group, obtain Serial Numbers (for &target=) + panurl = 'https://'+PAN+'/api/?type=config&action=show&xpath=/config/devices/entry[@name='+"'"+"localhost.localdomain"+"'"+']/device-group/entry[@name='+"'"+DEVICEGROUP+"'"+']/devices&key='+key + # create a request object + panReq = urllib2.Request(panurl) + # make the request to the PAN + try: + xmlresult = opener.open(panReq) + except: + sys.exit(-1) + # create a root by reading the xml results + # save the raw xml + xmlresult = xmlresult.read() + root = ET.fromstring(xmlresult) + # a list of serial numbers + devicegroup = [] + serial = [] + for node in root.getiterator(): + # for the devices node + if node.tag == 'devices': + for child in node.getiterator(): + # add the entry names to the serial list + if child.tag == 'entry': + serial.append(child.attrib['name']) + return serial def getKey(PAN, PANUSER, PANPASS): - ''' Logs into the PAN firewall and obtains a PAN session key''' - try: - # the url for the PAN - panurl = 'https://'+PAN+'/api/?type=keygen&user='+PANUSER+'&password='+PANPASS - panReq = urllib2.Request(panurl) - # make the request - req = opener.open(panReq) - except: - stack = traceback.format_exc() - logger.warn(stack) - sys.exit(-1) - # the result of the URL request - result = req.read() - # get the status of the result - try: - sm = re.search(r"success",result).group(0) - if sm == 'success' : - status = 'success' - except: - stack = traceback.format_exc() - logger.warn(stack) - sys.exit(-1) - # parse the key from the result - key = result.split("")[0].split("")[1] - return key + ''' Logs into the PAN firewall and obtains a PAN session key''' + try: + # the url for the PAN + panurl = 'https://'+PAN+'/api/?type=keygen&user='+PANUSER+'&password='+PANPASS + panReq = urllib2.Request(panurl) + # make the request + req = opener.open(panReq) + except: + stack = traceback.format_exc() + logger.warn(stack) + sys.exit(-1) + # the result of the URL request + result = req.read() + # get the status of the result + try: + sm = re.search(r"success",result).group(0) + if sm == 'success' : + status = 'success' + except: + stack = traceback.format_exc() + logger.warn(stack) + sys.exit(-1) + # parse the key from the result + key = result.split("")[0].split("")[1] + return key def commitConfig(PAN, key): - '''Save the changes made to the address objects''' - panReq = urllib2.Request('https://'+PAN+'//api/?type=commit&cmd=&key='+key) - req = opener.open(panReq) - return 0 + '''Save the changes made to the address objects''' + panReq = urllib2.Request('https://'+PAN+'//api/?type=commit&cmd=&key='+key) + req = opener.open(panReq) + return 0 def panoramaCommit(PAN,key): - '''Commit the currently loaded updates''' - panurl = 'https://'+PAN+'/api/?type=commit&action=all&cmd='+DEVICEGROUP+'&key='+key - try: - # the url for the PAN - panReq = urllib2.Request(panurl) - # make the request - req = opener.open(panReq) - except: - stack = traceback.format_exc() - logger.warn(stack) - sys.exit(-1) - # the result of the URL - return req.read() + '''Commit the currently loaded updates''' + panurl = 'https://'+PAN+'/api/?type=commit&action=all&cmd='+DEVICEGROUP+'&key='+key + try: + # the url for the PAN + panReq = urllib2.Request(panurl) + # make the request + req = opener.open(panReq) + except: + stack = traceback.format_exc() + logger.warn(stack) + sys.exit(-1) + # the result of the URL + return req.read() def panoramaAddLinkId(key, device, devicegroup, addrip): - '''Set dynamic address object (with LinkID) at Panorama level''' - #we will use the same name for linkid as the addrIp - #Set dynamic address object (with LinkID) at Panorama level - panurl = 'https://'+device+'/api/?type=config&action=set&xpath=/config/devices/entry[@name='+"'"+'localhost.localdomain'+"'"+']/device-group/entry[@name='+"'"+devicegroup+"'"+']/address/entry[@name='+"'"+addrip+"'"+']&element='+addrip+'&key='+key - logger.info("Adding LinkID using:") - logger.info(panurl) - try: - # the url for the PAN - panReq = urllib2.Request(panurl) - # make the request - req = opener.open(panReq) - except: - stack = traceback.format_exc() - logger.warn(stack) - # the response from the url request - logger.info(req.read()) - return 0 + '''Set dynamic address object (with LinkID) at Panorama level''' + #we will use the same name for linkid as the addrIp + #Set dynamic address object (with LinkID) at Panorama level + panurl = 'https://'+device+'/api/?type=config&action=set&xpath=/config/devices/entry[@name='+"'"+'localhost.localdomain'+"'"+']/device-group/entry[@name='+"'"+devicegroup+"'"+']/address/entry[@name='+"'"+addrip+"'"+']&element='+addrip+'&key='+key + logger.info("Adding LinkID using:") + logger.info(panurl) + try: + # the url for the PAN + panReq = urllib2.Request(panurl) + # make the request + req = opener.open(panReq) + except: + stack = traceback.format_exc() + logger.warn(stack) + # the response from the url request + logger.info(req.read()) + return 0 def panoramaMapIpToLinkId(key, device, results, serial): - '''Map IPs to correct link ID (No commit required)''' - register = '' - for result in results: - register = '' - panurl = 'https://'+device+'/api/?type=user-id&action=set&cmd=1.0update'+register+'&key='+key+'&target='+serial - logger.info("Mapping IP to LinkID using:") - logger.info(panurl) - try: - # the url for the PAN - panReq = urllib2.Request(panurl) - # make the request - req = opener.open(panReq) - except: - stack = traceback.format_exc() - logger.info(stack) - return -1 - # the response from the url request - logger.info(req.read()) - return 0 - + '''Map IPs to correct link ID (No commit required)''' + register = '' + for result in results: + register = '' + panurl = 'https://'+device+'/api/?type=user-id&action=set&cmd=1.0update'+register+'&key='+key+'&target='+serial + logger.info("Mapping IP to LinkID using:") + logger.info(panurl) + try: + # the url for the PAN + panReq = urllib2.Request(panurl) + # make the request + req = opener.open(panReq) + except: + stack = traceback.format_exc() + logger.info(stack) + return -1 + # the response from the url request + logger.info(req.read()) + return 0 + def panoramaMapIpToUser(key, device, results, serial): - '''Map IPs to UserID''' - login = '' - for result in results: - login = '' - panurl = 'https://'+device+'/api/?type=user-id&action=set&cmd=1.0update'+login+'&key='+key+'&target='+serial - logger.info("Mapping IP to User using:") - logger.info(panurl) - try: - # the url for the PAN - panReq = urllib2.Request(panurl) - # make the request - req = opener.open(panReq) - except: - stack = traceback.format_exc() - logger.info(stack) - sys.exit(-1) - # the result of the URL - # the response from the url request - logger.info(req.read()) - return 0 + '''Map IPs to UserID''' + login = '' + for result in results: + login = '' + panurl = 'https://'+device+'/api/?type=user-id&action=set&cmd=1.0update'+login+'&key='+key+'&target='+serial + logger.info("Mapping IP to User using:") + logger.info(panurl) + try: + # the url for the PAN + panReq = urllib2.Request(panurl) + # make the request + req = opener.open(panReq) + except: + stack = traceback.format_exc() + logger.info(stack) + sys.exit(-1) + # the result of the URL + # the response from the url request + logger.info(req.read()) + return 0 def panoramaUpdate(results, PAN, DEVICEGROUP): - '''Given search results, updates the PAN's device group with the addrip and addruser ''' - #PAN = '192.168.4.211' Good catch Jeff Hillon PAN - key = getKey(PAN, PANUSER, PANPASS) - # create LinkIDs - for result in results: - linkid = panoramaAddLinkId(key, PAN, DEVICEGROUP, result["addrip"]) - logger.info(linkid) - #commit - commit = panoramaCommit(PAN, key) - logger.info(commit) - #get serial numbers for all PAN devices from the Panorama - serialnum = getPanSerial(key,PAN) - # update info for each of the devices - for serial in serialnum: - #Map IP to LinkID - panoramaMapIpToLinkId(key, PAN, results, serial) - #Map IP to User - panoramaMapIpToUser(key, PAN, results, serial) - return 0 - + '''Given search results, updates the PAN's device group with the addrip and addruser ''' + #PAN = '192.168.4.211' Good catch Jeff Hillon PAN + key = getKey(PAN, PANUSER, PANPASS) + # create LinkIDs + for result in results: + linkid = panoramaAddLinkId(key, PAN, DEVICEGROUP, result["addrip"]) + logger.info(linkid) + #commit + commit = panoramaCommit(PAN, key) + logger.info(commit) + #get serial numbers for all PAN devices from the Panorama + serialnum = getPanSerial(key,PAN) + # update info for each of the devices + for serial in serialnum: + #Map IP to LinkID + panoramaMapIpToLinkId(key, PAN, results, serial) + #Map IP to User + panoramaMapIpToUser(key, PAN, results, serial) + return 0 + # setup the logger. $SPLUNK_HOME/var/log/splunk/python.log logger = dcu.getLogger() # create a global http opener @@ -249,12 +249,12 @@ def panoramaUpdate(results, PAN, DEVICEGROUP): args, kwargs = splunk.Intersplunk.getKeywordsAndOptions() #parse the kwargs if kwargs.has_key('device'): - PAN = kwargs['device'] + PAN = kwargs['device'] if kwargs.has_key('devicegroup'): - DEVICEGROUP = kwargs['devicegroup'] + DEVICEGROUP = kwargs['devicegroup'] else: - logger.warn("You did not specify a Panorama device name or IP in the splunk command") - sys.exit(-1) + logger.warn("You did not specify a Panorama device name or IP in the splunk command") + sys.exit(-1) # an empty dictionary will be used to hold system values settings = dict() @@ -265,16 +265,16 @@ def panoramaUpdate(results, PAN, DEVICEGROUP): sessionKey = settings['sessionKey'] # get the Panorama user and password from Splunk using the sessionKey PANUSER, PANPASS = getCredentials(sessionKey) -# Copying the results in a new dict. We don't want to update and commit individual results against the Panorama. +# Copying the results in a new dict. We don't want to update and commit individual results against the Panorama. ipandusers = [] for result in results: - if result.has_key("addrip") and result.has_key("addruser"): - pair = {"addrip":result["addrip"],"addruser":result["addruser"]} - ipandusers.append(pair) + if result.has_key("addrip") and result.has_key("addruser"): + pair = {"addrip":result["addrip"],"addruser":result["addruser"]} + ipandusers.append(pair) panoramaUpdate(ipandusers, PAN, DEVICEGROUP) try: - splunk.Intersplunk.outputResults([{"result":"Your request has been submitted. It might take up to a minute for changes to take effect"}]) + splunk.Intersplunk.outputResults([{"result":"Your request has been submitted. It might take up to a minute for changes to take effect"}]) except: - stack = traceback.format_exc() - logger.warn(stack) \ No newline at end of file + stack = traceback.format_exc() + logger.warn(stack) From 748b70ff6fd9d31ce87c44ee5da46f1829ffce55 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Thu, 27 Jun 2013 15:27:34 -0700 Subject: [PATCH 02/27] Modified order of Content Dashboard menu items to better align with PANW web gui. --- default/data/ui/nav/default.xml | 6 ++++-- default/data/ui/views/url_filtering.xml | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/default/data/ui/nav/default.xml b/default/data/ui/nav/default.xml index 81cb1b28..e1e3c6af 100755 --- a/default/data/ui/nav/default.xml +++ b/default/data/ui/nav/default.xml @@ -39,11 +39,12 @@ - + - Search Threat Data + Search URL Data + Search Data Filtering Data @@ -64,6 +65,7 @@ + + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` + action="$click.value$" + + + + + + + + | tstats count(dst_ip) AS cti FROM pan_wildfire WHERE * $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby dst_ip + + Top Dest IP + pie + bottom + + 100% + 250px + + + + wildfire_overview?earliest=$earliest$&latest=$latest$&form.dst_ip=$click.value$ + + + + + + + + + + + + + + From f216d1dfd60df84d9c8c26528ad4752c48d4ba07 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Thu, 27 Jun 2013 15:58:04 -0700 Subject: [PATCH 09/27] Created a saved search to index WildFire logs into a tscollect index for display on the WildFire Dashboard. Created another saved search to run the wildfirereport command to retrieve and index the reports in a summary index. --- default/savedsearches.conf | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/default/savedsearches.conf b/default/savedsearches.conf index 52559ff7..28f1f27f 100755 --- a/default/savedsearches.conf +++ b/default/savedsearches.conf @@ -62,14 +62,24 @@ request.ui_dispatch_view = flashtimeline search = `pan_data_filtering` | table _time src_user dst_location src_ip dst_ip app threat_id action vsys | tscollect namespace=pan_data disabled = 0 -[PAN - Data Filtering - Collect] +[PAN - WildFire - Collect] cron_schedule = */5 * * * * dispatch.earliest_time = -5m@m displayview = flashtimeline enableSched = 1 realtime_schedule = 0 request.ui_dispatch_view = flashtimeline -search = `pan_data_filtering` | table _time src_user dst_location src_ip dst_ip app threat_id action vsys | tscollect namespace=pan_data +search = `pan_wildfire` | table _time src_user dst_location src_ip dst_ip category app threat_id action vsys misc | tscollect namespace=pan_wildfire +disabled = 0 + +[PAN - WildFire Reports - Collect] +cron_schedule = */1 * * * * +dispatch.earliest_time = -1m@m +displayview = flashtimeline +enableSched = 1 +realtime_schedule = 0 +request.ui_dispatch_view = flashtimeline +search = `pan_wildfire` | rex field=threat_id "\((?\d+)\)" | wildfirereport | table wildfire_report | rename wildfire_report AS _raw | collect index=pan_logs sourcetype=pan_wildfire_report disabled = 0 [PAN - System - Collect] From 00b25957d4e37a2d45680547de6b94ecef87d59f Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Thu, 27 Jun 2013 16:00:50 -0700 Subject: [PATCH 10/27] Added a field for the API key to the app setup page. --- default/setup.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/default/setup.xml b/default/setup.xml index c7290bde..1cff29f3 100644 --- a/default/setup.xml +++ b/default/setup.xml @@ -11,4 +11,24 @@ password + + Used to retrieve reports from the WildFire Cloud. API Key is available from the WildFire Portal (https://wildfire.paloaltonetworks.com) under My Account. + + + + text + + + + text + + + $(function(){ + var username_div = $('#item-\\/storage\\/passwords\\/_new\\/name'); + username_div.hide(); + var username_input = $('#\\/storage\\/passwords\\/_new\\/name_id'); + username_input.val('wildfire_api_key'); + }); + ]]> + From d8a6b96eb5cc5aa15fb26afe487c10ca7a27a400 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Thu, 27 Jun 2013 18:00:41 -0700 Subject: [PATCH 11/27] Script adds report_id to the XML for correlation with original WildFire log from firewall. --- bin/retrieveWildFireReport.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/retrieveWildFireReport.py b/bin/retrieveWildFireReport.py index 21c90fe7..562196ef 100644 --- a/bin/retrieveWildFireReport.py +++ b/bin/retrieveWildFireReport.py @@ -104,6 +104,8 @@ def retrieveWildFireData(apikey, serial, reportid): try: # get the report wfReportXml = retrieveWildFireData(PAN_WF_APIKEY, result['serial_number'], result['report_id']).read().strip() + # Add the report id to the XML for correlation to the original WildFire log from the firewall + wfReportXml = wfReportXml.replace("", "\n "+result['report_id']+"", 1) result['wildfire_report'] = wfReportXml except: logger.warn("Error retrieving WildFire report for report id: %s" % result['report_id']) From d02bfc69a2416fed8c81b82a1b53c966f2abc7fc Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Fri, 28 Jun 2013 15:55:56 -0700 Subject: [PATCH 12/27] Enhanced WildFire Dashboard and corresponding searches. Added 'Malware Traffic' view. --- default/data/ui/views/wildfire_overview.xml | 88 +++++++++++++-------- default/savedsearches.conf | 14 +++- 2 files changed, 67 insertions(+), 35 deletions(-) diff --git a/default/data/ui/views/wildfire_overview.xml b/default/data/ui/views/wildfire_overview.xml index b7221e55..d37d8c87 100755 --- a/default/data/ui/views/wildfire_overview.xml +++ b/default/data/ui/views/wildfire_overview.xml @@ -150,7 +150,7 @@ | tstats count(action) AS cact FROM pan_wildfire WHERE * $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby _time action span=5m | timechart values(cact) by action - WildFire Event Actions + All WildFire Events by Action column stacked bottom @@ -158,41 +158,36 @@ 100% - - wildfire_overview?earliest=$earliest$&latest=$latest$&form.action=$click.name2$ - + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_wildfire`" - - |`tstats` count(dst_user) AS ca FROM pan_wildfire WHERE * $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby dst_user dst_ip misc | stats values(ca) AS Count by dst_user dst_ip misc | + + |`tstats` count(dst_user) AS ca FROM pan_wildfire WHERE * category="malicious" $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby dst_ip action misc dst_user | table dst_ip dst_user misc action | rename dst_user AS "Destination User" | rename dst_ip AS "Destination IP" | + rename action AS Action | rename misc AS Filename - 10 + 5 results True all - ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` - dst_user="$row.Destination User$" - dst_ip="$row.Destination IP$" - misc="$row.Filename$" - + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` dst_user="$row.Destination User$" dst_ip="$row.Destination IP$" misc="$row.Filename$" - - | tstats count(misc) AS cti FROM pan_wildfire WHERE * $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby misc + + | tstats count(misc) AS cti FROM pan_wildfire WHERE * category="malicious" $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby misc - Top Filenames + Top Malware pie bottom @@ -208,10 +203,10 @@ - - | tstats count(app) AS ca FROM pan_wildfire WHERE * $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby app + + | tstats count(app) AS ca FROM pan_wildfire WHERE * category="malicious" $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby app - WildFire Events by App + Malware Downloads by App pie bottom @@ -227,10 +222,10 @@ - - | tstats count(dst_user) AS cti FROM pan_wildfire WHERE * $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ $dst_user$ groupby dst_user + + | tstats count(dst_user) AS cti FROM pan_wildfire WHERE * category="malicious" $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ $dst_user$ groupby dst_user - WildFire Events by User + Malware Downloaders by User pie bottom @@ -246,10 +241,10 @@ - - | tstats count(src_ip) AS csrc FROM pan_wildfire WHERE * $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby src_ip + + | tstats count(src_ip) AS csrc FROM pan_wildfire WHERE * category="malicious" $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby src_ip - Top Source IP + Top Malware Sources pie bottom @@ -265,13 +260,13 @@ - - |`tstats` count(action) AS cact FROM pan_wildfire WHERE * (action=wildfire-upload-success OR action=wildfire-upload-skip) $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby action | + + |`tstats` count(action) AS cact FROM pan_wildfire WHERE * category="malicious" (action=wildfire-upload-success OR action=wildfire-upload-skip) $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby action | replace wildfire-upload-success with "New Sample" in action | - replace wildfire-upload-skip with "Previously Analyzed" in action + replace wildfire-upload-skip with "Known" in action - Known Malware + Previously Analyzed Malware pie bottom @@ -280,18 +275,17 @@ - ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` - action="$click.value$" + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` action="$click.value$" - - | tstats count(dst_ip) AS cti FROM pan_wildfire WHERE * $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby dst_ip + + | tstats count(dst_ip) AS cti FROM pan_wildfire WHERE * category="malicious" $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby dst_ip - Top Dest IP + Top Malware Downloaders by IP pie bottom @@ -307,6 +301,34 @@ + + |`tstats` count(dst_ip) AS cdip FROM pan_wildfire_report_malware_traffic WHERE * NOT (protocol=udp AND dst_port=53) groupby dst_ip dst_port report_id protocol | table report_id dst_ip dst_port protocol | + join protocol dst_ip dst_port [ |`tstats` count(src_ip) FROM pan_traffic WHERE * NOT (protocol=udp AND dst_port=53) groupby src_ip dst_ip dst_port protocol src_user | table src_ip src_user dst_port dst_ip protocol ] | + rename src_user AS "User" | rename src_ip AS "Compromised IP" | + join type=left report_id [|`tstats` count(report_id) AS crid FROM pan_wildfire WHERE * groupby report_id misc | table report_id misc ] | + table "Compromised IP" "User" misc dst_ip dst_port protocol | + rename misc AS "Malware File" | + rename dst_ip AS "Traffic Dst_IP" | + rename dst_port AS "Traffic Dst_Port" | + rename protocol AS "Protocol" + + 10 + results + + True + all + + + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` src_user="$row.User$" src_ip="$row.Compromised IP$" dst_ip="$row.Traffic Dst_IP$" dst_port="$row.Traffic Dst_Port$" protocol="$row.Protocol$" + + + + + + These hosts attempted a connection to servers known to be accessed by malware programs detected by the firewall. These hosts may be compromised as they show network behavior consistent with an analyzed malware sample. This data is based on a correlation of traffic logs and malware network behavior from WildFire Cloud analysis reports. (Note: This correlation requires a WildFire API Key]]>) + + + diff --git a/default/savedsearches.conf b/default/savedsearches.conf index 28f1f27f..ac244444 100755 --- a/default/savedsearches.conf +++ b/default/savedsearches.conf @@ -69,10 +69,10 @@ displayview = flashtimeline enableSched = 1 realtime_schedule = 0 request.ui_dispatch_view = flashtimeline -search = `pan_wildfire` | table _time src_user dst_location src_ip dst_ip category app threat_id action vsys misc | tscollect namespace=pan_wildfire +search = `pan_wildfire` | rex field=threat_id "\((?\d+)\)" | table _time report_id dst_user dst_location src_ip dst_ip category app threat_id action vsys misc | tscollect namespace=pan_wildfire disabled = 0 -[PAN - WildFire Reports - Collect] +[PAN - WildFire Reports - Retrieve] cron_schedule = */1 * * * * dispatch.earliest_time = -1m@m displayview = flashtimeline @@ -82,6 +82,16 @@ request.ui_dispatch_view = flashtimeline search = `pan_wildfire` | rex field=threat_id "\((?\d+)\)" | wildfirereport | table wildfire_report | rename wildfire_report AS _raw | collect index=pan_logs sourcetype=pan_wildfire_report disabled = 0 +[PAN - WildFire Reports Malware Traffic - Collect] +cron_schedule = */5 * * * * +dispatch.earliest_time = -5m@m +displayview = flashtimeline +enableSched = 1 +realtime_schedule = 0 +request.ui_dispatch_view = flashtimeline +search = `pan_wildfire_report` wildfire.report.malware=yes | rename wildfire.report.id AS report_id | rename wildfire.report.network.TCP{@ip} AS tcp_ip | rename wildfire.report.network.TCP{@port} AS tcp_port | rename wildfire.report.network.UDP{@ip} AS udp_ip | rename wildfire.report.network.UDP{@port} AS udp_port | fillnull value="" tcp_ip tcp_port udp_ip udp_port | eval tcp_ip_marked = mvjoin(tcp_ip,"-tcp,") | eval tcp_ip_marked = replace(tcp_ip_marked,"$","-tcp,") | eval udp_ip_marked = mvjoin(udp_ip,"-udp,") | eval udp_ip_marked = replace(udp_ip_marked,"$","-udp,") | eval dst_ip = tcp_ip_marked+","+udp_ip_marked | eval tcp_port_str=mvjoin(tcp_port,",") | eval udp_port_str=mvjoin(udp_port,",") | eval dst_port = tcp_port_str+","+udp_port_str | makemv delim="," dst_ip | makemv delim="," dst_port | eval dst_ip_port = mvzip(dst_ip,dst_port) | mvexpand dst_ip_port | rex field=dst_ip_port "(?[^,-]+)-(?[\w]{3}),(?[^,-]+)" | table _time report_id dst_ip dst_port protocol | tscollect namespace=pan_wildfire_report_malware_traffic +disabled = 0 + [PAN - System - Collect] cron_schedule = */5 * * * * dispatch.earliest_time = -5m@m From 2afe4188c5d6b1f9dfbf442d75eba02865bafb93 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Fri, 28 Jun 2013 15:56:56 -0700 Subject: [PATCH 13/27] Better help text describing how to change credentials. --- default/setup.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/default/setup.xml b/default/setup.xml index 1cff29f3..49ed1394 100644 --- a/default/setup.xml +++ b/default/setup.xml @@ -12,7 +12,7 @@ - Used to retrieve reports from the WildFire Cloud. API Key is available from the WildFire Portal (https://wildfire.paloaltonetworks.com) under My Account. + Used to retrieve reports from the WildFire Cloud. API Key is available from the WildFire Portal (https://wildfire.paloaltonetworks.com) under My Account]]>. @@ -31,4 +31,8 @@ }); ]]> + + Note: To change the Credentials or WildFire API Key later, first remove the entry from SPLUNK_HOME/etc/apps/SplunkforPaloAltoNetworks/local/app.conf. + + From a6af0a27f79b3ca07941ea36b2a2092e6ab9f7a6 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Fri, 28 Jun 2013 16:21:44 -0700 Subject: [PATCH 14/27] Adjusted WildFire recent events widget to show recent events instead of grouped events. --- default/data/ui/views/wildfire_overview.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/default/data/ui/views/wildfire_overview.xml b/default/data/ui/views/wildfire_overview.xml index d37d8c87..8340efa2 100755 --- a/default/data/ui/views/wildfire_overview.xml +++ b/default/data/ui/views/wildfire_overview.xml @@ -165,13 +165,13 @@ - |`tstats` count(dst_user) AS ca FROM pan_wildfire WHERE * category="malicious" $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby dst_ip action misc dst_user | table dst_ip dst_user misc action | + |`tstats` count(dst_user) AS ca FROM pan_wildfire WHERE * category="malicious" $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby _time dst_ip action misc dst_user | table _time dst_ip dst_user misc action | rename dst_user AS "Destination User" | rename dst_ip AS "Destination IP" | rename action AS Action | rename misc AS Filename - 5 + 7 results True From bb72dbb24ca7ff884ab9a33c798981f9d4c48c3a Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Fri, 28 Jun 2013 17:02:15 -0700 Subject: [PATCH 15/27] Convert README to Markdown format --- README.md | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.txt | 114 ---------------------------------------------- 2 files changed, 129 insertions(+), 114 deletions(-) create mode 100755 README.md delete mode 100755 README.txt diff --git a/README.md b/README.md new file mode 100755 index 00000000..48f445d9 --- /dev/null +++ b/README.md @@ -0,0 +1,129 @@ + +Splunk for Palo Alto Networks App +================================= + +## Description ## + +Field extractions and sample reports, +and dashboards for the Palo Alto +Networks Firewall + +#### Version #### + +* Splunk Version: 5.x +* App Version: 3.2.1 +* Last Modified: May 2013 +* Authors: Monzy Merza - Splunk, Inc. + +#### Credits #### + +Many Thanks to Contributors, Advisors, Testers: + +* Joel 'JayKul' Bennett, David Dorsey +* David Hazekamp, Mike Munn, Adam Sealey +* David Markquardt, Gerald Kannapathy +* Will Hayes, Marc Benoit, Jeff Hillon +* Genti Zaimi + +#### Support #### + +For fastest response to support, setup, help +or feedback, please post to +answers.splunk.com and tag your questions +with 'palo' or paloalto' + +Alternatively, contact: bd-sec@splunk.com + +## IMPORTANT ## + +This app ONLY works on Splunk 5.x + +## Dependencies ## + +The app requires the following Splunk Apps available from Splunk Base [http://splunk-base.splunk.com/apps/] (http://splunk-base.splunk.com/apps/) : + +- [Splunk for use with AMMAP Flash maps] (http://splunk-base.splunk.com/apps/22372/splunk-for-use-with-ammap-flash-maps) +- [Google Maps] (http://splunk-base.splunk.com/apps/22365/google-maps) +- [Geo Location Lookup Script] (http://splunk-base.splunk.com/apps/22282/geo-location-lookup-script-powered-by-maxmind) + +You do not need to install these apps if you do not wish to use the Apps mapping and geo location features. The main dashboard will not render properly without the above apps. + +## Installing ## + +Ensure that the apps listed in the Dependencies section are installed. + +To install this app: + +- Unpack the tar ball into `$SPLUNK_HOME/etc/apps` +- Restart Splunk + +Note + +- After restart, it can take up to 5 minutes for new data to show up. +- For older data, you can use the backfill feature of splunk to backfill the summary index: + +[http://www.splunk.com/base/Documentation/latest/Knowledge/Managesummaryindexgapsandoverlaps#Use_the_backfill_script_to_add_other_data_or_fill_summary_index_gaps](http://www.splunk.com/base/Documentation/latest/Knowledge/Managesummaryindexgapsandoverlaps#Use_the_backfill_script) + +## Configuring ## + +Setup Screen and Custom Commands: +The first time you run the app from the web ui, you will be presented with a setup screen. The credentials are only needed if you wish to use the panblock and panupdate custom commands. These passwords will be stored in Splunk. The same way as other splunk credentials are stored. If you do not wish to use the custom commands, you can leave this page blank or enter garbage values. + +To get the firewall data into Splunk: +IMPORTANT: When you configure the input port, you must set the sourcetype of the firewall data to pan_log and the index to pan_logs. + +From the web ui: + +Manager -> Data Inputs -> UDP -> New -> UDP port: + + Palo Alto Networks firewalls default to UDP. + Source type: Set Sourcetype From list: + Select Sourcetype: pan_log -> More -> Index: pan_logs + +For details, [http://www.splunk.com/base/Documentation/latest/admin/MonitorNetworkPorts](http://www.splunk.com/base/Documentation/latest/admin/MonitorNetworkPorts +) + +### Input configuration via inputs.conf ### + +- Edit `$SPLUNK_HOME/etc/apps/SplunkforPaloAltoNetworks/local/inputs.conf` + +Example: (Palo Alto Networks firewalls default to udp port 514) + + [udp://514] + index= pan_logs + connection_host = ip + sourcetype = pan_log + no_appending_timestamp = true + +- Next, configure the firewall device to direct log traffic to the Splunk server on the network port that you specified. + +- Refer to the Palo Alto documentation for details on PAN log forwarding. The Palo Alto devices have a variety of different logs. This app works with the default log configuration. If you use any customized log types that are not defined in the Palo Alto syslog configuration documentation (PANOS-Syslog-Integration-TN-RevM), some of the apps features may not work. + +### Source types ### + +As Splunk indexes your Palo Alto Networks firewall data, the app will rename the sourcetypes to pan_threat, pan_traffic, pan_config, and pan_system depending on the logging facility. + +### High Performance Value Store (HPVS) ### + +The app uses the HPVS feature introduced in Splunk 5.0. This feature provides a tremendous performance improvement for dashboards and views. The views and dashboards make use of saved searches that store data on your search head. This means that disk storage on your search head will be consumed as a result of these searches. If you turn off these saved searches, your dashboards will not render. Or dashboard rendering will be really, really slow. Please post a question to answers.splunk.com if you'd like to explore alternatives. + +### Lookups ### + +Lookups are provided for the threat_id and app field to provide additional information about threats and applications on the network. + +### Using the form fields on the dashboards ### + +All the dashboards work without any filtering values for the form fields. If you want to filter based on a field you should use asterisks before and after the search terms unless you are absolutely sure of the filter value. e.g. In the Content Filtering View, if you want to filter results by the virtual system called 'vsys1', a good practice would be to enter `#vsys1#` in the Virtual System field. + +Keep in mind that searches that have longer time ranges may take a little longer to return the results. + +## What's new in this version ## + +- Malware analysis reports from the WildFire Cloud are dynamically downloaded and indexed when a WildFire log is recieved from a firewall. +- WildFire dashboard + - Recent WildFire events + - Graphs of WildFire statistical data + - Detect compromised hosts using malware behavior to traffic log correlation + +Note: Malware analysis report retrieval requires a WildFire API Key from [https://wildfire.paloaltonetworks.com](https://wildfire.paloaltonetworks.com). + diff --git a/README.txt b/README.txt deleted file mode 100755 index 23089c85..00000000 --- a/README.txt +++ /dev/null @@ -1,114 +0,0 @@ -# ### ### ### ### ### ### ### ### ### ### ### ### ## -# ## -# Splunk for Palo Alto Networks App ## -# ## -# Description: ## -# Field extractions and sample reports, ## -# and dashboards for the Palo Alto ## -# Networks Firewall ## -# ## -# ## -# ## -# Splunk Version:5.x ## -# App Version: 3.2.1 ## -# Last Modified: May 2013 ## -# Authors: Monzy Merza - Splunk, Inc. ## -# ## -# Many Thanks to Contributors, Advisors, testers## -# Joel 'JayKul' Bennett, David Dorsey ## -# David Hazekamp, Mike Munn, Adam Sealey ## -# David Markquardt, Gerald Kannapathy ## -# Will Hayes, Marc Benoit, Jeff Hillon ## -# Genti Zaimi ## -# ## -# For fastest response to support, setup, help ## -# or feedback, please post to ## -# answers.splunk.com and tag your questions ## -# with 'palo' or paloalto' ## -# ## -# Alternatively, contact: bd-sec@splunk.com ## -# ## -# ## -# ## -# ### ### ### ### ### ### ### ### ### ### ### ### ## - -###IMPORTANT### - -This app ONLY works on Splunk 5.x - -###Dependencies### - -The app requires the following Splunk Apps available from Splunk Base http://splunk-base.splunk.com/apps/ : - -- Splunk for use with AMMAP Flash maps -- Google Maps -- Geo Location Lookup Script - -You do not need to install these apps if you do not wish to use the Apps mapping and geo location features. The main dashboard will not render properly without the above apps. - -### Installing ### - -Ensure that the apps listed in the Dependencies section are installed. Create an index called, pan_logs if one doesn't already exist. Ensure that the index named pan_logs is in the default search path. You can verify this by going to : Manager -> Access Controls -> Users (or some other user group) -> Indexes searched by default, select the pan_logs index so it appears in the Selected indexes. - -To install this app: -- Unpack the tar ball into $SPLUNK_HOME/etc/apps -- Restart Splunk - -Note: -- After restart, it can take up to 5 minutes for new data to show up. -- For older data, you can use the backfill feature of splunk to backfill the summary index: - -http://www.splunk.com/base/Documentation/latest/Knowledge/Managesummaryindexgapsandoverlaps#Use_the_backfill_script_to_add_other_data_or_fill_summary_index_gaps - -### Configuring ### - -Setup Screen and Custom Commands: -The first time you run the app from the web ui, you will be presented with a setup screen. The credentials are only needed if you wish to use the panblock and panupdate custom commands. These passwords will be stored in Splunk. The same way as other splunk credentials are stored. If you do not wish to use the custom commands, you can leave this page blank or enter garbage values. - -To get the firewall data into Splunk: -IMPORTANT: When you configure the input port, you must set the sourcetype of the firewall data to pan_log and the index to pan_logs. - -From the web ui: - -Manager -> Data Inputs -> UDP -> New -> UDP port: Palo Alto defaults to UDP. Source type:Set Sourcetype From list: Select Sourcetype: pan_log -> More -> Index: pan_logs - -For details, http://www.splunk.com/base/Documentation/latest/admin/MonitorNetworkPorts - -Input configuration via inputs.conf: -edit $SPLUNK_HOME/etc/apps/SplunkforPaloAltoNetworks/local/inputs.conf , example below. your udp port may be different. PaloAlto defaults to udp port 514 - -[udp://514] -index= pan_logs -connection_host = ip -sourcetype = pan_log -no_appending_timestamp = true - -- Next, configure the firewall device to direct log traffic to the Splunk server on the network port that you specified. - -- Refer to the Palo Alto documentation for details on PAN log forwarding. The Palo Alto devices have a variety of different logs. This app works with the default log configuration. If you use any customized log types that are not defined in the Palo Alto syslog configuration documentation (PANOS-Syslog-Integration-TN-RevM), some of the apps features may not work. - -### Source types ### - -As Splunk indexes your Palo Alto Networks firewall data, the app will rename the sourcetypes to pan_threat, pan_traffic, pan_config, and pan_system depending on the logging facility. - -### High Performance Value Store (HPVS) ### - -The app uses the HPVS feature introduced in Splunk 5.0. This feature provides a tremendous performance improvement for dashboards and views. The views and dashboards make use of saved searches that store data on your search head. This means that disk storage on your search head will be consumed as a result of these searches. If you turn off these saved searches, your dashboards will not render. Or dashboard rendering will be really, really slow. Please post a question to answers.splunk.com if you'd like to explore alternatives. - -### Lookups ### - -Lookups are provided for the threat_id and app field to provide additional information about threats and applications on the network. - -###Using the form fields on the dashboards### - -All the dashboards work without any filtering values for the form fields. If you want to filter based on a field you should use asterisks before and after the search terms unless you are absolutely sure of the filter value. e.g. In the Content Filtering View, if you want to filter results by the virtual system called 'vsys1', a good practice would be to enter #vsys1# in the Virtual System field. - -Keep in mind that searches that have longer time ranges may take a little longer to return the results. - -###What's in this version#### - -- Major improvements on drilldowns in charts - -Bug Fixes: -savedsearches.conf: changed hard coded index=pan_logs to `pan_index` in scheduled searches. Thanks to Genti Zaimi for finding the issue and providing the fix -pan_overview_switcher_maps.xml: modified geoip search to include localop to force the search to run on the searchhead. Thanks to Genti Zaimi for identifying the problem and providing the fix \ No newline at end of file From 2614aebc23a0abfc675d4305a0691b3cbf89de21 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Fri, 28 Jun 2013 17:06:33 -0700 Subject: [PATCH 16/27] Update version to 3.3 --- README.md | 8 +++++--- default/app.conf | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 48f445d9..42aa8e73 100755 --- a/README.md +++ b/README.md @@ -11,9 +11,11 @@ Networks Firewall #### Version #### * Splunk Version: 5.x -* App Version: 3.2.1 -* Last Modified: May 2013 -* Authors: Monzy Merza - Splunk, Inc. +* App Version: 3.3 +* Last Modified: June 2013 +* Authors: + * Monzy Merza - Splunk, Inc. + * Brian Torres-Gil - Palo Alto Networks #### Credits #### diff --git a/default/app.conf b/default/app.conf index e3331f68..3ac2969f 100755 --- a/default/app.conf +++ b/default/app.conf @@ -3,9 +3,9 @@ is_visible = true label = Splunk for Palo Alto Networks [launcher] -author= monzy@splunk.com +author= btorres-gil@paloaltonetworks.com description= The Splunk for Palo Alto Networks app is a set of field extractions, reports, lookups and dashboards which provide visibility into the Palo Alto Networks Firewall data. -version = 3.2.1 +version = 3.3 [package] id= SplunkforPaloAltoNetworks From e16ec66599a9eaa295f3af2e75f9bc9dc13e7504 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Fri, 28 Jun 2013 19:02:00 -0700 Subject: [PATCH 17/27] Readme corrections and updates --- README.md | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 42aa8e73..74fd087c 100755 --- a/README.md +++ b/README.md @@ -29,12 +29,9 @@ Many Thanks to Contributors, Advisors, Testers: #### Support #### -For fastest response to support, setup, help -or feedback, please post to -answers.splunk.com and tag your questions -with 'palo' or paloalto' +For fastest response to support, setup, help or feedback, please post to +answers.splunk.com and tag your questions with `paloalto`. -Alternatively, contact: bd-sec@splunk.com ## IMPORTANT ## @@ -59,19 +56,16 @@ To install this app: - Unpack the tar ball into `$SPLUNK_HOME/etc/apps` - Restart Splunk -Note - -- After restart, it can take up to 5 minutes for new data to show up. -- For older data, you can use the backfill feature of splunk to backfill the summary index: - -[http://www.splunk.com/base/Documentation/latest/Knowledge/Managesummaryindexgapsandoverlaps#Use_the_backfill_script_to_add_other_data_or_fill_summary_index_gaps](http://www.splunk.com/base/Documentation/latest/Knowledge/Managesummaryindexgapsandoverlaps#Use_the_backfill_script) +Note: After restart, it can take up to 5 minutes for new data to show up. ## Configuring ## -Setup Screen and Custom Commands: +### Setup Screen and Custom Commands ### + The first time you run the app from the web ui, you will be presented with a setup screen. The credentials are only needed if you wish to use the panblock and panupdate custom commands. These passwords will be stored in Splunk. The same way as other splunk credentials are stored. If you do not wish to use the custom commands, you can leave this page blank or enter garbage values. -To get the firewall data into Splunk: +### To get the firewall data into Splunk ### + IMPORTANT: When you configure the input port, you must set the sourcetype of the firewall data to pan_log and the index to pan_logs. From the web ui: @@ -82,7 +76,7 @@ Manager -> Data Inputs -> UDP -> New -> UDP port: Source type: Set Sourcetype From list: Select Sourcetype: pan_log -> More -> Index: pan_logs -For details, [http://www.splunk.com/base/Documentation/latest/admin/MonitorNetworkPorts](http://www.splunk.com/base/Documentation/latest/admin/MonitorNetworkPorts +For details: [http://www.splunk.com/base/Documentation/latest/admin/MonitorNetworkPorts](http://www.splunk.com/base/Documentation/latest/admin/MonitorNetworkPorts ) ### Input configuration via inputs.conf ### @@ -115,7 +109,7 @@ Lookups are provided for the threat_id and app field to provide additional infor ### Using the form fields on the dashboards ### -All the dashboards work without any filtering values for the form fields. If you want to filter based on a field you should use asterisks before and after the search terms unless you are absolutely sure of the filter value. e.g. In the Content Filtering View, if you want to filter results by the virtual system called 'vsys1', a good practice would be to enter `#vsys1#` in the Virtual System field. +All the dashboards work without any filtering values for the form fields. If you want to filter based on a field you should use asterisks before and after the search terms unless you are absolutely sure of the filter value. e.g. In the Content Filtering View, if you want to filter results by the virtual system called 'vsys1', a good practice would be to enter "vsys1" in the Virtual System field. Keep in mind that searches that have longer time ranges may take a little longer to return the results. From bf07defa3ebfe8e07d2ea593487db2d2591a02c6 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Tue, 9 Jul 2013 12:41:09 -0700 Subject: [PATCH 18/27] WildFire Dashboard fixes: Remove 'action' field from Recent Malware Download table. Fix timeRange on Possible Malware Traffic table. --- default/data/ui/views/wildfire_overview.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/default/data/ui/views/wildfire_overview.xml b/default/data/ui/views/wildfire_overview.xml index 8340efa2..8a3e56c3 100755 --- a/default/data/ui/views/wildfire_overview.xml +++ b/default/data/ui/views/wildfire_overview.xml @@ -165,7 +165,7 @@ - |`tstats` count(dst_user) AS ca FROM pan_wildfire WHERE * category="malicious" $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby _time dst_ip action misc dst_user | table _time dst_ip dst_user misc action | + |`tstats` count(dst_user) AS ca FROM pan_wildfire WHERE * category="malicious" $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby _time dst_ip misc dst_user | table _time dst_ip dst_user misc | rename dst_user AS "Destination User" | rename dst_ip AS "Destination IP" | rename action AS Action | @@ -303,10 +303,11 @@ |`tstats` count(dst_ip) AS cdip FROM pan_wildfire_report_malware_traffic WHERE * NOT (protocol=udp AND dst_port=53) groupby dst_ip dst_port report_id protocol | table report_id dst_ip dst_port protocol | - join protocol dst_ip dst_port [ |`tstats` count(src_ip) FROM pan_traffic WHERE * NOT (protocol=udp AND dst_port=53) groupby src_ip dst_ip dst_port protocol src_user | table src_ip src_user dst_port dst_ip protocol ] | + join protocol dst_ip dst_port [ |`tstats` count(src_ip) FROM pan_traffic WHERE * NOT (protocol=udp AND dst_port=53) groupby _time src_ip dst_ip dst_port protocol src_user | table _time src_ip src_user dst_port dst_ip protocol | rename _time AS traffic_time ] | rename src_user AS "User" | rename src_ip AS "Compromised IP" | join type=left report_id [|`tstats` count(report_id) AS crid FROM pan_wildfire WHERE * groupby report_id misc | table report_id misc ] | - table "Compromised IP" "User" misc dst_ip dst_port protocol | + table traffic_time "Compromised IP" "User" misc dst_ip dst_port protocol | + rename traffic_time AS _time | rename misc AS "Malware File" | rename dst_ip AS "Traffic Dst_IP" | rename dst_port AS "Traffic Dst_Port" | From 6af19889e392d25d7c93cf5d6c3c0f093dac1c9d Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Thu, 18 Jul 2013 12:49:49 -0700 Subject: [PATCH 19/27] Cleaned up Compromised Host table and improved drill downs --- default/data/ui/views/wildfire_overview.xml | 28 ++++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/default/data/ui/views/wildfire_overview.xml b/default/data/ui/views/wildfire_overview.xml index 8a3e56c3..79fb3ee3 100755 --- a/default/data/ui/views/wildfire_overview.xml +++ b/default/data/ui/views/wildfire_overview.xml @@ -303,30 +303,40 @@ |`tstats` count(dst_ip) AS cdip FROM pan_wildfire_report_malware_traffic WHERE * NOT (protocol=udp AND dst_port=53) groupby dst_ip dst_port report_id protocol | table report_id dst_ip dst_port protocol | - join protocol dst_ip dst_port [ |`tstats` count(src_ip) FROM pan_traffic WHERE * NOT (protocol=udp AND dst_port=53) groupby _time src_ip dst_ip dst_port protocol src_user | table _time src_ip src_user dst_port dst_ip protocol | rename _time AS traffic_time ] | + join protocol dst_ip dst_port [ |`tstats` count(src_ip) FROM pan_traffic WHERE * NOT (protocol=udp AND dst_port=53) groupby _time src_ip dst_ip dst_port protocol app src_user | dedup 1 src_ip dst_ip dst_port protocol app src_user | table _time src_ip src_user dst_port dst_ip protocol app | rename _time AS traffic_time ] | rename src_user AS "User" | rename src_ip AS "Compromised IP" | - join type=left report_id [|`tstats` count(report_id) AS crid FROM pan_wildfire WHERE * groupby report_id misc | table report_id misc ] | - table traffic_time "Compromised IP" "User" misc dst_ip dst_port protocol | + eval "Traffic Link" = "View Traffic Logs" | eval "WildFire Link" = "View Wildfire Report" | + table traffic_time "Compromised IP" "User" dst_ip dst_port protocol app report_id "Traffic Link" "WildFire Link" | rename traffic_time AS _time | - rename misc AS "Malware File" | - rename dst_ip AS "Traffic Dst_IP" | - rename dst_port AS "Traffic Dst_Port" | - rename protocol AS "Protocol" + rename dst_ip AS "Dst_IP" | + rename dst_port AS "Dst_Port" | + rename protocol AS "Protocol" | + rename app AS "Application" | + rename report_id AS "WildFire Report ID" 10 results True all + true - ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` src_user="$row.User$" src_ip="$row.Compromised IP$" dst_ip="$row.Traffic Dst_IP$" dst_port="$row.Traffic Dst_Port$" protocol="$row.Protocol$" + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` src_ip="$row.Compromised IP$" dst_ip="$row.Dst_IP$" dst_port="$row.Dst_Port$" protocol="$row.Protocol$" + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` $row.Compromised IP$ + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` $row.User$ + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` $row.Dst_IP$ + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` $row.Dst_Port$ + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` $row.Protocol$ + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` $row.Application$ + ./flashtimeline?earliest=0&q=`pan_index` $row.WildFire Report ID$ + ./flashtimeline?earliest=0&q=`pan_index` (sourcetype="pan_wildfire_report" wildfire.report.id="$row.WildFire Report ID$") OR (sourcetype="pan_threat" log_subtype="wildfire" threat_id="$row.WildFire Report ID$($row.WildFire Report ID$)") - These hosts attempted a connection to servers known to be accessed by malware programs detected by the firewall. These hosts may be compromised as they show network behavior consistent with an analyzed malware sample. This data is based on a correlation of traffic logs and malware network behavior from WildFire Cloud analysis reports. (Note: This correlation requires a WildFire API Key]]>) + This table shows hosts that may be compromised as they show network behavior consistent with an analyzed malware sample. This data is based on a correlation of traffic logs and malware network behavior from WildFire Cloud analysis reports. (Note: This correlation requires a WildFire API Key]]>) From 9acba7df33f313dcf8e017c0643774da9e68f459 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Thu, 18 Jul 2013 15:21:29 -0700 Subject: [PATCH 20/27] Helpful text placed on dashboard and setup screen for some general UI cleanup. --- default/data/ui/views/wildfire_overview.xml | 9 ++++++--- default/setup.xml | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/default/data/ui/views/wildfire_overview.xml b/default/data/ui/views/wildfire_overview.xml index 79fb3ee3..7575a8d7 100755 --- a/default/data/ui/views/wildfire_overview.xml +++ b/default/data/ui/views/wildfire_overview.xml @@ -15,7 +15,7 @@ This dashboard provides visibility into WildFire activity in your Palo Alto Networks - environment. WildFire data is collected from the firewall logs and the WildFire Cloud. Data can be filtered by type using the form fields. + environment. WildFire data is collected from the firewall logs and the WildFire Cloud. Data can be filtered by type using the form fields. Note: This dashboard shows statistics for malicious WildFire events only. @@ -163,6 +163,10 @@ + + This chart shows all WildFire events. Other charts on this dashboard only show malicious WildFire events. + + |`tstats` count(dst_user) AS ca FROM pan_wildfire WHERE * category="malicious" $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby _time dst_ip misc dst_user | table _time dst_ip dst_user misc | @@ -274,7 +278,6 @@ 250px - ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` action="$click.value$" @@ -336,7 +339,7 @@ - This table shows hosts that may be compromised as they show network behavior consistent with an analyzed malware sample. This data is based on a correlation of traffic logs and malware network behavior from WildFire Cloud analysis reports. (Note: This correlation requires a WildFire API Key]]>) + This table shows hosts that may be compromised because they show network behavior consistent with an analyzed malware sample. This data is based on a correlation of traffic logs and malware network behavior from WildFire Cloud analysis reports. (Note: To download WildFire reports, this correlation requires a WildFire API Key]]>) diff --git a/default/setup.xml b/default/setup.xml index 49ed1394..2824dafa 100644 --- a/default/setup.xml +++ b/default/setup.xml @@ -12,7 +12,7 @@ - Used to retrieve reports from the WildFire Cloud. API Key is available from the WildFire Portal (https://wildfire.paloaltonetworks.com) under My Account]]>. + Used to retrieve reports from the WildFire Cloud. An API Key is available from the WildFire Portal (https://wildfire.paloaltonetworks.com/Wildfire/Home/MyAccount)]]>. From 75b108161216881494930f0861c6aa7ad1d9807b Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Thu, 18 Jul 2013 15:39:15 -0700 Subject: [PATCH 21/27] WildFire events over time shows category instead of action (more useful). Fixed drill down, too. Corrected stats in "Known Malware" chart. --- default/data/ui/views/wildfire_overview.xml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/default/data/ui/views/wildfire_overview.xml b/default/data/ui/views/wildfire_overview.xml index 7575a8d7..059e995f 100755 --- a/default/data/ui/views/wildfire_overview.xml +++ b/default/data/ui/views/wildfire_overview.xml @@ -148,9 +148,9 @@ - | tstats count(action) AS cact FROM pan_wildfire WHERE * $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby _time action span=5m | timechart values(cact) by action + | tstats count(category) AS ccat FROM pan_wildfire WHERE * $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby _time category span=5m | timechart values(ccat) by category - All WildFire Events by Action + All WildFire Events by Category column stacked bottom @@ -158,7 +158,7 @@ 100% - ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_wildfire`" + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_wildfire` category="$click.name2$" earliest=$click.value$ [| stats count | eval latest = $click.value$ %2b 300 | fields latest] @@ -266,8 +266,10 @@ |`tstats` count(action) AS cact FROM pan_wildfire WHERE * category="malicious" (action=wildfire-upload-success OR action=wildfire-upload-skip) $src_ip$ $dst_ip$ $dst_user$ $misc$ $vsys$ $app$ groupby action | - replace wildfire-upload-success with "New Sample" in action | - replace wildfire-upload-skip with "Known" in action + replace wildfire-upload-success with "New Malware" in action | + replace alert with "Known Malware" in action | + replace wildfire-upload-skip with "Known Malware" in action | + stats sum(cact) by action Previously Analyzed Malware From 84edaf6657c03aacde96f0e3fffcd6b72a77200c Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Thu, 18 Jul 2013 15:58:52 -0700 Subject: [PATCH 22/27] Readme Corrections --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 74fd087c..fdced2bd 100755 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ This app ONLY works on Splunk 5.x ## Dependencies ## -The app requires the following Splunk Apps available from Splunk Base [http://splunk-base.splunk.com/apps/] (http://splunk-base.splunk.com/apps/) : +The app requires the following Splunk Apps available from Splunk Base http://splunk-base.splunk.com/apps/ : - [Splunk for use with AMMAP Flash maps] (http://splunk-base.splunk.com/apps/22372/splunk-for-use-with-ammap-flash-maps) - [Google Maps] (http://splunk-base.splunk.com/apps/22365/google-maps) @@ -76,8 +76,7 @@ Manager -> Data Inputs -> UDP -> New -> UDP port: Source type: Set Sourcetype From list: Select Sourcetype: pan_log -> More -> Index: pan_logs -For details: [http://www.splunk.com/base/Documentation/latest/admin/MonitorNetworkPorts](http://www.splunk.com/base/Documentation/latest/admin/MonitorNetworkPorts -) +For details: http://www.splunk.com/base/Documentation/latest/admin/MonitorNetworkPorts ### Input configuration via inputs.conf ### @@ -121,5 +120,4 @@ Keep in mind that searches that have longer time ranges may take a little longer - Graphs of WildFire statistical data - Detect compromised hosts using malware behavior to traffic log correlation -Note: Malware analysis report retrieval requires a WildFire API Key from [https://wildfire.paloaltonetworks.com](https://wildfire.paloaltonetworks.com). - +Note: Malware analysis report retrieval requires a WildFire API Key from https://wildfire.paloaltonetworks.com From fc088abf08cb14f9f84608b482de7f29bb061954 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Thu, 18 Jul 2013 16:02:41 -0700 Subject: [PATCH 23/27] Fixed link in setup.xml --- default/setup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/default/setup.xml b/default/setup.xml index 2824dafa..ef283cdb 100644 --- a/default/setup.xml +++ b/default/setup.xml @@ -12,7 +12,7 @@ - Used to retrieve reports from the WildFire Cloud. An API Key is available from the WildFire Portal (https://wildfire.paloaltonetworks.com/Wildfire/Home/MyAccount)]]>. + Used to retrieve reports from the WildFire Cloud. An API Key is available from the WildFire Portal (https://wildfire.paloaltonetworks.com)]]>. From 0e0c1ced0accc24fa1f6af1e289566a4ffbf111b Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Thu, 18 Jul 2013 16:52:35 -0700 Subject: [PATCH 24/27] Added a couple aliases to the default macros for url and data --- default/macros.conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/default/macros.conf b/default/macros.conf index 34df35e9..32ed7d5a 100755 --- a/default/macros.conf +++ b/default/macros.conf @@ -24,9 +24,15 @@ definition = `pan_index` sourcetype="pan_config" [pan_web_activity] definition = `pan_index` "THREAT,url" (sourcetype="pan_threat" OR sourcetype="pan_threat-2050") +[pan_url] +definition = `pan_index` "THREAT,url" (sourcetype="pan_threat" OR sourcetype="pan_threat-2050") + [pan_data_filtering] definition = `pan_index` "THREAT,data" (sourcetype="pan_threat" OR sourcetype="pan_threat-2050") +[pan_data] +definition = `pan_index` "THREAT,data" (sourcetype="pan_threat" OR sourcetype="pan_threat-2050") + [pan_wildfire] definition = `pan_index` "THREAT,wildfire" (sourcetype="pan_threat" OR sourcetype="pan_threat-2050") From 3a86a745f04ea24e5250219af82ccb33d1ef9168 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Mon, 22 Jul 2013 15:10:30 -0700 Subject: [PATCH 25/27] Fixed filter problem on WildFire Dashboard. Renamed column in table. --- default/data/ui/views/wildfire_overview.xml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/default/data/ui/views/wildfire_overview.xml b/default/data/ui/views/wildfire_overview.xml index 059e995f..783ca859 100755 --- a/default/data/ui/views/wildfire_overview.xml +++ b/default/data/ui/views/wildfire_overview.xml @@ -74,6 +74,9 @@ + + + @@ -84,6 +87,11 @@ " false + + src_user=" + " + false + @@ -164,7 +172,7 @@ - This chart shows all WildFire events. Other charts on this dashboard only show malicious WildFire events. + This chart shows all WildFire events including benign events. Other charts on this dashboard only show malicious WildFire events. @@ -308,10 +316,10 @@ |`tstats` count(dst_ip) AS cdip FROM pan_wildfire_report_malware_traffic WHERE * NOT (protocol=udp AND dst_port=53) groupby dst_ip dst_port report_id protocol | table report_id dst_ip dst_port protocol | - join protocol dst_ip dst_port [ |`tstats` count(src_ip) FROM pan_traffic WHERE * NOT (protocol=udp AND dst_port=53) groupby _time src_ip dst_ip dst_port protocol app src_user | dedup 1 src_ip dst_ip dst_port protocol app src_user | table _time src_ip src_user dst_port dst_ip protocol app | rename _time AS traffic_time ] | - rename src_user AS "User" | rename src_ip AS "Compromised IP" | + join protocol dst_ip dst_port [ |`tstats` count(src_ip) FROM pan_traffic WHERE * (NOT (protocol=udp AND dst_port=53)) $src_ip$ $dst_ip$ $src_user$ $vsys$ $app$ groupby _time src_ip dst_ip dst_port protocol app src_user | dedup 1 src_ip dst_ip dst_port protocol app src_user | table _time src_ip src_user dst_port dst_ip protocol app | rename _time AS traffic_time ] | + rename src_user AS "User" | rename src_ip AS "Source IP" | eval "Traffic Link" = "View Traffic Logs" | eval "WildFire Link" = "View Wildfire Report" | - table traffic_time "Compromised IP" "User" dst_ip dst_port protocol app report_id "Traffic Link" "WildFire Link" | + table traffic_time "Source IP" "User" dst_ip dst_port protocol app report_id "Traffic Link" "WildFire Link" | rename traffic_time AS _time | rename dst_ip AS "Dst_IP" | rename dst_port AS "Dst_Port" | @@ -327,8 +335,8 @@ true - ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` src_ip="$row.Compromised IP$" dst_ip="$row.Dst_IP$" dst_port="$row.Dst_Port$" protocol="$row.Protocol$" - ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` $row.Compromised IP$ + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` src_ip="$row.Source IP$" dst_ip="$row.Dst_IP$" dst_port="$row.Dst_Port$" protocol="$row.Protocol$" + ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` $row.Source IP$ ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` $row.User$ ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` $row.Dst_IP$ ./flashtimeline?earliest=$earliest$&latest=$latest$&q=`pan_index` $row.Dst_Port$ From d19c6ee343dd0d3cd16cac95d0f86def9617f9b0 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Mon, 22 Jul 2013 15:10:42 -0700 Subject: [PATCH 26/27] Corrections and updates to ReadMe file --- README.md | 70 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index fdced2bd..a0509f46 100755 --- a/README.md +++ b/README.md @@ -30,8 +30,10 @@ Many Thanks to Contributors, Advisors, Testers: #### Support #### For fastest response to support, setup, help or feedback, please post to -answers.splunk.com and tag your questions with `paloalto`. +http://answers.splunk.com and tag your questions with `paloalto`. +For bugs or feature requests, you can also open an issue on github at +https://github.com/PaloAltoNetworks-BD/SplunkforPaloAltoNetworks/issues ## IMPORTANT ## @@ -39,46 +41,46 @@ This app ONLY works on Splunk 5.x ## Dependencies ## -The app requires the following Splunk Apps available from Splunk Base http://splunk-base.splunk.com/apps/ : +This app depends on the following Splunk Apps available from Splunk Base http://splunk-base.splunk.com/apps/ : - [Splunk for use with AMMAP Flash maps] (http://splunk-base.splunk.com/apps/22372/splunk-for-use-with-ammap-flash-maps) - [Google Maps] (http://splunk-base.splunk.com/apps/22365/google-maps) - [Geo Location Lookup Script] (http://splunk-base.splunk.com/apps/22282/geo-location-lookup-script-powered-by-maxmind) -You do not need to install these apps if you do not wish to use the Apps mapping and geo location features. The main dashboard will not render properly without the above apps. +You do not need to install these apps if you do not wish to use the mapping and geo location features. The main dashboard will not render properly without the above apps. ## Installing ## -Ensure that the apps listed in the Dependencies section are installed. - -To install this app: - +- Ensure that the apps listed in the Dependencies section are installed. - Unpack the tar ball into `$SPLUNK_HOME/etc/apps` - Restart Splunk -Note: After restart, it can take up to 5 minutes for new data to show up. +Note: After restart, it can take up to 5 minutes for new data to show up in the dashboards. ## Configuring ## ### Setup Screen and Custom Commands ### -The first time you run the app from the web ui, you will be presented with a setup screen. The credentials are only needed if you wish to use the panblock and panupdate custom commands. These passwords will be stored in Splunk. The same way as other splunk credentials are stored. If you do not wish to use the custom commands, you can leave this page blank or enter garbage values. +The first time you run the app from the web ui, you will be presented with a setup screen. The credentials are only needed if you wish to use the `panblock` and `panupdate` custom commands. The WildFire API is only needed if you are a WildFire subscriber and want Splunk to index WildFire analysis reports from the cloud when a malware sample is analyzed. These credentials will be stored in Splunk using encryption the same way other Splunk credentials are stored. -### To get the firewall data into Splunk ### +If you do not wish to use these extra features, you can enter garbage values. -IMPORTANT: When you configure the input port, you must set the sourcetype of the firewall data to pan_log and the index to pan_logs. +### To get the firewall data into Splunk ### -From the web ui: +IMPORTANT: When you configure the input port, you must set the sourcetype of the firewall data to pan_log and the index to pan_logs. This can be done from the Web UI or the CLI. Then, configure the firewall to set traffic to Splunk. -Manager -> Data Inputs -> UDP -> New -> UDP port: +#### From the Splunk Web UI #### - Palo Alto Networks firewalls default to UDP. - Source type: Set Sourcetype From list: - Select Sourcetype: pan_log -> More -> Index: pan_logs +- Navigate to Manager -> Data Inputs -> UDP -> New +- Set the UDP port (Palo Alto Networks firewalls default to port 514) +- Set sourcetype: From list +- Select source type From list: pan_log +- Click on More settings +- Index: pan_logs For details: http://www.splunk.com/base/Documentation/latest/admin/MonitorNetworkPorts -### Input configuration via inputs.conf ### +#### From the CLI via inputs.conf #### - Edit `$SPLUNK_HOME/etc/apps/SplunkforPaloAltoNetworks/local/inputs.conf` @@ -90,34 +92,58 @@ Example: (Palo Alto Networks firewalls default to udp port 514) sourcetype = pan_log no_appending_timestamp = true -- Next, configure the firewall device to direct log traffic to the Splunk server on the network port that you specified. +#### Configure the Firewall #### + +Next, on the Palo Alto Networks firewall or Panorama management center, create a Log Forwarding object to send desired syslogs to the Splunk Server. Refer to the Palo Alto Networks documentation for details on log forwarding. https://live.paloaltonetworks.com/community/documentation + +Note: Palo Alto Networks devices have a variety of different logs including traffic, threat, url filtering, malware, etc. This app works with the all the default log types. Customized log types may not work, if they are not defined in the Palo Alto Networks syslog configuration documentation (PANOS-Syslog-Integration-TN-RevM). -- Refer to the Palo Alto documentation for details on PAN log forwarding. The Palo Alto devices have a variety of different logs. This app works with the default log configuration. If you use any customized log types that are not defined in the Palo Alto syslog configuration documentation (PANOS-Syslog-Integration-TN-RevM), some of the apps features may not work. +## Hints and Tips ## ### Source types ### As Splunk indexes your Palo Alto Networks firewall data, the app will rename the sourcetypes to pan_threat, pan_traffic, pan_config, and pan_system depending on the logging facility. +Log can be further filtered by type during search by using predefined macros. The following macros are available in the search bar to filter on logs of a specific type. + +- pan_traffic +- pan_threat +- pan_url +- pan_data +- pan_wildfire +- pan_wildfire_report +- pan_config +- pan_system + +Use these macros in the search bar by surrounding them with back-ticks. + ### High Performance Value Store (HPVS) ### The app uses the HPVS feature introduced in Splunk 5.0. This feature provides a tremendous performance improvement for dashboards and views. The views and dashboards make use of saved searches that store data on your search head. This means that disk storage on your search head will be consumed as a result of these searches. If you turn off these saved searches, your dashboards will not render. Or dashboard rendering will be really, really slow. Please post a question to answers.splunk.com if you'd like to explore alternatives. ### Lookups ### -Lookups are provided for the threat_id and app field to provide additional information about threats and applications on the network. +Lookups are provided for the threat_id and app field to provide additional information about threats and applications on the network. ### Using the form fields on the dashboards ### -All the dashboards work without any filtering values for the form fields. If you want to filter based on a field you should use asterisks before and after the search terms unless you are absolutely sure of the filter value. e.g. In the Content Filtering View, if you want to filter results by the virtual system called 'vsys1', a good practice would be to enter "vsys1" in the Virtual System field. +All the dashboards work without any filtering values for the form fields. If you want to filter based on a field you should use asterisks before and after the search terms unless you are absolutely sure of the filter value. Keep in mind that searches that have longer time ranges may take a little longer to return the results. ## What's new in this version ## -- Malware analysis reports from the WildFire Cloud are dynamically downloaded and indexed when a WildFire log is recieved from a firewall. +- Malware analysis reports from the WildFire Cloud are dynamically downloaded and indexed when a WildFire log is received from a firewall. - WildFire dashboard - Recent WildFire events - Graphs of WildFire statistical data - Detect compromised hosts using malware behavior to traffic log correlation Note: Malware analysis report retrieval requires a WildFire API Key from https://wildfire.paloaltonetworks.com + +## Installing from Git ## + +This app is available on [Splunkbase](http://splunk-base.splunk.com/apps/22327/splunk-for-palo-alto-networks) and [Github](https://github.com/PaloAltoNetworks-BD/SplunkforPaloAltoNetworks). Optionally, you can clone the github repository to install the app. +From the directory `$SPLUNK_HOME/etc/apps/`, type the following command: + + git clone https://github.com/PaloAltoNetworks-BD/SplunkforPaloAltoNetworks.git From befe6829fa2281dfe00794a223572e64663bbf84 Mon Sep 17 00:00:00 2001 From: Brian Torres-Gil Date: Mon, 22 Jul 2013 15:18:42 -0700 Subject: [PATCH 27/27] Modify text in setup screen --- default/setup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/default/setup.xml b/default/setup.xml index ef283cdb..98e3559f 100644 --- a/default/setup.xml +++ b/default/setup.xml @@ -32,7 +32,7 @@ ]]> - Note: To change the Credentials or WildFire API Key later, first remove the entry from SPLUNK_HOME/etc/apps/SplunkforPaloAltoNetworks/local/app.conf. + Note: To change the Credentials or WildFire API Key later, first remove the entry from SPLUNK_HOME/etc/apps/SplunkforPaloAltoNetworks/local/app.conf and restart Splunk.