Server IP : 92.204.138.22 / Your IP : 18.191.162.222 Web Server : Apache System : Linux ns1009439.ip-92-204-138.us 4.18.0-553.22.1.el8_10.x86_64 #1 SMP Tue Sep 24 05:16:59 EDT 2024 x86_64 User : internationaljou ( 1019) PHP Version : 7.4.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /usr/lib/panopta-agent/library/ |
Upload File : |
from datetime import datetime try: # Python 2.x import httplib except: import http.client as httplib import logging import random import re import socket import sys import time import traceback try: # Python 2.x import urlparse except: import urllib.parse as urlparse try: import json except ImportError: try: import simplejson as json # it's possible that we may not need json for the action that we're taking. # for example, for the rpm post install script, on a python version that # doesn't have json, we'll get this far in the code. but the post # install doesn't use json, so we're fine except ImportError: json = None try: # trying to import SSL to make sure we can use unverified SSLs if possible # otherwise we'll set this and later keep from using the wrong connection settings import ssl except: ssl = None class Client(object): ''' This is a client used for communicating with an aggregator, either *the* Aggregator or an Onsight instance. ''' def __init__(self, agg_url, version, server_key=None, customer_key=None): self.log = logging.getLogger(self.__class__.__name__) self.customer_key = customer_key self.server_key = server_key self.agg_url = agg_url self.version = version def call(self, action, data={}, method="POST"): agg_urls = [url.strip() for url in self.agg_url.split(",") if url.strip()] random.shuffle(agg_urls) error = None for agg_url in agg_urls: try: url_parts = urlparse.urlparse(agg_url) if url_parts[0] == "" or url_parts[1] == "": url_parts = urlparse.urlparse("http://" + agg_url) if url_parts[0] not in ('http', 'https') or \ url_parts[1] == "": raise Exception("Invalid aggregator URL") except: raise ValueError("Invalid aggregator URL format.") agg_url = url_parts[1] if ':' in agg_url: url, port = agg_url.split(":") port = int(port or 443) else: url = agg_url port = 443 if "/v" in agg_url: url = url[:url.index("/v")] self.log.info("aggregator call %r to %r:%d", action, url, port) data['agent_version'] = self.version data['metadata_version'] = 'v3' data['agent_time'] = time.mktime(datetime.now().timetuple()) self.log.debug('Data: %r' % data) uri = "/v%s/%s" % (self.version, action) if method == "POST": params = json.dumps(data) else: if sys.version[0] == '3': params = urllib.parse.urlencode(data) else: params = urllib.urlencode(data) headers = { "Content-type": "application/json", "Accept": "application/json", "Authorization": self.server_key or '' } if port in (443, 8443): # XXX Below is a workaround for later versions of Python changing how self-signed # certs are handled. # Special logic because HPUX is made by satan and has diverging ssl and socket library versions # that cause issues trying to get the syncs to work properly with the agent if 'hp-ux' in sys.platform: connection = httplib.HTTPSConnection(url, port, context=ssl._create_unverified_context()) else: self.log.debug(sys.version_info) if ssl and hasattr(ssl, '_create_unverified_context'): self.log.debug('ssl has _create_unverified_context attribute: %s', hasattr(ssl, '_create_unverified_context')) connection = httplib.HTTPSConnection(url, port, timeout=25, context=ssl._create_unverified_context()) elif sys.version_info >= (2, 6, 0): connection = httplib.HTTPSConnection(url, port, timeout=25) else: connection = httplib.HTTPSConnection(url, port) else: if sys.version_info >= (2, 6, 0) and 'hp-ux' not in sys.platform: connection = httplib.HTTPConnection(url, port, timeout=25) else: connection = httplib.HTTPConnection(url, port) try: self.log.debug("%s - %s - %s - %s" % (method, uri, params, headers)) connection.request(method, uri, params, headers) resp = connection.getresponse() if method == "POST" and int(resp.status) != 201: raise Exception(resp.reason) elif method != "POST" and int(resp.status) != 200: raise Exception(resp.reason) return json.loads(resp.read().decode('utf-8')) except Exception: err = sys.exc_info()[1] error = str(err) self.log.error('Unable to connect: %s' % error) continue raise Exception(error) def get_local_ip(self): self.log.info('Creating connection to %s', self.agg_url) client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) if ':' in self.agg_url.split(',')[0]: host, port = self.agg_url.split(',')[0].split(':') else: host = self.agg_url port = 0 client.connect((host, int(port))) return client.getsockname()[0] def sync( self, results, outage_events, metadata, countermeasures_metadata, facts, discovered_containers, deleted_containers, register, register_custom_metrics, incidents, config, ips, auto_topo_scans, force_send_schedules=False, command_results={} ): payload = dict( results=results, outage_events=outage_events, metadata=metadata, countermeasures_metadata=countermeasures_metadata, facts=facts, discovered_containers=discovered_containers, deleted_containers=deleted_containers, force_send_schedules=force_send_schedules, command_results=command_results, register=register, register_custom_metrics=register_custom_metrics, incidents=incidents, config=config, ips=ips, auto_topo_scans=auto_topo_scans, ) return self.call("sync", payload) def notify_of_uninstall(self, remove_instance=False): success = False self.log.info("beginning uninstall notification") try: self.call("uninstall", data={'remove_instance': remove_instance}) except: self.log.error("error connecting") else: self.log.info("notified successfully") success = True return success def handshake(self, ips, properties, attributes=None): self.log.info('Beginning handshake') hostname = socket.getfqdn() data = { 'customer_key': self.customer_key, 'hostname': hostname, 'ips': ips, } data.update(properties) data.update(self.parse_attributes(attributes, properties)) self.log.info('Handshaking with aggregator') self.log.debug('Handshake data: %r' % data) server_key = None found_server = False success = False error = None log_level = None try: data = self.call('handshake', data) self.log.debug(data) server_key = data['server_key'] found_server = data['found_server'] if 'commands' in data and 'log_level' in data['commands']: log_level = data['commands']['log_level'] except Exception: err = sys.exc_info()[1] self.log.error('Error connecting: %s' % err) error = err else: success = True self.log.info('Connected successfully! Received server key: %r', server_key) if not (data.get('server_key') or data.get('found_server')): error = 'Error performing handshake with the aggregator, please check connectivity' success = False if 'error' in data and data.get('error'): success = False if data['error'] == 'invalid_credentials': error = 'Incorrect username/password' elif data['error'] == 'invalid_customer_key': error = 'Unknown/invalid customer key' elif data['error'] == 'invalid_server_group': error = 'Unknown server group' elif data['error'] == 'inactive_customer': error = 'Customer inactive' elif data['error'] == 'no_user_found_for_customer': error = 'No active user found for the customer' elif data['error'].startswith('invalid_template_id'): error = 'Unknown template id %s' % data['error'].replace('invalid_template_id:', '') else: error = data['error'] self.log.info('Success: %s, server key: %s, found server: %s, error: %s', success, server_key, found_server, error) return success, server_key, found_server, error, log_level def maintenance(self, duration, metric_tags=None): """ Request to the aggregator that it starts a maintenance for our server. It requires the duration of the maintenance in minutes, and accepts an optional list of tags to mark metrics that are going to be affected by it. """ payload = { 'duration': duration, 'metric_tags': metric_tags and metric_tags or [] } return self.call('maintenance', payload) def end_maintenance(self): """ Request to the aggregator that it ends all active maintenances for our server. POST call with no parameters. """ return self.call('end_maintenance') def parse_attributes(self, attributes, properties): """ Receive a dictionary containing the attributes stored in the manifest file. Parse them accordingly and store them in an attribute dictionary. Attributes get parsed only if the customer key is present in the properties. """ if 'customer_key' in properties: return attributes and {'attributes': attributes} or {} else: return {}