class EventPublisher(DxlPublisherInterface): def __init__(self): self.client = None def connect(self, config_file="./dxlclient.config"): if self.isConnected(): raise DxlJythonException( 1100, "Already connected to the OpenDXL broker") try: logger.info("Reading configuration file from '%s'", config_file) config = DxlClientConfig.create_dxl_config_from_file(config_file) # Initialize DXL client using our configuration self.client = DxlClient(config) # Connect to DXL Broker self.client.connect() return except Exception as e: logger.info("Exception: " + e.message) raise DxlJythonException( 1000, "Unable to establish a connection with the DXL broker") def sendMessage(self, topic="/dsa/dxl/test/event2", message="Default message"): if not self.isConnected(): raise DxlJythonException(1200, "Not connected to a OpenDXL broker") try: event = Event(topic) # Encode string payload as UTF-8 event.payload = message.encode() # Send event on DXL logger.info("Sending '" + message + "' to '" + topic + "'") self.client.send_event(event) return "Event successfully posted to topic '%s'" % topic except Exception as e: logger.info("Exception: " + e.message) raise DxlJythonException( 1010, "Unable to communicate with a DXL broker") def disconnect(self): if not self.isConnected(): return self.client.disconnect() def isConnected(self): if self.client is None: return False return self.client.connected
class FunctionComponent(ResilientComponent): """Component that implements Resilient function 'mcafee_publish_to_dxl""" config_file = "dxlclient_config" def __init__(self, opts): """constructor provides access to the configuration options""" super(FunctionComponent, self).__init__(opts) try: self.config = opts.get("fn_mcafee_opendxl").get(self.config_file) if self.config is None: log.error( self.config_file + " is not set. You must set this path to run this function") raise ValueError( self.config_file + " is not set. You must set this path to run this function") # Create configuration from file for DxlClient config = DxlClientConfig.create_dxl_config_from_file(self.config) # Create client self.client = DxlClient(config) self.client.connect() except AttributeError: log.error( "There is no [fn_mcafee_opendxl] section in the config file," "please set that by running resilient-circuits config -u") raise AttributeError( "[fn_mcafee_opendxl] section is not set in the config file") @handler("reload") def _reload(self, event, opts): """Configuration options have changed, save new values""" self.config = opts.get("fn_mcafee_opendxl").get(self.config_file) @function("mcafee_publish_to_dxl") def _mcafee_publish_to_dxl_function(self, event, *args, **kwargs): """Function: A function which takes 3 inputs: mcafee_topic_name: String of the topic name. ie: /mcafee/service/epo/remote/epo1. mcafee_dxl_payload: The text of the payload to publish to the topic. mcafee_return_request: Specify whether or not to wait for and return the response. The function will publish the provided payload to the provided topic. Indicate whether acknowledgment response should be returned.""" try: yield StatusMessage("Starting...") # Get the function parameters: mcafee_topic_name = kwargs.get("mcafee_topic_name") # text if not mcafee_topic_name: yield FunctionError("mcafee_topic_name is required") mcafee_dxl_payload = kwargs.get("mcafee_dxl_payload") # text if not mcafee_dxl_payload: yield FunctionError("mcafee_dxl_payload is required") mcafee_publish_method = self.get_select_param( kwargs.get("mcafee_publish_method") ) # select, values: "Event", "Service" if not mcafee_publish_method: yield FunctionError("mcafee_publish_method is required") mcafee_wait_for_response = self.get_select_param( kwargs.get( "mcafee_wait_for_response")) # select, values: "Yes", "No" log.info("mcafee_topic_name: %s", mcafee_topic_name) log.info("mcafee_dxl_payload: %s", mcafee_dxl_payload) log.info("mcafee_publish_method: %s", mcafee_publish_method) log.info("mcafee_wait_for_response: %s", mcafee_wait_for_response) response = None # Publish Event if mcafee_publish_method == "Event": event = Event(mcafee_topic_name) event.payload = mcafee_dxl_payload yield StatusMessage("Publishing Event...") self.client.send_event(event) # Invoke Service else: req = Request(mcafee_topic_name) req.payload = mcafee_dxl_payload yield StatusMessage("Invoking Service...") if mcafee_wait_for_response == "No": self.client.async_request(req) else: response = Response( self.client.sync_request(req, timeout=300)) yield StatusMessage("Done...") r = { "mcafee_topic_name": mcafee_topic_name, "mcafee_dxl_payload": mcafee_dxl_payload, "mcafee_publish_method": mcafee_publish_method, "mcafee_wait_for_response": mcafee_wait_for_response } # Return response from publishing to topic if response is not None: r["response"] = vars(response) yield FunctionResult(r) else: yield FunctionResult(r) except Exception as e: yield FunctionError(e)
class DXLBroker(object): class MyEventCallback(EventCallback): def __init__(self, broker): EventCallback.__init__(self) self.broker = broker def on_event(self, event): self.broker.logger.threaddebug( f"{self.broker.device.name}: Message {event.message_id} ({event.message_type}), received: {event.destination_topic}, payload: {event.payload}" ) indigo.activePlugin.processReceivedMessage(self.broker.device.id, event.destination_topic, event.payload) def __init__(self, device): self.logger = logging.getLogger("Plugin.DXLBroker") self.deviceID = device.id address = device.pluginProps.get(u'address', "") port = device.pluginProps.get(u'port', "") ca_bundle = indigo.server.getInstallFolderPath( ) + '/' + device.pluginProps.get(u'ca_bundle', "") cert_file = indigo.server.getInstallFolderPath( ) + '/' + device.pluginProps.get(u'cert_file', "") private_key = indigo.server.getInstallFolderPath( ) + '/' + device.pluginProps.get(u'private_key', "") self.logger.debug( f"{device.name}: Broker __init__ address = {address}, ca_bundle = {ca_bundle}, cert_file = {cert_file}, private_key = {private_key}" ) device.updateStateOnServer(key="status", value="Not Connected") device.updateStateImageOnServer(indigo.kStateImageSel.SensorOff) # Create the client configuration broker = Broker.parse(f"ssl://{address}:{port}") config = DxlClientConfig(broker_ca_bundle=ca_bundle, cert_file=cert_file, private_key=private_key, brokers=[broker]) # Create the DXL client self.dxl_client = DxlClient(config) # Connect to the fabric self.dxl_client.connect() device.updateStateOnServer(key="status", value="Connected") device.updateStateImageOnServer(indigo.kStateImageSel.SensorOn) subs = device.pluginProps.get(u'subscriptions', None) if subs: for topic in subs: self.dxl_client.add_event_callback(topic, self.MyEventCallback(self)) self.logger.info(u"{}: Subscribing to: {}".format( device.name, topic)) def disconnect(self): device = indigo.devices[self.deviceID] self.dxl_client.disconnect() self.dxl_client.destroy() device.updateStateOnServer(key="status", value="Not Connected") device.updateStateImageOnServer(indigo.kStateImageSel.SensorOff) def publish(self, topic, payload=None, qos=0, retain=False): event = Event(topic) event.payload = payload self.dxl_client.send_event(event) def subscribe(self, topic): device = indigo.devices[self.deviceID] self.logger.info(f"{device.name}: Subscribing to: {topic}") self.dxl_client.add_event_callback(topic, self.MyEventCallback(self)) def unsubscribe(self, topic): device = indigo.devices[self.deviceID] self.logger.info(f"{device.name}: Unsubscribing from: {topic}") self.dxl_client.unsubscribe(topic)
class EventSender: TRUST_LEVEL = { 'NOT_SET': '0', 'KNOWN_MALICIOUS': '1', 'MOST_LIKELY_MALICIOUS': '15', 'MIGHT_BE_MALICIOUS': '30', 'UNKNOWN': '50', 'MIGHT_BE_TRUSTED': '70', 'MOST_LIKELY_TRUSTED': '85', 'KNOWN_TRUSTED': '99', 'KNOWN_TRUSTED_INSTALLER': '100' } broker_ca_bundle = tempfile.NamedTemporaryFile().name cert_file = tempfile.NamedTemporaryFile().name private_key = tempfile.NamedTemporaryFile().name def __init__(self, params: Dict): with open(self.broker_ca_bundle, "w") as text_file: text_file.write(params['broker_ca_bundle']) with open(self.cert_file, "w") as text_file: text_file.write(params['cert_file']) with open(self.private_key, "w") as text_file: text_file.write(params['private_key']) if 'broker_urls' in params: self.broker_urls = params['broker_urls'].split(',') self.push_ip_topic = params.get('push_ip_topic') self.push_url_topic = params.get('push_url_topic') self.push_domain_topic = params.get('push_domain_topic') self.push_hash_topic = params.get('push_hash_topic') self.client = DxlClient(self.get_client_config()) self.client.connect() def __del__(self): self.client.disconnect() def push_ip(self, ip, trust_level, topic): if not is_ip_valid(ip): raise ValueError(f'argument ip {ip} is not a valid IP') trust_level_key = self.TRUST_LEVEL[trust_level] if topic: self.push_ip_topic = topic self.send_event(self.push_ip_topic, f'ip:{ip};trust_level:{trust_level_key}') return f'Successfully pushed ip {ip} with trust level {trust_level}' def push_url(self, url, trust_level, topic): trust_level_key = self.TRUST_LEVEL[trust_level] if topic: self.push_url_topic = topic self.send_event(self.push_url_topic, f'url:{url};trust_level:{trust_level_key}') return f'Successfully pushed url {url} with trust level {trust_level}' def push_domain(self, domain, trust_level, topic): trust_level_key = self.TRUST_LEVEL[trust_level] if topic: self.push_domain_topic = topic self.send_event(self.push_domain_topic, f'domain:{domain};trust_level:{trust_level_key}') return f'Successfully pushed domain {domain} with trust level {trust_level}' def push_hash(self, hash_obj, trust_level, topic): trust_level_key = self.TRUST_LEVEL[trust_level] if topic: self.push_ip_topic = topic self.send_event(self.push_hash_topic, f'hash:{hash_obj};trust_level:{trust_level_key}') return f'Successfully pushed hash {hash_obj} with trust level {trust_level}' def get_client_config(self): config = DxlClientConfig( broker_ca_bundle=self.broker_ca_bundle, cert_file=self.cert_file, private_key=self.private_key, brokers=[Broker.parse(url) for url in self.broker_urls]) config.connect_retries = CONNECT_RETRIES config.reconnect_delay = RECONNECT_DELAY config.reconnect_delay_max = RECONNECT_DELAY_MAX return config def send_event(self, topic, payload): if not topic: raise Exception( f'Error in {demisto.command()} topic field is required') event = Event(topic) event.payload = str(payload).encode() self.client.send_event(event) def send_event_wrapper(self, topic, payload): self.send_event(topic, payload) return 'Successfully sent event'