diff --git a/docs/user/bots.rst b/docs/user/bots.rst index 3524c746c..4f891f61c 100644 --- a/docs/user/bots.rst +++ b/docs/user/bots.rst @@ -4309,20 +4309,20 @@ Also you will need a so called "exchange point". **Configuration Parameters** -* `exchange`: STOMP *destination* to push at, e.g. "/exchange/_push" (which is default) -* `heartbeat`: default: 60000 -* `message_hierarchical_output`: Boolean, default: false -* `message_jsondict_as_string`: Boolean, default: false -* `message_with_type`: Boolean, default: false * `server`: STOMP server's hostname or IP, e.g. "n6stream.cert.pl" or "127.0.0.1" (which is default) * `port`: STOMP server's port number (default: 61614) -* `single_key`: Boolean or string (field name), default: false +* `exchange`: STOMP *destination* to push at, e.g. "/exchange/_push" (which is default) +* `heartbeat`: default: 60000 * `ssl_ca_certificate`: path to CA file * `auth_by_ssl_client_certificate`: Boolean, default: true (note: false is needed for new *n6* auth) * `ssl_client_certificate`: path to client cert file, used only if `auth_by_ssl_client_certificate` is true * `ssl_client_certificate_key`: path to client cert key file, used only if `auth_by_ssl_client_certificate` is true * `username`: STOMP *login* (e.g., *n6* user login), used only if `auth_by_ssl_client_certificate` is false * `password`: STOMP *passcode* (e.g., *n6* user API key), used only if `auth_by_ssl_client_certificate` is false +* `message_hierarchical_output`: Boolean, default: false +* `message_jsondict_as_string`: Boolean, default: false +* `message_with_type`: Boolean, default: false +* `single_key`: Boolean or string (field name), default: false .. _intelmq.bots.outputs.tcp.output: diff --git a/intelmq/bots/collectors/stomp/collector.py b/intelmq/bots/collectors/stomp/collector.py index 86ebee40c..5654b178a 100644 --- a/intelmq/bots/collectors/stomp/collector.py +++ b/intelmq/bots/collectors/stomp/collector.py @@ -4,15 +4,18 @@ # -*- coding: utf-8 -*- -from intelmq.lib.bot import CollectorBot -from intelmq.lib.mixins import StompMixin - try: import stomp import stomp.exception except ImportError: stomp = None -else: + +from intelmq.lib.bot import CollectorBot +from intelmq.lib.mixins import StompMixin + + +if stomp is not None: + class StompListener(stomp.PrintingListener): """ the stomp listener gets called asynchronously for @@ -74,17 +77,28 @@ def connect_and_subscribe(conn, logger, destination, start=False, connect_kwargs class StompCollectorBot(CollectorBot, StompMixin): """Collect data from a STOMP Interface""" """ main class for the STOMP protocol collector """ - exchange: str = '' + + server: str = 'n6stream.cert.pl' port: int = 61614 - server: str = "n6stream.cert.pl" - auth_by_ssl_client_certificate: bool = True - username: str = 'guest' # ignored if `auth_by_ssl_client_certificate` is true - password: str = 'guest' # ignored if `auth_by_ssl_client_certificate` is true - ssl_ca_certificate: str = 'ca.pem' # TODO pathlib.Path - ssl_client_certificate: str = 'client.pem' # TODO pathlib.Path - ssl_client_certificate_key: str = 'client.key' # TODO pathlib.Path + exchange: str = '' heartbeat: int = 6000 + # Note: the `ssl_ca_certificate` configuration parameter must always + # be set to the server's CA certificate(s) file path. + ssl_ca_certificate: str = 'ca.pem' + # (^ TODO: could also be pathlib.Path) + + auth_by_ssl_client_certificate: bool = True + + # Used if `auth_by_ssl_client_certificate` is true (otherwise ignored): + ssl_client_certificate: str = 'client.pem' # (cert file path) + ssl_client_certificate_key: str = 'client.key' # (cert's key file path) + # (^ TODO: could also be pathlib.Path) + + # Used if `auth_by_ssl_client_certificate` is false (otherwise ignored): + username: str = 'guest' # (STOMP auth *login*) + password: str = 'guest' # (STOMP auth *passcode*) + _collector_empty_process: bool = True __conn = False # define here so shutdown method can check for it diff --git a/intelmq/bots/outputs/stomp/output.py b/intelmq/bots/outputs/stomp/output.py index a28de3f4e..1fff5825a 100644 --- a/intelmq/bots/outputs/stomp/output.py +++ b/intelmq/bots/outputs/stomp/output.py @@ -4,34 +4,46 @@ # -*- coding: utf-8 -*- -from intelmq.lib.bot import OutputBot -from intelmq.lib.mixins import StompMixin - try: import stomp except ImportError: stomp = None +from intelmq.lib.bot import OutputBot +from intelmq.lib.mixins import StompMixin + class StompOutputBot(OutputBot, StompMixin): """Send events to a STMOP server""" """ main class for the STOMP protocol output bot """ - exchange: str = "/exchange/_push" - heartbeat: int = 60000 + http_verify_cert = True keep_raw_field: bool = False message_hierarchical_output: bool = False message_jsondict_as_string: bool = False message_with_type: bool = False - port: int = 61614 - server: str = "127.0.0.1" # TODO: could be ip address single_key: bool = False + + server: str = '127.0.0.1' # TODO: could be ip address + port: int = 61614 + exchange: str = '/exchange/_push' + heartbeat: int = 60000 + + # Note: the `ssl_ca_certificate` configuration parameter must always + # be set to the server's CA certificate(s) file path. + ssl_ca_certificate: str = 'ca.pem' + # (^ TODO: could also be pathlib.Path) + auth_by_ssl_client_certificate: bool = True - username: str = 'guest' # ignored if `auth_by_ssl_client_certificate` is true - password: str = 'guest' # ignored if `auth_by_ssl_client_certificate` is true - ssl_ca_certificate: str = 'ca.pem' # TODO: could be pathlib.Path - ssl_client_certificate: str = 'client.pem' # TODO: pathlib.Path - ssl_client_certificate_key: str = 'client.key' # TODO: patlib.Path + + # Used if `auth_by_ssl_client_certificate` is true (otherwise ignored): + ssl_client_certificate: str = 'client.pem' # (cert file path) + ssl_client_certificate_key: str = 'client.key' # (cert's key file path) + # (^ TODO: could also be pathlib.Path) + + # Used if `auth_by_ssl_client_certificate` is false (otherwise ignored): + username: str = 'guest' # (STOMP auth *login*) + password: str = 'guest' # (STOMP auth *passcode*) _conn = None diff --git a/intelmq/etc/feeds.yaml b/intelmq/etc/feeds.yaml index 54c251d8b..73aeea2ca 100644 --- a/intelmq/etc/feeds.yaml +++ b/intelmq/etc/feeds.yaml @@ -1158,12 +1158,12 @@ providers: module: intelmq.bots.collectors.stomp.collector parameters: exchange: "{insert your STOMP *destination* to subscribe to, as given by CERT.pl, e.g. /exchange/my.example.org/*.*.*.*}" + server: n6stream.cert.pl + port: 61614 ssl_ca_certificate: "{insert path to CA file for CERT.pl's n6}" auth_by_ssl_client_certificate: false username: "{insert your *n6* login, e.g. someuser@my.example.org}" password: "{insert your *n6* API key}" - port: 61614 - server: n6stream.cert.pl name: __FEED__ provider: __PROVIDER__ parser: diff --git a/intelmq/lib/mixins/stomp.py b/intelmq/lib/mixins/stomp.py index 41cbd29cb..293107b59 100644 --- a/intelmq/lib/mixins/stomp.py +++ b/intelmq/lib/mixins/stomp.py @@ -31,14 +31,21 @@ class StompMixin: port: int heartbeat: int + # Note: the `ssl_ca_certificate` configuration parameter must always + # be set to the server's CA certificate(s) file path. + ssl_ca_certificate: str + # (^ TODO: could also be pathlib.Path) + auth_by_ssl_client_certificate: bool - username: str # to be ignored if `auth_by_ssl_client_certificate` is true - password: str # to be ignored if `auth_by_ssl_client_certificate` is true + # Used if `auth_by_ssl_client_certificate` is true (otherwise ignored): + ssl_client_certificate: str # (cert file path) + ssl_client_certificate_key: str # (cert's key file path) + # (^ TODO: could also be pathlib.Path) - ssl_ca_certificate: str # TODO: could be pathlib.Path - ssl_client_certificate: str # TODO: could be pathlib.Path - ssl_client_certificate_key: str # TODO: could be patlib.Path + # Used if `auth_by_ssl_client_certificate` is false (otherwise ignored): + username: str # (STOMP auth *login*) + password: str # (STOMP auth *passcode*) # # Helper methods intended to be used in subclasses @@ -74,6 +81,9 @@ def prepare_stomp_connection(self) -> Tuple['stomp.Connection', dict]: `` object. """ ssl_kwargs, connect_kwargs = self.__get_ssl_and_connect_kwargs() + # Note: here we coerce `port` to int just to be on the safe + # side, as some historical versions of `etc/feeds.yaml` used + # to set it to a string. host_and_ports = [(self.server, int(self.port))] stomp_connection = stomp.Connection(host_and_ports=host_and_ports, heartbeats=(self.heartbeat, @@ -154,9 +164,9 @@ def __raise_value_error(self, msg: str) -> NoReturn: raise ValueError(msg) def __get_ssl_and_connect_kwargs(self) -> Tuple[dict, dict]: - # Note: the `ca_certs` argument to `set_ssl()` must always be - # provided, otherwise the `stomp.py`'s machinery would *not* - # perform any certificate verification! + # Note: a *non-empty* and *non-None* `ca_certs` argument must + # always be passed to `set_ssl()`; otherwise the `stomp.py`'s + # machinery would *not* enable any certificate verification! ssl_kwargs = dict(ca_certs=self.ssl_ca_certificate) connect_kwargs = dict(wait=True) if self.auth_by_ssl_client_certificate: