More improvements
This commit is contained in:
parent
5211142f5c
commit
89a643f218
113
ifxlookup
113
ifxlookup
@ -17,16 +17,12 @@ import importlib
|
||||
|
||||
yaml.Dumper.ignore_aliases = lambda *args : True
|
||||
|
||||
|
||||
class IFXLookup():
|
||||
|
||||
DEFAULT_CONFIG = '~/.config/ifxlookup.yml'
|
||||
|
||||
_errorinfo = {}
|
||||
_warninfo = {}
|
||||
_plugin_map = {}
|
||||
_delegate_map = {}
|
||||
_internal_messages = {}
|
||||
_debug_level = 0
|
||||
_config = {}
|
||||
_caching = False
|
||||
@ -38,7 +34,7 @@ class IFXLookup():
|
||||
pass
|
||||
|
||||
def __add_plugin(self,plugin,reload) :
|
||||
"""Adds a given ServiceBase plugin to the instance, reinitializing it if it already exists and such is specified."""
|
||||
"""Adds a given ServiceBase plugin and instance, reinitializing one if it already exists and such is specified."""
|
||||
plugin_name = plugin.__module__.split('.')[-1]
|
||||
if not reload and plugin_name in self._plugin_map.keys():
|
||||
pass
|
||||
@ -52,22 +48,23 @@ class IFXLookup():
|
||||
self._plugin_map.update({plugin_name:plugin})
|
||||
self._delegate_map.update({plugin_name:plugin()})
|
||||
|
||||
def __init_once(self,plugin_obj) :
|
||||
if plugin_obj not in self._initialized_plugins :
|
||||
plugin_obj.startup()
|
||||
self._initialized_plugins.append(plugin_obj)
|
||||
def __init_once(self,instance) :
|
||||
"""Runs the startup function on the instance once."""
|
||||
if instance not in self._initialized_plugins :
|
||||
instance.startup()
|
||||
self._initialized_plugins.append(instance)
|
||||
|
||||
def __shutdown_plugins(self) :
|
||||
"""Shutdown all plugins."""
|
||||
for plugin in self._delegate_map.values() :
|
||||
plugin.shutdown()
|
||||
for instance in self._delegate_map.values() :
|
||||
instance.shutdown()
|
||||
|
||||
def __namespace(self) :
|
||||
"""Generate map of ifxlookup runtime values that should be made available to plugins."""
|
||||
return {'debug' : self._debug_level,'caching': self._caching}
|
||||
|
||||
def __filter_and_return(self,subjects,dataset,filter) :
|
||||
"""Returns the submitted dataset"""
|
||||
"""Returns the submitted dataset, applying the given filter."""
|
||||
final_result = {}
|
||||
if 'error' in dataset or 'warn' in dataset :
|
||||
final_result['status'] = {}
|
||||
@ -80,7 +77,6 @@ class IFXLookup():
|
||||
for plugin in [x for x in dataset if x != 'error' and x != 'warn'] :
|
||||
subjectdataset.update({plugin:dataset[plugin][subject]})
|
||||
final_result.update({subject:subjectdataset})
|
||||
|
||||
# Apply Filter
|
||||
if filter :
|
||||
jsonpath_filter = jsonpath_ng.parse(filter)
|
||||
@ -93,25 +89,15 @@ class IFXLookup():
|
||||
if type(plugins) is not list :
|
||||
raise ValueError('argument \'plugins\' should be of type list')
|
||||
for plugin in plugins :
|
||||
# Check if the plugin is a string or a descendent of the ServiceBase class
|
||||
# Check if the plugin is a string or a descendent of a ServiceBase class
|
||||
if type(plugin) is not str and 'ServiceBase' not in [x.__name__ for x in inspect.getmro(plugin)] :
|
||||
raise ValueError('unkown type for plugin')
|
||||
# Find plugins by name using a default path
|
||||
if type(plugin) is str :
|
||||
pluginfolder = 'plugins'
|
||||
testpath = os.path.realpath(__file__)
|
||||
filedir = '/'.join(testpath.split('/')[:-1])
|
||||
pluginpath = filedir + '/' + pluginfolder
|
||||
pluginfiles = [x for x in os.listdir(pluginpath) if plugin in x and x.endswith('.py')]
|
||||
pluginfiles = sorted(pluginfiles)
|
||||
if len(pluginfiles) == 0 :
|
||||
available_plugins = [y for x,y in self.search_plugins().items() if x == plugin and 'ServiceBase' in [z.__name__ for z in inspect.getmro(y)]]
|
||||
if len(available_plugins) is 0 :
|
||||
raise FileNotFoundError(plugin + '.py not found')
|
||||
importlib.import_module(pluginfolder)
|
||||
for pluginfile in pluginfiles :
|
||||
plugin = getattr(importlib.import_module(pluginfolder+"."+pluginfile.split('.')[0]),'ServiceDelegate')
|
||||
self.__add_plugin(plugin,reload)
|
||||
continue
|
||||
# Load normal ServiceBase classes
|
||||
plugin = available_plugins[0]
|
||||
if 'ServiceBase' in [x.__name__ for x in inspect.getmro(plugin)] :
|
||||
self.__add_plugin(plugin,reload)
|
||||
continue
|
||||
@ -146,11 +132,11 @@ class IFXLookup():
|
||||
file_descriptor = open(os.path.normpath(os.path.expanduser(os.path.expandvars(config))))
|
||||
try :
|
||||
file_descriptor = open(os.path.normpath(os.path.expanduser(os.path.expandvars(config))))
|
||||
totalconfig = json.load(file_descriptor)
|
||||
total_config = json.load(file_descriptor)
|
||||
except json.decoder.JSONDecodeError as exception :
|
||||
file_descriptor = open(os.path.normpath(os.path.expanduser(os.path.expandvars(config))))
|
||||
totalconfig = yaml.safe_load(file_descriptor)
|
||||
self._config.update(totalconfig)
|
||||
total_config = yaml.safe_load(file_descriptor)
|
||||
self._config.update(total_config)
|
||||
|
||||
def dump_config(self) :
|
||||
"""Returns the current configuration state. This likely contains sensitive information such as API keys."""
|
||||
@ -166,9 +152,11 @@ class IFXLookup():
|
||||
|
||||
def set_debug_level(self,level) :
|
||||
"""Sets the debug level"""
|
||||
if not level :
|
||||
try:
|
||||
level = int(level)
|
||||
except Exception:
|
||||
level = 0
|
||||
self._debug_level = int(level)
|
||||
self._debug_level = level
|
||||
return self
|
||||
|
||||
def debug_level(self) :
|
||||
@ -243,19 +231,19 @@ if __name__ == '__main__':
|
||||
available_plugins = search.search_plugins()
|
||||
|
||||
# Define constants
|
||||
configdir = os.path.expanduser('~/.config')
|
||||
configfilename = 'ifxlookup.yml'
|
||||
servicetemplate = {'hosts':[],'username':'','key':''}
|
||||
configtemplate = {'bluecat':servicetemplate,'f5':servicetemplate,'paloalto':servicetemplate,'aruba':servicetemplate,'openvpn':servicetemplate}
|
||||
configerror = {'config': 'There was a problem reading your config file, aborting.'}
|
||||
errorinfo = {}
|
||||
config_dir = os.path.expanduser('~/.config')
|
||||
config_filename = 'ifxlookup.yml'
|
||||
service_template = {'hosts':[],'username':'','key':''}
|
||||
config_template = {'bluecat':service_template,'f5':service_template,'paloalto':service_template,'aruba':service_template,'openvpn':service_template}
|
||||
config_error = {'config': 'There was a problem reading your config file, aborting.'}
|
||||
error_info = {}
|
||||
|
||||
# Gather Argument options
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-c', '--config', action='store', help='Specify a config file (~/.config/ifxlookup.yml by default)')
|
||||
parser.add_argument('-f', '--filter', action='store', default=None, help='Apply a JSONPath filter to the results')
|
||||
parser.add_argument('-j', '--json', action='store_true', help='Return results as a json object')
|
||||
# parser.add_argument('-l', '--link', action='store_true', help="Return physical link information about the subject")
|
||||
# parser.add_argument('-l', '--link', action='store_true', help='Return physical link information about the subject')
|
||||
for delegate in available_plugins.values() :
|
||||
arguments = delegate().get_arguments()
|
||||
if arguments and len(arguments) == 4 :
|
||||
@ -267,7 +255,6 @@ if __name__ == '__main__':
|
||||
|
||||
# Load plugins based on submitted args
|
||||
selected_plugins = []
|
||||
|
||||
if vars(args)['all'] :
|
||||
for delegate in available_plugins.values() :
|
||||
selected_plugins.append(delegate)
|
||||
@ -289,46 +276,46 @@ if __name__ == '__main__':
|
||||
if args.config :
|
||||
# TODO: this section throws a yaml error if a json doc can't be found, errors should be more specific.
|
||||
try:
|
||||
# totalconfig = yaml.safe_load(open(os.path.normpath(args.config)))
|
||||
config = config=os.path.normpath(args.config)
|
||||
except Exception as exception :
|
||||
errorinfo.update(configerror)
|
||||
error_info.update(config_error)
|
||||
else :
|
||||
try:
|
||||
config = config=os.path.normpath(configdir + '/' + configfilename)
|
||||
config = config=os.path.normpath(config_dir + '/' + config_filename)
|
||||
except FileNotFoundError as exception :
|
||||
if not os.path.exists(os.path.normpath(configdir)):
|
||||
os.makedirs(os.path.normpath(configdir))
|
||||
if not os.path.exists(os.path.normpath(configdir + '/' + configfilename)):
|
||||
with open(configdir + '/' + configfilename, 'w') as output :
|
||||
yaml.dump(configtemplate, output)
|
||||
errorinfo.update({'config': 'Config file ' + configdir + '/' + configfilename + ' is empty, aborting.'})
|
||||
if not os.path.exists(os.path.normpath(config_dir)):
|
||||
os.makedirs(os.path.normpath(config_dir))
|
||||
if not os.path.exists(os.path.normpath(config_dir + '/' + config_filename)):
|
||||
with open(config_dir + '/' + config_filename, 'w') as output :
|
||||
yaml.dump(config_template, output)
|
||||
error_info.update({'config': 'Config file ' + config_dir + '/' + config_filename + ' is empty, populating with a template.'})
|
||||
else :
|
||||
errorinfo.update(configerror)
|
||||
error_info.update(config_error)
|
||||
except Exception as exception :
|
||||
errorinfo.update(configerror)
|
||||
error_info.update(config_error)
|
||||
if not config :
|
||||
with open(configdir + '/' + configfilename, 'w') as output :
|
||||
yaml.dump(configtemplate, output)
|
||||
errorinfo.update({'config': 'Config file ' + configdir + '/' + configfilename + ' is empty, populating with a template.'})
|
||||
with open(config_dir + '/' + config_filename, 'w') as output :
|
||||
yaml.dump(config_template, output)
|
||||
error_info.update({'config': 'Config file ' + config_dir + '/' + config_filename + ' is empty, populating with a template.'})
|
||||
# Stop on initialization error
|
||||
if not config :
|
||||
errorinfo.update(configerror)
|
||||
error_info.update(config_error)
|
||||
try :
|
||||
file_descriptor = open(os.path.normpath(os.path.expanduser(os.path.expandvars(config))))
|
||||
totalconfig = json.load(file_descriptor)
|
||||
total_config = json.load(file_descriptor)
|
||||
except json.decoder.JSONDecodeError as exception :
|
||||
file_descriptor = open(os.path.normpath(os.path.expanduser(os.path.expandvars(config))))
|
||||
totalconfig = yaml.safe_load(file_descriptor)
|
||||
total_config = yaml.safe_load(file_descriptor)
|
||||
for plugin_name in search.get_plugins().keys() :
|
||||
if plugin_name not in totalconfig.keys() :
|
||||
errorinfo.update({'config': 'Config file is missing information for plugin ' + plugin_name})
|
||||
if errorinfo != {} :
|
||||
print(subjects,{'error':errorinfo})
|
||||
if plugin_name not in total_config.keys() :
|
||||
error_info.update({'config': 'Config file is missing information for plugin ' + plugin_name})
|
||||
if error_info != {} :
|
||||
print(subjects,{'error':error_info})
|
||||
exit(1)
|
||||
search.configure(totalconfig)
|
||||
|
||||
search.configure(total_config)
|
||||
search.set_debug_level(args.debug)
|
||||
|
||||
# Run search
|
||||
report = search.lookup(args.subjects,filter=args.filter)
|
||||
if args.json :
|
||||
print(json.dumps(report))
|
||||
|
@ -18,7 +18,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
|
||||
def get_arguments(cls) :
|
||||
"""Returns an array of information used to construct an argumentparser argument."""
|
||||
return ['-w', '--wifi', 'store_true', "Return wireless connection information about the subject (aruba)"]
|
||||
return ['-w', '--wifi', 'store_true', 'Return wireless connection information about the subject (aruba)']
|
||||
|
||||
def startup(self) :
|
||||
for requirement in ['host','username','key'] :
|
||||
@ -35,11 +35,11 @@ class ServiceDelegate(ServiceBase) :
|
||||
apssortfield = 'mon_ssid'
|
||||
apsfilter = 'mon_ap_status equals 1'
|
||||
apsdevicetype = 'mon_bssid'
|
||||
self.debug("Retrieving list of Aruba's detected basestations...",1)
|
||||
self.debug('Retrieving list of Aruba\'s detected basestations...',1)
|
||||
self.aruba_basestations = self.perform_list_query(apsquery,self.aps_fields,apssortfield,apsfilter,apsdevicetype)
|
||||
self.debug("Retrieving list of Aruba connected clients...",1)
|
||||
self.debug('Retrieving list of Aruba connected clients...',1)
|
||||
self.aruba_hosts = self.perform_list_query(clientsquery,self.clients_fields,clientssortfield,clientsfilter,clientsdevicetype)
|
||||
self.debug("Searching Aruba information...",1)
|
||||
self.debug('Searching Aruba information...',1)
|
||||
|
||||
def lookup(self,subject) :
|
||||
# We return the first result that matches the subject out of our lists of Aruba information.
|
||||
@ -75,17 +75,17 @@ class ServiceDelegate(ServiceBase) :
|
||||
if subject.lower() in str(value) :
|
||||
return entry
|
||||
# Not found
|
||||
return {}
|
||||
return None
|
||||
|
||||
def start_session(self,host,username,key) :
|
||||
headers = { "Content-Type": "application/x-www-form-urlencoded" }
|
||||
data = "opcode=login&uid=" + username + "&passwd=" + key
|
||||
self.api_session.post("https://" + host + "/screens/wms/wms.login",verify=False,headers=headers,data=data)
|
||||
headers = { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
data = 'opcode=login&uid=' + username + '&passwd=' + key
|
||||
self.api_session.post('https://' + host + '/screens/wms/wms.login',verify=False,headers=headers,data=data)
|
||||
if 'SESSION' in self.api_session.cookies.get_dict() :
|
||||
self.session_key = self.api_session.cookies.get_dict()['SESSION']
|
||||
|
||||
def perform_list_query(self,queryname,columnlist,sortfield,queryfilter,devicetype) :
|
||||
"""Performs a list-type XML query against the Aruba UI API"""
|
||||
""""Performs a list-type XML query against the Aruba UI API"""
|
||||
# So uncivilized.
|
||||
# Build the basic object for our list query. Takes the parameters that the Aruba controller needs for it's
|
||||
# XML api, performs the query, and returns a list of results.
|
||||
@ -129,9 +129,9 @@ class ServiceDelegate(ServiceBase) :
|
||||
while not finished :
|
||||
start_row.text = str(nextstart)
|
||||
num_rows.text = str(self.query_page_size)
|
||||
datapayload = "".join(['query=',tostring(aruba_queries).decode(),'&UIDARUBA=',self.session_key])
|
||||
page = self.api_session.post("https://" + self.host + "/screens/cmnutil/execUiQuery.xml",verify=False,timeout=1,data=datapayload)
|
||||
self.debug("Got a page of results from Aruba host...",3)
|
||||
datapayload = ''.join(['query=',tostring(aruba_queries).decode(),'&UIDARUBA=',self.session_key])
|
||||
page = self.api_session.post('https://' + self.host + '/screens/cmnutil/execUiQuery.xml',verify=False,timeout=1,data=datapayload)
|
||||
self.debug('Got a page of results from Aruba host...',3)
|
||||
if page.status_code == 100 or page.status_code == 200 :
|
||||
pagecontents = ElementTree.fromstring(page.text)
|
||||
rows = pagecontents.iter('row')
|
||||
@ -154,5 +154,5 @@ class ServiceDelegate(ServiceBase) :
|
||||
nextstart = nextstart + self.query_page_size
|
||||
else :
|
||||
finished = True
|
||||
self.debug("Retrieved " + str((len(allitems))) + " results from query",2)
|
||||
self.debug('Retrieved ' + str((len(allitems))) + ' results from query',2)
|
||||
return allitems
|
||||
|
@ -10,7 +10,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
host = None
|
||||
|
||||
def get_arguments(cls) :
|
||||
return ['-b', '--bluecat', 'store_true', "Return network information about the subject (bluecat)"]
|
||||
return ['-b', '--bluecat', 'store_true', 'Return network information about the subject (bluecat)']
|
||||
|
||||
def startup(self) :
|
||||
for requirement in ['host','username','key'] :
|
||||
@ -19,43 +19,48 @@ class ServiceDelegate(ServiceBase) :
|
||||
return
|
||||
self.host = self.config['host']
|
||||
self.current_auth_header = self.get_bc_auth_header(self.config['host'],self.config['username'],self.config['key'])
|
||||
self.debug("Searching BlueCat for hosts...",1)
|
||||
self.debug('Searching BlueCat for hosts...',1)
|
||||
|
||||
def lookup(self,subject) :
|
||||
self.return_payload = {}
|
||||
if self.current_auth_header :
|
||||
object = self.search_bc_object(subject)
|
||||
self.return_payload.update({'object':object})
|
||||
if object :
|
||||
self.return_payload.update({'object':object})
|
||||
parent = self.search_bc_parent_object(subject)
|
||||
self.return_payload.update({'parent':parent})
|
||||
return self.return_payload
|
||||
if parent :
|
||||
self.return_payload.update({'parent':parent})
|
||||
if len(self.return_payload) > 0 :
|
||||
return self.return_payload
|
||||
else :
|
||||
return None
|
||||
else :
|
||||
return self.return_payload
|
||||
return None
|
||||
|
||||
def shutdown(self) :
|
||||
if self.host and self.current_auth_header :
|
||||
logout = requests.get("https://" + self.host + "/Services/REST/v1/logout",headers=self.current_auth_header)
|
||||
if not logout.text.endswith("successfully logged out.\"") :
|
||||
logout = requests.get('https://' + self.host + '/Services/REST/v1/logout',headers=self.current_auth_header)
|
||||
if not logout.text.endswith('successfully logged out.\"') :
|
||||
self.error.append('Unable to log out of BlueCat API session')
|
||||
|
||||
|
||||
def get_bc_auth_header(self,host,username,password) :
|
||||
auth_token = requests.get("https://" + host + "/Services/REST/v1/login?username=" + username + "&password=" + password).text[17:71]
|
||||
auth_token = requests.get('https://' + host + '/Services/REST/v1/login?username=' + username + '&password=' + password).text[17:71]
|
||||
if auth_token.startswith('BAMAuthToken') :
|
||||
return { "Authorization" : auth_token }
|
||||
return { 'Authorization' : auth_token }
|
||||
else :
|
||||
return None
|
||||
|
||||
def search_bc_object(self,subject) :
|
||||
"""Searches BC for the subject."""
|
||||
# Search by IP
|
||||
response = requests.get("https://" + self.host + "/Services/REST/v1/searchByObjectTypes?keyword=" + subject + "&types=IP4Address,IP6Address&start=0&count=10",headers=self.current_auth_header)
|
||||
response = requests.get('https://' + self.host + '/Services/REST/v1/searchByObjectTypes?keyword=' + subject + '&types=IP4Address,IP6Address&start=0&count=10',headers=self.current_auth_header)
|
||||
return self.clean_response(response)
|
||||
|
||||
def search_bc_parent_object(self,subject) :
|
||||
"""Search for and return information about the objects parent, if any"""
|
||||
if 'object' in self.return_payload and type(self.return_payload['object']) is dict and 'id' in self.return_payload['object'] :
|
||||
response = requests.get("https://" + self.host + "/Services/REST/v1/getParent?entityId=" + str(self.return_payload['object']['id']), headers=self.current_auth_header)
|
||||
response = requests.get('https://' + self.host + '/Services/REST/v1/getParent?entityId=' + str(self.return_payload['object']['id']), headers=self.current_auth_header)
|
||||
return self.clean_response(response)
|
||||
# Make a guess that it belongs to a static net that hasn't been put in bluecat
|
||||
elif subject != '' :
|
||||
@ -63,7 +68,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
try :
|
||||
ip = ipaddress.IPv4Network(subject + net,strict=False)
|
||||
network = str(ip.network_address)
|
||||
response = requests.get("https://" + self.host + "/Services/REST/v1/searchByObjectTypes?keyword=" + network + "&types=IP4Network,IP4Block&start=0&count=10",headers=self.current_auth_header)
|
||||
response = requests.get('https://' + self.host + '/Services/REST/v1/searchByObjectTypes?keyword=' + network + '&types=IP4Network,IP4Block&start=0&count=10',headers=self.current_auth_header)
|
||||
if not response.text.startswith('[]') :
|
||||
return self.clean_response(response)
|
||||
except:
|
||||
@ -80,11 +85,11 @@ class ServiceDelegate(ServiceBase) :
|
||||
parsed = parsed[0]
|
||||
if type(parsed) is dict :
|
||||
if 'properties' in parsed :
|
||||
parsed["properties"] = parsed["properties"][:-1]
|
||||
bad_delim_props = [x for x in parsed["properties"].split("|") if x.count('=') > 1]
|
||||
parsed['properties'] = parsed['properties'][:-1]
|
||||
bad_delim_props = [x for x in parsed['properties'].split('|') if x.count('=') > 1]
|
||||
if len(bad_delim_props) > 0 :
|
||||
parsed.update({"unknown":bad_delim_props})
|
||||
attributes = dict(x.split("=") for x in parsed["properties"].split("|") if x.count('=') == 1)
|
||||
parsed.pop("properties", None)
|
||||
parsed.update({'unknown':bad_delim_props})
|
||||
attributes = dict(x.split('=') for x in parsed['properties'].split('|') if x.count('=') == 1)
|
||||
parsed.pop('properties', None)
|
||||
parsed.update(attributes)
|
||||
return parsed
|
||||
|
@ -8,7 +8,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
|
||||
def get_arguments(cls) :
|
||||
"""Returns an array of information used to construct an argumentparser argument."""
|
||||
return [ '-d', '--dns', 'store_true', "Return DNS resolution about the subject (dns)" ]
|
||||
return [ '-d', '--dns', 'store_true', 'Return DNS resolution about the subject (dns)' ]
|
||||
|
||||
def startup(self) :
|
||||
self.resolver = dns.resolver.Resolver()
|
||||
@ -29,11 +29,11 @@ class ServiceDelegate(ServiceBase) :
|
||||
subjecttuple = socket.gethostbyname_ex(subject)
|
||||
except socket.gaierror :
|
||||
# Not a valid host or IP
|
||||
self.error.append("Unable to resolve " + subject)
|
||||
self.error.append('Unable to resolve ' + subject)
|
||||
subjecttuple = [subject,[],['']]
|
||||
# Can't make IPv4 out of it
|
||||
except socket.gaierror :
|
||||
self.error.append("Unable to resolve " + subject)
|
||||
self.error.append('Unable to resolve ' + subject)
|
||||
subjecttuple = [subject,[],['']]
|
||||
# Get additional records
|
||||
return_dict = {}
|
||||
|
@ -13,7 +13,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
_partition = 'Common'
|
||||
|
||||
def get_arguments(cls) :
|
||||
return ['-cg', '--vip', 'store_true', "Return VIP information about the subject (f5)"]
|
||||
return ['-cg', '--vip', 'store_true', 'Return VIP information about the subject (f5)']
|
||||
|
||||
def startup(self) :
|
||||
for requirement in ['hosts','username','key'] :
|
||||
@ -21,7 +21,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
self.error.append('Missing required config option ' + requirement)
|
||||
return
|
||||
self.hosts = self.config['hosts']
|
||||
self.debug("Logging into F5's and searching for hosts, this make take some time.",1)
|
||||
self.debug('Logging into F5\'s and searching for hosts, this make take some time.',1)
|
||||
|
||||
def lookup(self,subject) :
|
||||
self.return_payload = {}
|
||||
@ -30,7 +30,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
if logincandidates == {} :
|
||||
# Idk what to do here, do we give up or do we log in to all of them?
|
||||
self.error.append('Unable to find an LB to log into to check host ' + str(subject))
|
||||
return self.return_payload
|
||||
return None
|
||||
|
||||
for host in logincandidates.keys() :
|
||||
auth_header = self.get_iq_auth_header(host,self.config['username'],self.config['key'])
|
||||
@ -42,7 +42,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
vips = self.get_vip(host,auth_header);
|
||||
# If it's a VIP, return it and the host and let's be done
|
||||
if vips :
|
||||
self.debug("Got vips from " + host,3)
|
||||
self.debug('Got vips from ' + host,3)
|
||||
vip_match = [ x for x in vips if x['destination'].split('/' + self._partition + '/')[1].startswith(subject)]
|
||||
all_vips = [ x['destination'] for x in vips ]
|
||||
if len(vip_match) > 0 :
|
||||
@ -54,15 +54,15 @@ class ServiceDelegate(ServiceBase) :
|
||||
self.return_payload.update(pool_info)
|
||||
break
|
||||
else :
|
||||
self.debug("Unable to find VIP in VIPs from host " + host,2)
|
||||
self.debug('Unable to find VIP in VIPs from host ' + host,2)
|
||||
else :
|
||||
# Otherwise, get all nodes.
|
||||
# If it's in the nodes, we have to start all over and find where.
|
||||
# Take all vips and get all pools
|
||||
# Check if address is in the pool. If it is, return the node, pool, vip, and host.
|
||||
# Naw, I'll just give up.
|
||||
self.debug("Unable to get VIPs from " + host,2)
|
||||
if self.return_payload == {} :
|
||||
self.debug('Unable to get VIPs from ' + host,2)
|
||||
if len(self.return_payload) is 0 :
|
||||
self.return_payload = None
|
||||
return self.return_payload
|
||||
|
||||
@ -71,50 +71,50 @@ class ServiceDelegate(ServiceBase) :
|
||||
|
||||
|
||||
def get_iq_auth_header(self,host,username,password) :
|
||||
assumed_login_provider = "tmos"
|
||||
assumed_login_provider = 'tmos'
|
||||
token = None
|
||||
try :
|
||||
payload = { "username" : username, "password" : password, "loginProviderName" : assumed_login_provider }
|
||||
payload = { 'username' : username, 'password' : password, 'loginProviderName' : assumed_login_provider }
|
||||
login = requests.post('https://' + host + '/mgmt/shared/authn/login', json=payload,verify=False,timeout=2)
|
||||
if 'token' in json.loads(login.text) :
|
||||
token = json.loads(login.text)['token']['token']
|
||||
except Exception as exception:
|
||||
self.debug("Exception getting auth header for host " + host + ": \n" + str(exception),2)
|
||||
self.debug('Exception getting auth header for host ' + host + ': \n' + str(exception),2)
|
||||
return None
|
||||
if token :
|
||||
return { "X-F5-Auth-Token" : token }
|
||||
return { 'X-F5-Auth-Token' : token }
|
||||
else :
|
||||
return None
|
||||
|
||||
def get_vip(self,host,header) :
|
||||
"""Given a host and an authorization header, gets all the vips for that host"""
|
||||
# Please don't give me empty auth headers, I don't want to check it everywhere
|
||||
self.debug("Trying VIP info for " + host,2)
|
||||
self.debug('Trying VIP info for ' + host,2)
|
||||
try :
|
||||
vips = requests.get("https://" + host + '/mgmt/tm/ltm/virtual',headers=header,verify=False,timeout=10)
|
||||
vips = requests.get('https://' + host + '/mgmt/tm/ltm/virtual',headers=header,verify=False,timeout=10)
|
||||
if vips.status_code == 200 :
|
||||
return json.loads(vips.text)['items']
|
||||
self.debug("Response not OK for " + vips.history[0].url,3)
|
||||
self.debug('Response not OK for ' + vips.history[0].url,3)
|
||||
return None
|
||||
except Exception as exception :
|
||||
self.debug("Exception getting VIPs for host " + host + ": \n" + str(exception),2)
|
||||
self.debug('Exception getting VIPs for host ' + host + ': \n' + str(exception),2)
|
||||
return None
|
||||
|
||||
def get_pool(self,host,header,pool) :
|
||||
"""Given a host, auth header, and pool, return the pool and it's members from the host"""
|
||||
try :
|
||||
pools = requests.get("https://" + host + "/mgmt/tm/ltm/pool/~" + self._partition + "~" + pool,headers=header,verify=False,timeout=5)
|
||||
pools = requests.get('https://' + host + '/mgmt/tm/ltm/pool/~' + self._partition + '~' + pool,headers=header,verify=False,timeout=5)
|
||||
if pools.status_code == 200 :
|
||||
poolobj = json.loads(pools.text)
|
||||
members = requests.get("https://" + host + "/mgmt/tm/ltm/pool/~" + self._partition + "~" + pool + "/members",headers=header,verify=False,timeout=10)
|
||||
members = requests.get('https://' + host + '/mgmt/tm/ltm/pool/~' + self._partition + '~' + pool + '/members',headers=header,verify=False,timeout=10)
|
||||
if members.status_code == 200 :
|
||||
membersobj = json.loads(members.text)['items']
|
||||
return {'pool':poolobj,'members':membersobj}
|
||||
return {'pool':poolobj}
|
||||
elf.debug("Response not OK for " + pool.history[0].url,3)
|
||||
elf.debug('Response not OK for ' + pool.history[0].url,3)
|
||||
return None
|
||||
except Exception as exception:
|
||||
self.debug("Exception getting pool for host " + host + ": \n" + str(exception),2)
|
||||
self.debug('Exception getting pool for host ' + host + ': \n' + str(exception),2)
|
||||
return None
|
||||
|
||||
def choose_host(self,subject) :
|
||||
@ -132,7 +132,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
hosts.update({host:self.hosts[host]})
|
||||
if hosts != {} :
|
||||
return hosts
|
||||
self.debug("Unable to determine DC from subject name: " + subject,3)
|
||||
self.debug('Unable to determine DC from subject name: ' + subject,3)
|
||||
# No hostname match, maybe we have info from bluecat?
|
||||
if 'bluecat' in self.hints and subject in self.hints['bluecat'] and self.hints['bluecat'][subject]['object'] :
|
||||
if 'locationCode' in self.hints['bluecat'][subject]['object'] :
|
||||
@ -142,6 +142,6 @@ class ServiceDelegate(ServiceBase) :
|
||||
for host in self.hosts.keys() :
|
||||
if potentialdc.lower() in host.lower() :
|
||||
hosts.update({host:self.hosts[host]})
|
||||
self.debug("Got additional host info from the BlueCat plugin!",3)
|
||||
self.debug('Got additional host info from the BlueCat plugin!',3)
|
||||
return hosts
|
||||
return hosts
|
||||
|
File diff suppressed because one or more lines are too long
@ -8,7 +8,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
|
||||
def get_arguments(cls) :
|
||||
"""Returns an array of information used to construct an argumentparser argument."""
|
||||
return ['-r', '--vpn','store_true',"Return VPN information about the subject (openvpn)"]
|
||||
return ['-r', '--vpn','store_true','Return VPN information about the subject (openvpn)']
|
||||
|
||||
def startup(self) :
|
||||
for requirement in ['hosts','username','key'] :
|
||||
@ -16,26 +16,26 @@ class ServiceDelegate(ServiceBase) :
|
||||
self.error.append('Missing required config option ' + requirement)
|
||||
return
|
||||
self.hosts = self.config['hosts']
|
||||
self.debug("Logging into OpenVPN servers...",1)
|
||||
connections = []
|
||||
self.debug('Logging into OpenVPN servers...',1)
|
||||
connections = {}
|
||||
for host in self.config['hosts'] :
|
||||
try :
|
||||
sshclient = paramiko.SSHClient()
|
||||
sshclient.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
sshclient.connect(host,22,username=self.config['username'],password=self.config['key'])
|
||||
connections.append(sshclient)
|
||||
connections.update({host:sshclient})
|
||||
except Exception as exception :
|
||||
self.error.append("Unable to ssh into " + host + ': ' + str(exception))
|
||||
self.error.append('Unable to ssh into ' + host + ': ' + str(exception))
|
||||
self.connections = connections
|
||||
|
||||
|
||||
def shutdown(self) :
|
||||
for connection in self.connections :
|
||||
for connection in self.connections.values() :
|
||||
connection.close()
|
||||
|
||||
def lookup(self,subject) :
|
||||
search_command = "sudo -S cat /etc/openvpn/openvpn-*p.log | grep 'primary virtual IP for' | grep '" + subject + "' | tail -n 1"
|
||||
for connection in self.connections :
|
||||
search_command = 'sudo -S cat /var/log/openvpn/openvpn.log | grep \'primary virtual IP for\' | grep \'' + subject + '\' | tail -n 1'
|
||||
for host,connection in self.connections.items() :
|
||||
try:
|
||||
stdin,stdout,stderr=connection.exec_command(search_command)
|
||||
stdin.write(self.config['key'] + '\n')
|
||||
@ -45,17 +45,18 @@ class ServiceDelegate(ServiceBase) :
|
||||
self.debug('Retrieved line from ssh session: \n' + result[0],2)
|
||||
result = result[0]
|
||||
else :
|
||||
result = ""
|
||||
items = {'nat_address': r'.*\ ([0-9a-fA-F\.\:]*)$', 'source_address': r'.*/([0-9a-fA-F\.\:]*)\ .*', 'user_name': r'.*us=[0-9]*\ ([a-zA-Z0-9\.]*)/.*', 'timestamp': r'(.*)\ us=.*'}
|
||||
result = ''
|
||||
items = {'nat_address': r'.*\ ([0-9a-fA-F\.\:]*)$', 'source_address': r'.*/([0-9a-fA-F\.]*)\:.*', 'user_name': r'.*primary\ virtual\ IP\ for\ ([a-zA-Z0-9\.]*)/.*', 'timestamp': r'^([a-zA-Z]{3}\ +[0-9]{1,2}\ [0-9:]*)\ .*'}
|
||||
return_dictionary = {}
|
||||
for item in items.keys() :
|
||||
matches = re.match(items[item],result)
|
||||
if matches and len(matches.groups()) and matches[1] :
|
||||
return_dictionary.update({item: matches[1]})
|
||||
if return_dictionary is not {} :
|
||||
if len(return_dictionary) > 0 :
|
||||
return_dictionary.update({'host':host})
|
||||
return return_dictionary
|
||||
else :
|
||||
return None
|
||||
except Exception as exception :
|
||||
self.error.append("Unable to get results from ssh: " + str(exception))
|
||||
self.error.append('Unable to get results from ssh: ' + str(exception))
|
||||
pass
|
||||
|
@ -4,8 +4,8 @@ class ServiceDelegate(ServiceBase) :
|
||||
def get_arguments(cls) :
|
||||
"""Returns an array of information used to construct an argumentparser argument."""
|
||||
# [ <short flag>,<unix flag>,<arg type>,<description> ]
|
||||
# Example return: [ '-n', '--net', 'store_true', "Return network information about the subject" ]
|
||||
return ['-fw', '--rules', 'store_true', "Return Firewall rules relating to the subject"]
|
||||
# Example return: [ '-n', '--net', 'store_true', 'Return network information about the subject' ]
|
||||
return ['-fw', '--rules', 'store_true', 'Return Firewall rules relating to the subject']
|
||||
|
||||
def get_pa_auth_header(host,username,password) :
|
||||
"""Given authentication information, return the authenticated header for use in REST requests to a Palo Alto device."""
|
||||
|
@ -11,7 +11,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
|
||||
def get_arguments(cls) :
|
||||
"""Returns an array of information used to construct an argumentparser argument."""
|
||||
return [ '-s', '--shodan', 'store_true', "Return Shodan information about the subject (dns)" ]
|
||||
return [ '-s', '--shodan', 'store_true', 'Return Shodan information about the subject (dns)' ]
|
||||
|
||||
def startup(self) :
|
||||
for requirement in ['keys'] :
|
||||
@ -20,7 +20,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
return
|
||||
keys = self.config['keys']
|
||||
self._api_sessions = [shodan.Shodan(x) for x in keys]
|
||||
self.debug("Searching Shodan for hosts...",1)
|
||||
self.debug('Searching Shodan for hosts...',1)
|
||||
|
||||
|
||||
def lookup(self,subject) :
|
||||
@ -31,7 +31,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
clean = self._clean_newlines(host)
|
||||
return host
|
||||
except shodan.exception.APIError as e:
|
||||
self.debug("Unable to search for " + subject + " on shodan",1)
|
||||
self.debug('Unable to search for ' + subject + ' on shodan: ' + str(e),1)
|
||||
pass
|
||||
|
||||
def _clean_newlines(self,structure) :
|
||||
|
@ -35,7 +35,7 @@ class ServiceBase :
|
||||
def get_arguments(cls) :
|
||||
"""Returns an array of information used to construct an argumentparser argument."""
|
||||
# [ <short flag>,<unix flag>,<arg type>,<description> ]
|
||||
# Example return: [ '-n', '--net', 'store_true', "Output network information about the subject" ]
|
||||
# Example return: [ '-n', '--net', 'store_true', 'Output network information about the subject' ]
|
||||
return None
|
||||
|
||||
def startup(self) :
|
||||
|
Loading…
Reference in New Issue
Block a user