More improvements

This commit is contained in:
Daniel Dayley 2020-05-22 12:28:13 -06:00
parent 5211142f5c
commit 89a643f218
10 changed files with 133 additions and 138 deletions

113
ifxlookup
View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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 = {}

View File

@ -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

View File

@ -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

View File

@ -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."""

View File

@ -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) :

View File

@ -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) :