Retarus Secure Email Gateway

Integrate Retarus Secure Email Gateway to seamlessly fetch events from Secure Email Gateway by Retarus and enhance email security.

Email · Retarus Secure Email Gateway

Details

IDRetarus Secure Email Gateway
ProviderRetarus GmbH
CategoryEmail
From Version6.9.0
Docker Imagedemisto/netutils:1.0.0.10187688
Supported ModulesAgentix XSIAM

README

Integrate Retarus Secure Email Gateway to seamlessly fetch events and enhance email security.

Configure RetarusSecureEmailGateway Event Collector on Cortex XSIAM

  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for Retarus.
  3. Click Add instance to create and configure a new integration instance.

    Parameter Required Description
    Server URL True  
    Token ID True  
    Channel name False The channel to fetch events from. In Retarus, a channel name represents a specific configuration or processing pipeline used to manage email traffic based on criteria like sender, recipient, domain, or metadata, enabling tailored routing, filtering, compliance, and logging rules.
    Fetch interval in seconds True  
    Trust any certificate (not secure) False  

Troubleshooting

If you encounter any issues with this integration, follow these steps to troubleshoot:
Run the retarus-get-last-run-results command to obtain detailed information about the errors and problems you are facing. This command provides insights into the last execution of the integration and helps you understand the root cause of the issues.
If you receive an HTTP 400 or HTTP 401 status code when running the command, verify the token provided in the instance configuration.

When opening a support case, include the results you obtained from running the retarus-get-last-run-results command.

Note, due to the Retarus API limitation, only one instance can be configured for each token and channel. It is important to note that while two instances with the same token but different channels are allowed, configuring two instances with the same token and channel may result in errors and/or missing events.

Configuration parameters

  • url — Server URL (required)
  • credentials — (required)
  • channel — Channel name
  • fetch_interval — Fetch interval in seconds (required)
  • longRunning — Long Running Instance
  • insecure — Trust any certificate (not secure)

Commands (1)

  • retarus-get-last-run-results

    Retrieves the results of a connection attempt to Retarus, indicating whether it was successful or failed and why. If event fetching has been initiated, this command provides the results of the most recent fetch attempt.

import uuid
from contextlib import contextmanager

import demistomock as demisto
import pytest
import RetarusSecureEmailGateway
from CommonServerPython import *
from RetarusSecureEmailGateway import (
    Connection,
    EventConnection,
    datetime,
    fetch_events,
    json,
    long_running_execution_command,
    timedelta,
)

CURRENT_TIME: datetime | None = None

EVENTS = [
    {"ts": "2023-08-16T13:24:12.147573+0100", "id": "1"},
    {"ts": "2023-08-14T13:24:12.147573+0200", "id": "2"},
    {"ts": "2023-08-12T13:24:11.147573+0000", "id": "3"},
]


def is_interval_passed(fetch_start_time: datetime, fetch_interval: int) -> bool:
    global CURRENT_TIME
    if not CURRENT_TIME:
        CURRENT_TIME = fetch_start_time
    return fetch_start_time + timedelta(seconds=fetch_interval) < CURRENT_TIME


@pytest.fixture
def connection():
    # Set up a mock connection
    return MockConnection()


class MockConnection(Connection):
    def __init__(
        self,
    ):
        global CURRENT_TIME
        self.id = uuid.uuid4()
        self.events = EVENTS
        self.index = 0
        self.pongs = 0
        self.create_time = datetime.now()

    def recv(self, timeout):
        global CURRENT_TIME
        # pretend to sleep for 4 seconds
        assert CURRENT_TIME
        CURRENT_TIME += timedelta(seconds=4)

        if self.index >= len(self.events):
            raise TimeoutError
        event = self.events[self.index]
        self.index += 1
        return json.dumps(event)

    def pong(self):
        self.pongs += 1


def test_heartbeat(mocker, connection):
    """
    Given:
        - A connection object with scarce messages

    When:
        - The long running execution loop runs

    Then:
        - Periodic keep-alive messages (pongs) are sent to the websocket connection to prevent it from closing.

    """
    idle_timeout = 3

    @contextmanager
    def mock_websocket_connection(host, cluster_id, api_key, since_time=None, to_time=None, fetch_interval=60):
        yield EventConnection(connection, fetch_interval, idle_timeout)

    def mock_perform_long_running_loop(connection, interval):
        # This mock will raise exceptions to stop the long running loop
        # StopIteration exception marks success
        if connection.connection.pongs:
            raise StopIteration(f"Sent {connection.connection.pongs} pongs")
        if datetime.now() > connection.create_time + timedelta(seconds=idle_timeout + 2):
            # Heartbeat should've been sent already
            raise TimeoutError(f"No heartbeat sent within {idle_timeout} seconds")

    mocker.patch.object(RetarusSecureEmailGateway, "websocket_connection", side_effect=mock_websocket_connection)
    mocker.patch.object(RetarusSecureEmailGateway, "perform_long_running_loop", side_effect=mock_perform_long_running_loop)

    with pytest.raises(StopIteration):
        long_running_execution_command(url="url", token_id="token_id", fetch_interval=60, channel="channel", verify_ssl=False)

    assert connection.pongs > 0


def test_fetch_events(mocker, connection):
    """
    Given:
        A connection to the websocket

    When:
        Calling fetch_events function to get events from the websocket connection

    Then:
        - Ensure that the function returns the events from the websocket connection
        - Ensure that the function converts the timestamp to UTC
        - Ensure that the function returns the events collected in the interval.
    """

    # We set fetch_interval to 7 to get this first two events (as we "wait" 4 seconds between each event)
    fetch_interval = 7
    event_connection = EventConnection(connection=connection)
    mocker.patch.object(RetarusSecureEmailGateway, "is_interval_passed", side_effect=is_interval_passed)
    debug_logs = mocker.patch.object(demisto, "debug")
    events = fetch_events(connection=event_connection, fetch_interval=fetch_interval)

    assert len(events) == 2
    assert events[0]["_time"] == "2023-08-16T12:24:12.147573+00:00"
    assert events[1]["_time"] == "2023-08-14T11:24:12.147573+00:00"

    debug_logs.assert_any_call("Retarus-logs Fetched 2 events")


def test_get_last_run_results_command__with_results(mocker):
    cnx = {"last_run_results": "results"}
    mocker.patch.object(demisto, "getIntegrationContext", return_value=cnx)
    res = RetarusSecureEmailGateway.get_last_run_results_command()
    assert res.readable_output == "results"


def test_get_last_run_results_command__no_results(mocker):
    mocker.patch.object(demisto, "getIntegrationContext", return_value={})
    res = RetarusSecureEmailGateway.get_last_run_results_command()
    assert "No results from the last run yet." in res.readable_output