This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

SOARCA Documentation

SOARCA, an open-source SOAR (Security Orchestration, Automation and Response) tool developed by TNO, is designed be vendor-agnostic, allowing it to orchestrate various security actuators and systems. It is the first open-source SOAR that aims to be compliant with the CACAO v2.0 standard.

SOARCA enables cyber defenders to coordinate and automate their cyber operations, by using executable CACAO playbooks, and aims to achieve the following goals:

  • Standard Compliance: Adhering to the latest standards, including CACAO v2.0 and OpenC2, allows for interoperability with a wide range of technologies.
  • Extensibility with Open Interfaces: Enjoy the flexibility of an extensible tool featuring open and well-defined interfaces, promoting adaptability, customization, and experimentation.
  • Open-Source: Embrace an open-source model that not only offers cost-effective solutions but also supports unrestricted use and adaptation for research purposes.

Interested in the vision and concepts of SOARCA? Then check the SOARCA vision and concepts.

SOARCA capabilities

SOARCA currently supports the following transport mechanisms:

OpenC2 - Native

OpenC2

HTTP - Native

Http

SSH - Native

Ssh

PowerShell WinRM - Native

PowerShell

Features of SOARCA

Where do I start?

1 - Getting Started

Getting SOARCA quickly setup

Prerequisites

Before you begin, you might need to install the following tools (Linux Ubuntu 22.04 adapt for your needs):

  • golang
  • go gin go get -u github.com/gin-gonic/gin
  • swaggo go install github.com/swaggo/swag/cmd/swag@latest
  • cyclonedx-gomod go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest
  • make sudo apt install build-essential
  • docker & docker compose

Quick Run

Below, we outline various options to kickstart SOARCA. The latest pre-compiled releases can be found here.

make build && ./build/soarca
wget https://github.com/COSSAS/SOARCA/releases/download/SOARCA_1.0.0/SOARCA_1.0.0_linux_amd64.tar.gz  && tar -xvf SOARCA* && ./SOARCA
cd docker/soarca && sudo docker compose up -d

Compiled binary files can be found under /bin.

Playbook execution

You can use the following commands to execute the example playbooks via the terminal while SOARCA is running assuming on localhost. Alternatively you can go to http://localhost:8080/swagger/index.html and use the trigger/playbook endpoint.

Example playbooks:

# make sure an ssh server is running on adres 192.168.0.10
curl -X POST -H "Content-Type: application/json" -d @./example/ssh-playbook.json localhost:8080/trigger/playbook
curl -X POST -H "Content-Type: application/json" -d @./example/http-playbook.json localhost:8080/trigger/playbook
curl -X POST -H "Content-Type: application/json" -d @./example/openc2-playbook.json localhost:8080/trigger/playbook

Configuration

SOARCA reads its configuration from the environment variables or a .env file. An example of a .env is given below:

PORT: 8080
MONGODB_URI: "mongodb://localhost:27017"
DATABASE_NAME: "soarca"
DB_USERNAME: "root"
DB_PASSWORD: "rootpassword"
PLAYBOOK_API_LOG_LEVEL: trace
DATABASE: "false"
MAX_REPORTERS: "5"

LOG_GLOBAL_LEVEL: "info"
LOG_MODE: "development"
LOG_FILE_PATH: ""
LOG_FORMAT: "json"

ENABLE_FINS: false
MQTT_BROKER: "localhost"
MQTT_PORT: 1883
VALIDATION_SCHEMA_URL: ""

Docker hub

docker pull cossas/soarca

Building from Source

git clone https://github.com/COSSAS/SOARCA.git
make build
cp .env.example .env
./build/soarca

Configuring SOARCA

variablecontentdescription
PORTportSet the exposed port of SOARCA the default is 8080
DATABASEtrue | falseSet if you want to run with external database default is false
MONGODB_URIuriSet the Mongo DB uri default is mongodb://localhost:27017
DATABASE_NAMEnameSet the Mongo DB database name when using docker default is soarca
DB_USERNAMEuserSet the Mongo DB database user when using docker default is root
DB_PASSWORDpasswordSet the Mongo DB database users password when using docker default is rootpassword. IT IS RECOMMENDED TO CHANGE THIS IN PRODUCTION!
MAX_REPORTERSnumberSet the maximum number of downstream reporters default is 5
LOG_GLOBAL_LEVEL[Log levels]One of the specified log levels. Defaults to info
LOG_MODEdevelopment | productionIf production is chosen the LOG_GLOBAL_LEVEL is used for all modules defaults to production
LOG_FILE_PATHfilepathPath to the logfile you want to use for all logging. Defaults to "" (empty string)
LOG_FORMATtext | jsonThe logging can be in plain text format or in JSON format. Defaults to json
MQTT_BROKERdns name or ipThe broker address for SOARCA to connect to, for communication with fins default is localhost
MQTT_PORTportThe broker address for SOARCA to connect to, for communication with fins default is 1883
ENABLE_FINStrue | falseEnable fins in SOARCA defaults to false
VALIDATION_SCHEMA_URLurlSet a custom validation schema to be used to validate playbooks defaul is "" to use internal. NOTE: changing this heavily impacts performance.

2 - Vision & Concepts

The what and why of SOARCA

Context and Background

Security Orchestrator for Advanced Response to Cyber Attacks​ - SOARCA

Organisations are increasingly automating threat and incident response through playbook driven security workflow orchestration. The essence of this concept is that specific security events trigger a predefined series of response actions that are executed with no or only limited human intervention. These automated workflows are captured in machine-readable security playbooks, which are typically executed by a so called Security Orchestration, Automation and Response (SOAR) tool. The market for SOAR solutions has matured significantly over the past years and present day products support sophisticated automation workflows and a wide array of integrations with external security tools and data resources. Typically, however, the technology employed is proprietary and not easily adaptable for research and experimentation purposes. SOARCA aims to offer an open-source alternative for such solutions that is free of vendor dependencies and supports standardized formats and technologies where applicable.

SOARCA, TNO’s open-source SOAR, was developed for research and innovation purposes and allows SOC, CERT and CTI professionals to experiment with the concept of playbook driven security automation. It is open and extensible and its interfaces are well-defined and elaborately documented. It also offers native support for two emerging technology standards, both developed and maintained by OASIS Open:

  • CACAOv2. The Collaborative Automated Course of Action Operations (CACAO) standard provides a common framework and machine-processable schema for security playbooks that are natively interoperable and can be shared and executed across technological and organizational boundaries.
  • OpenC2. A standardized language for the command and control of cyber defense technologies. In essence it provides a vendor agnostic language and interface through which so called security actuators (e.g. firewalls or IAM solutions) can be reconfigured automatically.

SOARCA is available through TNO’s community platform COSSAS (Community for Open Source Security Automation Software) under the Apache 2.0 license. With its release, TNO aims to drive both the adoption and further development of novel technologies for cyber security automation forward. Here we note that open and accessible SOAR functionality is not only relevant for automation in threat and incident response but also for attack & defense simulations, cyber ranges, digital twinning and other emerging innovations that require orchestration of complex (security oriented) workflows.



Slide: © 2024 TNO

Current state of SOARCA

At present, SOARCA is in an Alpha release phase and is intended for Proof of Concepts (PoCs) and research purposes, serving as a platform for demonstrations. The objective of the SOARCA team is to evolve SOARCA into a more mature SOAR orchestration tool suitable for operational environments. For potential applications of SOARCA, please refer to the ‘Use-Cases’ section of our documentation.

Core Concepts

Several concepts within SOARCA might be important to know.

Course of Action

A course of action (CoA) refers to a planned sequence of steps or activities taken to achieve a specific cyber security goal. These steps are often collected into “playbooks”. Usually in the form of prose in PDFs, internal wikis, or even scattered throughout emails.

CACAO Playbooks: Streamlining Cybersecurity Operations

The CACAO Security Playbooks Version 2.0 specification provides a standard for writing executable playbooks. These playbooks are stored in a machine-readable form, allowing them to be (semi-)automatically executed by an orchestration tool.

A CACAO playbook is a structured document that outlines a series of orchestrated actions to address specific security events, incidents, or other security-related activities. These playbooks allow for the automation of security steps.

Examples of repetitive tasks that might be automated using a CACAO Playbook might be:

  • Investigate the cause of security events.
  • Mitigate threats effectively.
  • Remediate vulnerabilities.

By following CACAO playbook specifications, organizations can enhance their automated response capabilities, foster collaboration, and ensure consistency of playbooks across diverse technological solutions.

More information can be found in our primer on playbooks.

SOARCA Fin(s): Extending the core capabilities

SOARCA can be extended with custom extensions or rather so-called FIN (inspired by the majestic orca). A fin can be integrated within the SOARCA core. Technical descriptions of the components can be found here. Fins communicate with the SOARCA core using a pre-defined MQTT protocol.

Join the SOARCA Community

The SOARCA team invites cybersecurity professionals, researchers, and enthusiasts to join our community. Explore, adapt, and contribute to SOARCA’s growth. Let’s fortify cyber defenses together! See our contribution guidelines on how to make contributions. 🛡️🌐

Key Details

  • Project Name: SOARCA (Security Orchestrator for Advanced Response to Cyber Attacks)
  • License: Apache 2.0
  • Release Date: March 2024

2.1 - Executable playbooks

A playbook primer

SOARCA is build on top of the CACAO Security Playbook Version 2.0 standard.

A CACAO playbook is a structured document that outlines a series of orchestrated actions to address specific security events, incidents, or other security-related activities. These playbooks allow for the automation of security steps.

SOARCA is a security orchestrator that reads the steps defined in a CACAO playbook and performs the necessary actions to execute the commands they contain. This makes a CACAO document an executable playbook.

SOARCA’s development is ongoing, and at this time, it only partly supports the entire CACAO specification. On this page we’ll go over the general concepts in a CACAO playbooks and the parts of the standard that are supported by SOARCA.

A CACAO playbook

Here, we have an example of a relatively simple CACAO playbook that demonstrates SOARCA’s capabilities. The flow of steps is depicted in the following image:

Example playbook flow

The JSON of this CACAO playbook can be found at the bottom of this page.

As you can see, this playbook contains a mix of logical steps (if-condition and while-condition) and action steps that perform commands on a target system.

Agents and targets

In CACAO playbooks, entities that execute commands are called agents, and the entities against which the commands are executed are called targets (see Agents and Targets in the spec).

Every action step in a CACAO playbook must have a single agent, and one or more targets. Both agents and targets are defined on the playbook level, in the agent_definitions and target_definitions properties. SOARCA will execute action steps that have an agent of the type soarca. The capability that will be selected to execute the step is determined by the name property of the agent. For more information, read the documentation on components.

"target_definitions": {
    "linux--b49069c2-0b69-4a46-8509-80196c4a9bf8": {
      "type": "linux",
      "name": "Target system",
      "description": "System to execute commands on",
      "address": {
        "ipv4": [
          "__target_ip__:value"
        ]
      }
    }
}

Start and end steps

Every CACAO playbook should start with a start step. From there, each step can define which step should be executed after the current step finishes. Depending on whether the current step has executed successfully, the next step is defined by the on_completion property. A non-successful step execution may instead trigger the workflow_exception step, specified in the playbook properties. Alternatively to on_completion, a step can specify on_success and on_failure, which allow a finer-grain control over the execution flow.

What constitutes a successful step execution and what is a failure depends on the specific capability executing the step.

Example of a start step:

"start--d6c44626-c9b6-426b-ad5d-3311bafaf068": {
    "on_completion": "action--4b08af84-3741-48ca-8c92-df1557a87379",
    "type": "start"
}

According to the CACAO specification, every branch of steps should end in a (unique) end step. This is the only step type may not specify a next step:

"end--60fc8d0c-3677-4363-8576-9ea9014f8c8e": {
    "type": "end"
}

If-condition, while-condition, and parallel steps

An if-condition step allows executing different branches depending on a specified condition. The step must specify an on_true property, which references the start of a branch of steps that should be executed if the condition evaluates to true. Optionally, the if-condition step can define an on_false property that defines an alternative branch that is executed if the condition evaluates to false. In each case, the specified branch keeps executing until it encounters an end step.

The condition property contains a string that specifies a STIX Pattern. Currently, SOARCA only supports a very small subsection of the STIX Patterning specification. We support string based equality ('a' = 'a') and inequality ('a' != 'b') comparison. Example:

"if-condition--4b95eaa4-944a-4a9d-88d4-1374a70dbacd": {
    "name": "If it is not new years",
    "description": "Checks if it is 01-01-2025",
    "on_completion": "end--db937fc8-3a42-41cc-b828-ec2db212f425",
    "type": "if-condition",
    "condition": "__soarca_ssh_result__:value != '01-01-2025'",
    "on_true": "action--7fe08053-3685-4d8c-bc0a-40efce75113e"
}

SOARCA supports variable interpolation, which means that variables can be used inside the condition property, as seen in the example above.

Similarly, CACAO specifies while-condition steps, whose on_true branch will be repeatedly executed until the condition evaluates to false.

The parallel step allows executing multiple branches (in parallel) specified in the next_steps property. At this time, the steps in next_steps are executed sequentially. Parallel execution is scheduled for a later release in SOARCA.

Next, we explain variables in CACAO and SOARCA.

Variables

The CACAO specification allows defining variables on the playbook level, as well as on the step level. Playbook variables are available throughout the playbook. In SOARCA, variables defined on the step level are available in that step, and in any step that executes in a sub-branch of an if-condition, while-condition, or parallel step.

According to the CACAO spec, variable names should start and end with double underscores (__). The CACAO spec allows defining multiple types of variables (strings, ip-addresses, numbers), but at this time SOARCA will interpret every value as a string. The constant and external properties are ignored.

"playbook_variables": {
    "__target_ip__": {
      "type": "ipv4-addr",
      "description": "IP address of target system",
      "constant": false,
      "external": true
    }
}

SOARCA supports the interpolation of variables in different strings. The specific string-based properties that support interpolation depend on the capability. In general, string interpolation is supported in the properties of agents, targets, authentication information, and command properties.

Variable interpolation happens at the last possible moment, which means that step-dependant variables can be used in agent and target definitions.

Substitution is performed by replacing any occurrence of [variable_name]:value with the string value of that variable. Undefined variables are not replaced.

Action steps

Within CACAO playbooks, action steps can define commands that are executed by an agent against one or more targets. The agent and targets are referenced by ID. SOARCA selects the internal capability for handling the step by looking at the type and name of the agent. After selecting the proper capability, SOARCA will sequentially perform every command in the commands property for every target specified in targets. If any command fails to execute successfully, further execution is halted and the step is considered to have failed.

Action steps may return variables. On the successful execution of an action step, any variables returned are added to the globally available playbook variables. If the out_args property is specified and non-empty, only the variables listed in out_args will be added to the global playbook variables. The in_args property from the CACAO spec is ignored. Any variable defined on the playbook level, in parent-steps and within the step itself are available for interpolation.

In the case an action step ends in a failure, any variables returned from the step are ignored.

The example below shows how to run an ssh command on a single target system:

"action--4b08af84-3741-48ca-8c92-df1557a87379": {
    "name": "Get current time current system",
    "step_variables": {
        "__soarca_ssh_result__": {
            "type": "string",
            "description": "Output of the ssh command",
            "constant": false,
            "external": false
        }
    },
    "on_completion": "if-condition--4b95eaa4-944a-4a9d-88d4-1374a70dbacd",
    "type": "action",
    "commands": [
        {
            "type": "ssh",
            "description": "Retrieve date",
            "command": "date -I | tr -d \"\\n\""
        }
    ],
    "agent": "soarca--664bbe4a-7ad3-462c-baca-53cee8d67594",
    "targets": [
        "linux--b49069c2-0b69-4a46-8509-80196c4a9bf8"
    ],
    "out_args": [
        "__soarca_ssh_result__"
    ]
},

Example playbook

This is de JSON data of the playbook used throughout this page.

{
  "type": "playbook",
  "spec_version": "cacao-2.0",
  "id": "playbook--52f8cd0d-179a-48bf-aa90-32401fe6993c",
  "name": "Example Playbook",
  "description": "Playbook demonstrating SOARCA 1.0 capabilities",
  "playbook_types": [
    "mitigation"
  ],
  "playbook_activities": [
    "step-sequence"
  ],
  "created_by": "identity--dd22fb7f-af84-4957-84ed-12deb6c42d5d",
  "created": "2024-03-07T15:16:19.068Z",
  "modified": "2024-03-07T15:16:19.068Z",
  "revoked": false,
  "derived_from": [
    "playbook--77995581-d375-4905-bd8c-55f820a3e1a3"
  ],
  "playbook_variables": {
    "__target_ip__": {
      "type": "ipv4-addr",
      "description": "IP address of target system",
      "constant": false,
      "external": true
    },
    "__openc2_actuator_ip__": {
      "type": "ipv4-addr",
      "description": "IP address of OpenC2 actuator",
      "constant": false,
      "external": true
    }
  },
  "workflow_start": "start--d6c44626-c9b6-426b-ad5d-3311bafaf068",
  "workflow": {
    "start--d6c44626-c9b6-426b-ad5d-3311bafaf068": {
      "on_completion": "action--4b08af84-3741-48ca-8c92-df1557a87379",
      "type": "start"
    },
    "action--4b08af84-3741-48ca-8c92-df1557a87379": {
      "name": "Get current time current system",
      "step_variables": {
        "__soarca_ssh_result__": {
          "type": "string",
          "description": "Output of the ssh command",
          "constant": false,
          "external": false
        }
      },
      "on_completion": "if-condition--4b95eaa4-944a-4a9d-88d4-1374a70dbacd",
      "type": "action",
      "commands": [
        {
          "type": "ssh",
          "description": "Retrieve date",
          "command": "date -I | tr -d \"\\n\""
        }
      ],
      "agent": "soarca--664bbe4a-7ad3-462c-baca-53cee8d67594",
      "targets": [
        "linux--b49069c2-0b69-4a46-8509-80196c4a9bf8"
      ],
      "out_args": [
        "__soarca_ssh_result__"
      ]
    },
    "if-condition--4b95eaa4-944a-4a9d-88d4-1374a70dbacd": {
      "name": "If it is not new years",
      "description": "Checks if it is 01-01-2025",
      "on_completion": "end--db937fc8-3a42-41cc-b828-ec2db212f425",
      "type": "if-condition",
      "condition": "__soarca_ssh_result__:value != '01-01-2025'",
      "on_true": "action--7fe08053-3685-4d8c-bc0a-40efce75113e"
    },
    "action--7fe08053-3685-4d8c-bc0a-40efce75113e": {
      "name": "Perform an HTTP request",
      "description": "Perform a GET request against httpbin.org",
      "step_variables": {
        "__soarca_http_api_result__": {
          "type": "string",
          "constant": false,
          "external": false
        }
      },
      "on_completion": "while-condition--d865da4e-4f53-4b29-aaba-b8f5711d50ff",
      "type": "action",
      "commands": [
        {
          "type": "http-api",
          "description": "Perform request against httpbin.org",
          "command": "GET /get?newyears=false HTTP/1.1"
        }
      ],
      "targets": [
        "http-api--f2ed7db1-54fc-4a3c-ac0c-837dffade754"
      ],
      "out_args": [
        "__soarca_http_api_result__"
      ]
    },
    "while-condition--d865da4e-4f53-4b29-aaba-b8f5711d50ff": {
      "name": "While the counter is not 5",
      "description": "Step showing while condition",
      "step_variables": {
        "__soarca_ssh_result__": {
          "type": "string",
          "description": "Incrementing counter",
          "value": "0",
          "constant": false,
          "external": false
        }
      },
      "on_completion": "action--76fe4c02-6a5d-43ae-8736-433c07ab80b8",
      "type": "while-condition",
      "condition": "__soarca_ssh_result__:value != '5'",
      "on_true": "action--a32cdbb6-403a-47c7-a35b-07430a8de3fd"
    },
    "action--a32cdbb6-403a-47c7-a35b-07430a8de3fd": {
      "name": "Increment the counter",
      "description": "Increment the counter stored in __soarca_ssh_result__",
      "step_variables": {
        "__soarca_ssh_result__": {
          "type": "string",
          "constant": false,
          "external": false
        }
      },
      "on_completion": "end--60fc8d0c-3677-4363-8576-9ea9014f8c8e",
      "type": "action",
      "commands": [
        {
          "type": "ssh",
          "description": "Increment a string counter using python",
          "command": "python -c \"print(__soarca_ssh_result__:value + 1, end='')\""
        }
      ],
      "targets": [
        "linux--b49069c2-0b69-4a46-8509-80196c4a9bf8"
      ],
      "out_args": [
        "__soarca_ssh_result__"
      ]
    },
    "end--60fc8d0c-3677-4363-8576-9ea9014f8c8e": {
      "type": "end"
    },
    "end--db937fc8-3a42-41cc-b828-ec2db212f425": {
      "type": "end"
    },
    "action--76fe4c02-6a5d-43ae-8736-433c07ab80b8": {
      "name": "Send OpenC2 command",
      "description": "Sends a command to an OpenC2 compliant actuator",
      "step_variables": {
        "__soarca_openc2_http_result__": {
          "type": "string",
          "constant": false,
          "external": false
        }
      },
      "on_completion": "end--40f92845-e67a-4f13-b72a-23f189bf0cb6",
      "type": "action",
      "commands": [
        {
          "type": "openc2-http",
          "command": "POST /openc2-api/ HTTP/1.1",
          "content_b64": "ewogICJoZWFkZXJzIjogewogICAgInJlcXVlc3RfaWQiOiAiZDFhYzA0ODktZWQ1MS00MzQ1LTkxNzUtZjMwNzhmMzBhZmU1IiwKICAgICJjcmVhdGVkIjogMTU0NTI1NzcwMDAwMCwKICAgICJmcm9tIjogInNvYXJjYS5ydW5uZXIubmV0IiwKICAgICJ0byI6IFsKICAgICAgImZpcmV3YWxsLmFwaS5jb20iCiAgICBdCiAgfSwKICAiYm9keSI6IHsKICAgICJvcGVuYzIiOiB7CiAgICAgICJyZXF1ZXN0IjogewogICAgICAgICJhY3Rpb24iOiAiZGVueSIsCiAgICAgICAgInRhcmdldCI6IHsKICAgICAgICAgICJmaWxlIjogewogICAgICAgICAgICAiaGFzaGVzIjogewogICAgICAgICAgICAgICJzaGEyNTYiOiAiMjJmZTcyYTM0ZjAwNmVhNjdkMjZiYjcwMDRlMmI2OTQxYjVjMzk1M2Q0M2FlN2VjMjRkNDFiMWE5MjhhNjk3MyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgfQogIH0KfQ==",
          "headers": {
            "Content-Type": ["application/openc2+json;version=1.0"]
          }
        }
      ],
      "targets": [
        "linux--c50901f4-3802-4b8f-9b19-e1e62cc8fac4"
      ],
      "out_args": [
        "__soarca_openc2_http_result__"
      ]
    },
    "end--40f92845-e67a-4f13-b72a-23f189bf0cb6": {
      "type": "end"
    }
  },
  "agent_definitions": {
    "soarca--664bbe4a-7ad3-462c-baca-53cee8d67594": {
      "type": "soarca",
      "name": "soarca-ssh",
      "description": "SOARCA SSH capability"
    }
  },
  "target_definitions": {
    "linux--b49069c2-0b69-4a46-8509-80196c4a9bf8": {
      "type": "linux",
      "name": "Target system",
      "description": "System to execute commands on",
      "address": {
        "ipv4": [
          "__target_ip__:value"
        ]
      }
    },
    "http-api--f2ed7db1-54fc-4a3c-ac0c-837dffade754": {
      "type": "http-api",
      "name": "HTTPBin",
      "description": "The HTTPBin.org testing website",
      "address": {
        "url": [
          "https://httpbin.org/"
        ]
      }
    },
    "linux--c50901f4-3802-4b8f-9b19-e1e62cc8fac4": {
      "type": "http-api",
      "name": "OpenC2 Actuator",
      "description": "OpenC2 compatiable actuator",
      "address": {
        "ipv4": [
          "__openc2_actuator_ip__:value"
        ]
      }
    }
  }
}

3 - REST API

The SOARCA REST Api

This page contains the Swagger documentation of the SOARCA REST API.

4 - Extensions & Capabilities

Extending SOARCA is done by developing a SOARCA-Fin.

SOARCA features a set of native capabilities. The HTTP, OpenC2 HTTP, and SSH transport mechanisms are supported by the first release of SOARCA. SOARCA’s capabilities can be extended with custom implementations, which is further discussed on this page.

Extending the native capabilities

The native capabilities supported by SOARCA can be extended through a mechanism we named Fins. Your capability can be integrated with SOARCA by implementing the Fin protocol. This protocol regulates communication between SOARCA and the extension capabilities over an MQTT bus.

MQTT is a lightweight messaging protocol with libraries written in various programming languages. To integrate with SOARCA, you can write your own implementation of the Fin protocol, or use our python or golang libraries for easier integration.

Fin protocol

The underlying protocol for the SOARCA fins can be found here.

4.1 - Fin Python Library

Documentation of the Python Fin library

For the documentation about the Fin protocol we refer to documention page of SOARCA Fin Protocol.

Quick Start

To include the SOARCA Fin library, you can use the following command to install it via pip:

pip install soarca-fin-library

Example

An example on how to use the library is given below. For more examples and the source code, we will refer to the Github page of the SOARCA-Fin-python-library, where we provide /examples folder.

import os
from dotenv import load_dotenv

from soarca_fin_python_library.soarca_fin import SoarcaFin
from soarca_fin_python_library.models.agent_structure import AgentStructure
from soarca_fin_python_library.models.external_reference import ExternalReference
from soarca_fin_python_library.models.step_structure import StepStructure
from soarca_fin_python_library.models.capability_structure import CapabilityStructure
from soarca_fin_python_library.enums.workflow_step_enum import WorkFlowStepEnum
from soarca_fin_python_library.models.command import Command
from soarca_fin_python_library.models.result_structure import ResultStructure

from soarca_fin_python_library.models.variable import Variable
from soarca_fin_python_library.enums.variable_type_enum import VariableTypeEnum


def capability_pong_callback(command: Command) -> ResultStructure:
    print("Received ping, returning pong!")

    result = Variable(
        type=VariableTypeEnum.string,
        name="pong_output",
        description="If ping, return pong",
        value="pong",
        constant=True,
        external=False)

    context = command.command.context

    return ResultStructure(
        state="success", context=context, variables={"result": result})


def main(mqtt_broker: str, mqtt_port: int, username: str, password: str) -> None:

    finId = "soarca-fin--pingpong-f877bb3a-bb37-429e-8ece-2d4286cf326d"
    agentName = "soarca-fin-pong-f896bb3b-bb37-429e-8ece-2d4286cf326d"
    externalReferenceName = "external-reference-example-name"
    capabilityId = "mod-pong--e896aa3b-bb37-429e-8ece-2d4286cf326d"

    # Create AgentStructure
    agent = AgentStructure(
        name=agentName)

    # Create ExternalReference
    external_reference = ExternalReference(name=externalReferenceName)

    # Create StepStructure
    step_structure = StepStructure(
        name="step_name",
        description="step description",
        external_references=[external_reference],
        command="pong",
        target=agentName)

    # Create CapabilityStructure
    capability_structure = CapabilityStructure(
        capability_id=capabilityId,
        type=WorkFlowStepEnum.action,
        name="Ping Pong capability",
        version="0.0.1",
        step={
            "test": step_structure},
        agent={
            "testagent": agent})

    # Create Soarca fin
    fin = SoarcaFin(finId)
    # Set config for MQTT Server
    fin.set_config_MQTT_server(mqtt_broker, mqtt_port, username, password)
    # Register Capabilities
    fin.create_fin_capability(capability_structure, capability_pong_callback)
    # Start the fin
    fin.start_fin()


if __name__ == "__main__":
    load_dotenv()
    MQTT_BROKER = os.getenv("MQTT_BROKER", "localhost")
    MQTT_PORT = int(os.getenv("MQTT_PORT", "1883"))
    USERNAME = os.getenv("MQTT_USERNAME", "soarca")
    PASSWD = os.getenv("MQTT_PASSWD", "password")

    main(MQTT_BROKER, MQTT_PORT, USERNAME, PASSWD)

Below we have provided an example env file. Note that this changes according to your setup.

MQTT_BROKER = "localhost"
MQTT_PORT = "1883"
MQTT_USERNAME = "soarca"
MQTT_PASSWD = "password"

Env file can be exported by running:

export $(cat .env | grep -v "#" | xargs)

Architecture

The main object of the application is the SoarcaFin object, which is responsible for configuring and creating and controlling the capabilities. The SoarcaFin creates MQTTClients for each capability registered, plus one for registering, unregistering and controlling the fi itself. MQTTClients each have their own connection to the MQTT Broker and own Parser and Executor objects. The Parser object parsers the raw MQTT messages and tries to convert them to one of the objects in src/models. The Executor runs in their own thread and handles the actual execution of the messages. The Executor polls a thread-safe queue for new messages and performs IO operations, such as sending messages to the MQTT broker and calling capability callbacks.

Setup SOARCA Capabilities

To register a fin to SOARCA, first create a SoarcaFin object and pass the fin_id in the constructor. The SOARCA fin_id must be in the format of: sourca-fin-<capability>-<uuid4>. Call set_config_MQTT_server() to set the required configurations for the fin to connect to the MQTT broker. For each capability to be registered, call create_fin_capability(). The capability callback funtion should return an object of type ResultStructure. When all capabilities are initialized, call start_fin() for the SOARCA Fin to connect to the MQTT broker and register itself to SOARCA.

An example is given in this project in the file [examples/pong_example.py]

Class Overview

interface IParser {
 Message parse_on_message()
}

interface IMQTTClient {
 void on_connect()
 void on_message()
}

interface ISoarcaFin {
 void set_config_MQTTServer()
 void set_fin_capabilities()
 void start_fin()
}

interface IExecutor {
 void queue_message()
}


class SoarcaFin
class MQTTClient
class Parser
class Executor

ISoarcaFin <|.. SoarcaFin
IMQTTClient <|.. MQTTClient
IParser <|.. Parser
IExecutor <|.. Executor

IMQTTClient <- SoarcaFin
MQTTClient -> IExecutor
IParser <-MQTTClient

Sequence Diagrams

Command

Soarca -> "MQTTClient (Capability 1)" : Command Message [Capability ID Topic]

"MQTTClient (Capability 1)" -> Parser : parse_on_message(message)
"MQTTClient (Capability 1)" <-- Parser : Message.Command

"MQTTClient (Capability 1)" -> "Executor (Capability 1)" : Command message
Soarca <-- "Executor (Capability 1)" : Ack

"Executor (Capability 1)" -> "Capability Callback" : Command
"Executor (Capability 1)" <-- "Capability Callback" : Result


Soarca <- "Executor (Capability 1)" : Result
Soarca --> "MQTTClient (Capability 1)" : Ack

"MQTTClient (Capability 1)" -> Parser : parse_on_message(message)
"MQTTClient (Capability 1)" <-- Parser : Message.Ack

"MQTTClient (Capability 1)" -> "Executor (Capability 1)" : Ack message

Register

Soarca -> Soarca : Create Soarca Topic

Library -> SoarcaFin : Set MQTT Server config

Library -> SoarcaFin : Set Capability1
SoarcaFin -> "MQTTClient (Capability 1)" : Create capability

Library -> SoarcaFin : Set Capability2
SoarcaFin -> "MQTTClient (Capability 2)" : Create capability


Library -> SoarcaFin : Start Fin


SoarcaFin -> "MQTTClient (Capability 1)" : Start capability
"MQTTClient (Capability 1)" -> "MQTTClient (Capability 1)" : Register Capability Topic
SoarcaFin -> "MQTTClient (Capability 2)" : Start capability
"MQTTClient (Capability 2)" -> "MQTTClient (Capability 2)" : Register Capability Topic

SoarcaFin -> "MQTTClient (Fin)" : Register Fin
"MQTTClient (Fin)" -> "MQTTClient (Fin)" : Register SoarcaFin Topic

"MQTTClient (Fin)" -> "Executor (Fin)" : Send Register Message

Soarca <- "Executor (Fin)" : Message.Register [Soarca Topic]

Soarca --> "MQTTClient (Fin)" :  Message.Ack [Fin ID Topic]

"MQTTClient (Fin)" -> "Parser (Fin)" : parse_on_message(ack)
"MQTTClient (Fin)" <-- "Parser (Fin)" : Message.Ack

"MQTTClient (Fin)" -> "Executor (Fin)" : Message.Ack

Bugs or Contributing

Want to contribute to this project? It is possible to contribute here. Have you found a bug or want to request a feature? Please create an issue here.

4.2 - Fin protocol

Specification of the SOARCA Fin protocol

Goals

The goal of the protocol is to provide a simple and robust way to communicate between the SOARCA orchestrator and the capabilities (Fins) that can provide extra functions.

MQTT

To allow for dynamic communication MQTT is used to provide the backbone for the fin communication. SOARCA can be configured using the environment to use MQTT or just run stand-alone.

The Fin will use the protocol to register itself to SOARCA via the register message. Once register, it will communicate over the channel new channel designated by the fin UUID.

Commands to a specific capability will be communicated of the capability UUID channel.

Messages

Messages defined in the protocol

  • ack
  • nack
  • register
  • unregister
  • command
  • pause
  • resume
  • stop

legend

|field |content |type |description |field name have the (optional) key if the field is not required |content indication |type of the value could be string, int etc. |A description for the field to provide extra information and context

ack

The ack message is used to acknowledge messages.

fieldcontenttypedescription
typeackstringThe ack message type
message_idUUIDstringmessage id that the ack is referring to
@startjson
{
    "type": "ack",
    "message_id": "uuid"
}
@endjson

nack

The nack message is used to non acknowledgements, message was unimplemented or unsuccessful.

fieldcontenttypedescription
typenackstringThe ack message type
message_idUUIDstringmessage id that the nack is referring to
@startjson
{
    "type": "nack",
    "message_id": "uuid"
}
@endjson

register

The message is used to register a fin to SOARCA. It has the following payload.

fieldcontenttypedescription
typeregisterstringThe register message type
message_idUUIDstringMessage UUID
fin_idUUIDstringFin uuid separate form the capability id
NameNamestringFin name
protocol_versionversionstringVersion information of the protocol in semantic version schema e.g. 1.2.4-beta
securitysecurity informationSecuritySecurity information for protocol see security structure
capabilitieslist of capability structurelist of capability structureCapability structure information for protocol see security structure
metameta dictMetaMeta information for the fin protocol structure

capability structure

fieldcontenttypedescription
capability_idUUIDstringCapability id to identify the unique capability a fin can have multiple
typeactionworkflow-step-type-enumMost common is action
namenamestringcapability name
versionversionstringVersion information of the Fin implementation used in semantic version schema e.g. 1.2.4-beta
stepstep structurestep structureStep to specify an example for the step so it can be queried in the SOARCA API
agentagent structureagent structureAgent to specify the agent definition to match in playbooks for SOARCA

step structure

fieldcontenttypedescription
typeactionstringAction type
namenamestringmessage id
descriptiondescriptionstringDescription of the step
external_referenceslist of external referenceReferences to external recourses to further enhance the step also see CACAO V2 10.9.
commandcommandstringCommand to execute
targetUUIDstringTarget UUID cto execute command against

agent structure

fieldcontenttypedescription
typesoarca-finstringSOARCA Fin type, a custom type used to specify Fins
namenamestringSOARCA Fin name in the following form: soarca-fin-<name>-<uuid>, this grantees the fin is unique
@startjson
{
    "type": "register",
    "message_id": "uuid",
    "fin_id" : "uuid",
    "name": "Fin name",
    "protocol_version": "<semantic-version>",
    "security": {
        "version": "0.0.0",
        "channel_security": "plaintext"
    },
    "capabilities": [
        {
            "capability_id": "uuid",
            "name": "ssh executer",
            "version": "0.1.0", 
            "step": { 
                "type": "action",
                "name": "<step_name>",
                "description": "<description>",
                "external_references": { 
                    "name": "<reference name>",
                    "...": "..."
                    },
                "command": "<command string example>",
                "target": "<target uuid>"
            },
            "agent" : {
                "soarca-fin--<uuid>": {
                    "type": "soarca-fin",
                    "name": "soarca-fin--<name>-<capability_uuid>"
                }
            }

        }
    ],
    "meta": {

        "timestamp": "string: <utc-timestamp-nanoes + timezone-offset>",
        "sender_id": "uuid"
    }
}
@endjson

unregister

The message is used to unregister a fin to SOARCA. It has the following payload.

fieldcontenttypedescription
typeunregisterstringUnregister message type
message_idUUIDstringMessage UUID
capability_idUUIDstringCapability id or null (either capability_id != null, fin_id != null or all == true need to be set)
fin_idUUIDstringFin id or null (either capability_id != null, fin_id != null or all == true need to be set)
allboolboolTrue to address all fins to unregister otherwise false (either capability_id != null, fin_id != null or all == true need to be set)
@startjson
{
    "type": "unregister",
    "message_id": "uuid",
    "capability_id" : "capability uuid",
    "fin_id" : "fin uuid",
    "all" : "true | false"
}
@endjson

command

The message is used to send a command from SOARCA. It has the following payload.

fieldcontenttypedescription
typecommandstringCommand message type
message_idUUIDstringMessage UUID
commandcommandcommand substructurecommand structure
metameta dictMetaMeta information for the fin protocol structure

command substructure

fieldcontenttypedescription
commandcommandstringThe command to be executed
authentication (optional)authentication informationauthentication informationCACAO authentication information
contextcacao contextContextContext form the playbook
variablesdict of variablesdict of VariablesFrom the playbook
@startjson
{
    "type": "command",
    "message_id": "uuid",
    "command": {
        "command": "command",
        "authentication": {"auth-uuid": "<cacao authentication struct"},
        "context": {
            "generated_on": "string: <utc-timestamp-nanoes + timezone-offset>",
            "timeout": "string: <utc-timestamp-nanoes + timezone-offset>",
            "step_id": "uuid",
            "playbook_id": "uuid",
            "execution_id": "uuid"
        },
        "variables": {
            "__<var1>__": {
                "type": "<cacao.variable-type-ov>",
                "name": "__<var1>__",
                "description": "<string>",
                "value": "<string>",
                "constant": "<bool>",
                "external": "<bool>"
            },
            "__<var2>__": {
                "type": "<cacao.variable-type-ov>",
                "name": "__<var2>__",
                "description": "<string>",
                "value": "<string>",
                "constant": "<bool>",
                "external": "<bool>"
            }
        }
    },
    "meta": {
        "timestamp": "string: <utc-timestamp-nanoes + timezone-offset>",
        "sender_id": "uuid"
    }
}
@endjson

result

The message is used to send a response from the Fin to SOARCA. It has the following payload.

fieldcontenttypedescription
typeresultstringUnregister message type
message_idUUIDstringMessage UUID
resultresult structureresult structureThe result of the execution
metameta dictMetaMeta information for the fin protocol structure

result structure

fieldcontenttypedescription
statesucces or failurestringThe execution state of the playbook
contextcacao contextContextContext form the playbook
variablesdict of variablesdict of variablesDictionary of CACAO compatible variables
@startjson
{   
    "type": "result",
    "message_id": "uuid",
    "result": {
        "state": "enum(success | failure)",
        "context": {
            "generated_on": "string: <utc-timestamp-nanoes + timezone-offset>",
            "timeout": "string: <utc-timestamp-nanoes + timezone-offset>",
            "step_id": "uuid",
            "playbook_id": "uuid",
            "execution_id": "uuid"
        },
        "variables": {
            "__<var1>__": {
                "type": "<cacao.variable-type-ov>",
                "name": "__<var1>__",
                "description": "<string>",
                "value": "<string>",
                "constant": "<bool>",
                "external": "<bool>"
            },
            "__<var2>__": {
                "type": "<cacao.variable-type-ov>",
                "name": "__<var2>__",
                "description": "<string>",
                "value": "<string>",
                "constant": "<bool>",
                "external": "<bool>"
            }
        }
    },
    "meta": {
        "timestamp": "string: <utc-timestamp-nanoes + timezone-offset>",
        "sender_id": "uuid"
    }
}
@endjson

control

fieldcontenttypedescription
typepause or resume or stop or progressstringMessage type
message_idUUIDstringmessage uuid
capability_idUUIDstringCapability uuid to control

pause

The message is used to halt the further execution of the Fin. The following command will be responded to with a nack, unless it is resumed or stopped.

@startjson
{
    "type": "pause",
    "message_id" : "uuid",
    "capability_id": "uuid"
}
@endjson

resume

The message is used to resume a paused Fin, the response will be an ack if ok or a nack when the Fin could not be resumed.

@startjson
{
    "type": "resume",
    "message_id" : "uuid",
    "capability_id": "uuid"
}
@endjson

stop

The message is used to shut down the Fin. this will be responded to by ack, after that there will follow an unregister.

@startjson
{
    "type": "stop",
    "message_id" : "uuid",
    "capability_id": "uuid"
}
@endjson

progress

Ask for the progress of the execution of the

@startjson
{
    "type": "progress",
    "message_id" : "uuid",
    "capability_id": "uuid"
}
@endjson

Status response

fieldcontenttypedescription
typestatusstringMessage type
message_idUUIDstringmessage uuid
capability_idUUIDstringCapability uuid to control
progressready, working, paused, stoppedstringProgress of the execution or state it’s in.

Report the progress of the execution of the capability

@startjson
{
    "type": "status",
    "message_id" : "uuid",
    "capability_id": "uuid",
    "progress": "<execution status>"
}
@endjson

Common

These contain command parts that are used in different messages.

Security

fieldcontenttypedescription
versionversionstringVersion information of the protocol in semantic version schema e.g. 1.2.4-beta
channel_securityplaintextstringSecurity mechanism used for encrypting the channel and topic, plaintext is only supported at this time
@startjson
{
    "security": {
        "version": "0.0.0",
        "channel_security": "plaintext"
    }
}
@endjson

Variables

Variables information structure

fieldcontenttypedescription
typevariable typevariable-type-ovThe cacao variable type see CACAO V2 chapter 10.18, 10.18.4 Variable Type Vocabulary
namenamestringName of the variable this must be the same as the key on the map
descriptiondescriptionstringDescription of the variable
valuevaluestringValue of the variable
constanttrue or falseboolwhether it is constant
externaltrue or falseboolwhether it is external to the playbook
@startjson
{
    "__<var1>__": {
        "type": "<cacao.variable-type-ov>",
        "name": "<string>",
        "description": "<string>",
        "value": "<string>",
        "constant": "<bool>",
        "external": "<bool>"
        }
}
@endjson

Context

CACAO playbook context information structure

fieldcontenttypedescription
completed_on (optional)timestampstring<utc-timestamp-nanoes + timezone-offset>
generated_on (optional)timestampstring<utc-timestamp-nanoes + timezone-offset>
timeout (optional)durationstring<utc-timestamp-nanoes + timezone-offset>
step_idUUIDstringStep uuid that is referred to
playbook_idUUIDstringPlaybook uuid that is referred to
execution_idUUIDstringSOARCA execution uuid
@startjson
{
    "context": {
        "completed_on": "string: <utc-timestamp-nanoes + timezone-offset>",
        "generated_on": "string: <utc-timestamp-nanoes + timezone-offset>",
        "timeout": "string: <utc-timestamp-nanoes + timezone-offset>",
        "step_id": "uuid",
        "playbook_id": "uuid",
        "execution_id": "uuid"
    }
}
@endjson

Meta

Meta information for the fin protocol structure

fieldcontenttypedescription
timestamptimestampstring<utc-timestamp-nanoes + timezone-offset>
sender_idUUIDstringStep uuid that is referred to
@startjson
{
    "meta": {
        "timestamp": "string: <utc-timestamp-nanoes + timezone-offset>",
        "sender_id": "uuid"
    }
}
@endjson

Sequences

Registering a capability

@startuml

participant "SOARCA" as soarca
participant Capability as fin

soarca -> soarca : create [soarca] topic

fin -> fin : create [fin UUID] topic
soarca <- fin : [soarca] register
soarca --> fin : [fin UUID] ack 

@enduml

Sending command

@startuml

participant "SOARCA" as soarca
participant Capability as fin

soarca -> fin : [capability UUID] command
soarca <-- fin : [capability UUID] ack 

.... processing .... 

soarca <- fin : [capability UUID] result
soarca --> fin: ack

@enduml

Unregistering a capability

@startuml

participant "SOARCA" as soarca
participant Capability as fin
participant "Second capability" as fin2

... SOARCA initiate unregistering one fin ...

soarca -> fin : [SOARCA] unregister fin-id
soarca <-- fin : [SOARCA] ack 
note right fin2
    This capability does not respond to this message
end note

... Fin initiate unregistering ...

soarca <- fin : [SOARCA] unregister fin-id
soarca --> fin : [SOARCA] ack 
note right fin2
    This capability does not respond to this message
end note

... SOARCA unregister all ...

soarca -> fin : [SOARCA] unregister all == true
soarca <-- fin : [SOARCA] ack 
soarca <-- fin2 : [SOARCA] ack
note over soarca, fin2
    soarca will go down after this command
end note
@enduml

Control

@startuml

participant "SOARCA" as soarca
participant Capability as fin


soarca -> fin : [fin UUID] control message
soarca <-- fin : [fin UUID] status 

@enduml

4.3 - Native capabilities

Capabilities and transport mechanisms baked right into SOARCA

This page contains a list of capabilities that are natively implemented in SOARCA see details here. For MQTT-message-based capabilities, check here.

OpenC2 capability

The OpenC2 HTTP capability uses the http(s) transport layer as specified in OpenC2 HTTPS. It allows executing actions on an OpenC2-compatible security actuator.

CACAO documentation: OpenC2 HTTP Command

HTTP API capability

The HTTP capability allows sending arbitrary HTTP requests to other servers.

CACAO documentation: HTTP API Command

SSH capability

The SSH capability allows executing commands on systems running an SSH-server.

CACAO documentation: SSH Command

Powershell capability

The PowerShell capability allows executing commands on systems running an WinRM server.

CACAO documentation: PowerShell Command

5 - Design

The design of SOARCA

SOARCA consists of several key components:

  1. SOARCA Core: This is the heart of SOARCA, represented in green.
  2. SOARCA Native Capabilities: These are the functionalities explicitly defined in the Cacao v2 specification and are integral to the core. They are also represented in green.
  3. Fins: These are the extension capabilities, also known as Fins. They enhance the functionality and integration of SOARCA and are depicted in orange. These are not (yet) part of this repository, but may be implemented by partners or TNO in the future.

core

Core component overview

SOARCA interacts with many components, the following diagram shows the intended setup for SOARCA and its components. Currently, not all parts are fully implemented. Keep track of the release notes to see what’s implemented and what needs some work.

core detailed

5.1 - Application design

Details of the application architecture for SOARCA

Design decisions and core dependencies

To allow for fast execution and type-safe development SOARCA is developed in go. The application application can be deployed in Docker. Further dependencies are MQTT for the module system and go-gin for the REST API.

The overview on this page is aimed to guide you through the SOARCA architecture and components as well as the main flow.

Components

Components of SOARCA are displayed in the component diagram.

  • Green is implemented
  • Orange has limited functionality
  • Red is not started but will be added in future releases
@startuml
set separator ::

protocol /playbook #lightgreen
protocol /trigger #lightgreen
protocol /step #red
protocol /trusted/variables #red
protocol /status #red

class controller  #lightgreen
class database  #lightgreen
class log  #lightgreen
class core::decomposer #lightgreen
class core::executor  #lightgreen
class endpoints::playbook #lightgreen
class endpoints::trigger #lightgreen
class core::modules::http #lightgreen
class core::modules::ssh #lightgreen
class core::modules::openC2 #orange
class core::modules::fin #orange

class endpoints::step #red
class endpoints::variables #red
class endpoints::status #red


"/step" *-- endpoints::step 
"/playbook" *-- endpoints::playbook
"/trigger" *-- endpoints::trigger
"/status" *-- endpoints::status
"/trusted/variables" *-- endpoints::variables

endpoints *-down- controller
controller -* database
log *- controller
controller -down-* core::decomposer
core::decomposer -down-> core::executor
core::executor --> core::modules::openC2
core::executor --> core::modules::fin
core::executor --> core::modules::http
core::executor --> core::modules::ssh
@enduml

Classes

This diagram consists of the class structure used by SOARCA

@startuml

interface IPlaybook
interface IStatus
interface ITrigger
Interface IPlaybookDatabase
Interface IDatabase
Interface ICapability
Interface IDecomposer
Interface IExecuter

class Controller
class Decomposer
class PlaybookDatabase
class Status
class Mongo
class Capability
Class Executer


IPlaybook <|.. Playbook
ITrigger <|.. Trigger
IStatus <|.. Status
ICapability <|.. Capability
IExecuter <|.. Executer
Trigger -> IPlaybookDatabase
IPlaybookDatabase <- Playbook
IPlaybookDatabase <|.. PlaybookDatabase
IDatabase <-up- PlaybookDatabase
IDatabase <|.. Mongo
IDecomposer <- Trigger
IDecomposer <|.. Decomposer
IExecuter <- Decomposer
ICapability <- Executer
@enduml

Controller

The SOARCA controller will create all classed needed by SOARCA. The controller glues the endpoints and decomposer together. Each run will instantiate a new decomposer.

interface IPlaybook{
    void Get()
    void Get(PlaybookId id)
    void Add(Playbook playbook)
    void Update(Playbook playbook)
    void Remove(Playbook playbook)
}

interface IStatus{

}

interface ITrigger{
    void TriggerById(PlaybookId id)
    void Trigger(Playbook playbook)
}

Interface IPlaybookDatabase

Interface IDecomposer
Interface IExecuter

class Trigger
class Controller
class Decomposer

IPlaybook <|.. Playbook
ITrigger <|.. Trigger
IStatus <|.. Status


Trigger -> IPlaybookDatabase
IPlaybookDatabase <- Playbook
IPlaybookDatabase <|.. PlaybookDatabase


IDecomposer <- Trigger
IDecomposer <|.. Decomposer
IExecuter -> Decomposer

Main application flow

These sequences will show a simplified overview of how the SOARCA components interact.

The main flow of the application is the following. Execution will start by processing the JSON formatted CACAO playbook if successful the playbook is handed over to the Decomposer. This is where the playbook is decomposed into its parts and passed step by step to the executor. These operations will block the API until execution is finished. For now, no variables are exposed via the API to the caller.

Actor Caller
Caller -> Api
Api -> Trigger : /trigger
Trigger -> Decomposer : Trigger playbook as ad-hoc execution
loop for each step 
Decomposer -> Executor : Send step to executor
Executor -> Executor : select capability (ssh selected)
Executor -> Ssh : Command
Executor <-- Ssh : return
Decomposer <-- Executor
else execution failure (break loop)
Executor <-- Ssh : error
Decomposer <-- Executor: error
Decomposer -> Decomposer : stop execution

end 
Trigger <-- Decomposer : execution details
Api <-- Trigger : execution details
Caller <-- Api

5.2 - API Description

Descriptions for the SOARCA REST API endpoints

Endpoint description

We will use HTTP status codes https://en.wikipedia.org/wiki/List_of_HTTP_status_codes

@startuml
protocol UiEndpoint {
    GET     /playbook
    GET     /playbook/meta
    POST    /playbook
    GET     /playbook/playbook-id
    PUT     /playbook/playbook-id
    DELETE  /playbook/playbook-id


    POST    /trigger/playbook
    POST    /trigger/playbook/id

    GET     /step

    GET     /status
    GET     /status/playbook
    GET     /status/playbook/id
    GET     /status/history

}
@enduml

General messages

Error

When an error occurs a 400 status is returned with the following JSON payload, the original call can be omitted in production for security reasons.

responses: 400/Bad request

@startjson
{
    "status": "400",
    "message": "What went wrong.",
    "original-call": "<optional> Request JSON data",
    "downstream-call" : "<optional> downstream call JSON"
}
@endjson

Unauthorized

When the caller does not have valid authentication 401/unauthorized will be returned.

cacao playbook JSON

@startjson
{
            "type": "playbook",
            "spec_version": "cacao-2.0",
            "id": "playbook--91220064-3c6f-4b58-99e9-196e64f9bde7",
            "name": "coa flow",
            "description": "This playbook will trigger a specific coa",
            "playbook_types": ["notification"],
            "created_by": "identity--06d8f218-f4e9-4f9f-9108-501de03d419f",
            "created": "2020-03-04T15:56:00.123456Z",
            "modified": "2020-03-04T15:56:00.123456Z",
            "revoked": false,
            "valid_from": "2020-03-04T15:56:00.123456Z",
            "valid_until": "2020-07-31T23:59:59.999999Z",
            "derived_from": [],
            "priority": 1,
            "severity": 1,
            "impact": 1,
            "industry_sectors": ["information-communications-technology", "research", "non-profit"],
            "labels": ["soarca"],
            "external_references": [
                {
                    "name": "TNO SOARCA",
                    "description": "SOARCA Homepage",
                    "source": "TNO - COSSAS - HxxPS://LINK-TO-CODE-REPO.TLD",
                    "url": "HxxPS://LINK-TO-CODE-REPO.TLD",
                    "hash": "00000000000000000000000000000000000000000000000000000000000",
                    "external_id": "TNO/SOARCA 2023.01"
                }
            ],
            "features": {
                "if_logic": true,
                "data_markings": false
            },
            "markings": [],
            "playbook_variables": {
                "$$flow_data_location$$": {
                    "type": "string",
                    "value": "<mongodb_location>",
                    "description": "location of event and flow data",
                    "constant": true
                },
                "$$event_type$$": {
                    "type" : "string",
                    "value": "<event_type_string>",
                    "description": "type of incomming event / trigger",
                    "constant": true	
                }
            },
            "workflow_start": "step--d737c35f-595e-4abf-83ef-d0b6793556b9",
            "workflow_exception": "step--40131926-89e9-44df-a018-5f92f2df7914",
            "workflow": {
                "step--5ea28f63-ac32-4e5e-bd0c-757a50a3a0d7":{
                    "type": "single",
                    "name": "BI for CoAs",
                    "delay": 0,
                    "timeout": 30000,
                    "command": {
                        "type": "http-api",
                        "command": "hxxps://our.bi/key=VALUE"
                    },
                    "on_success": "step--71b15428-275a-49b5-9f09-3944972a0054",
                    "on_failure": "step--71b15428-275a-49b5-9f09-3944972a0054"
                },
                "step--71b15428-275a-49b5-9f09-3944972a0054": {
                    "type": "end",
                    "name": "End Playbook SOARCA Main Flow"
                }
            },
            "targets": { 

            },
            "extension_definitions": { }
        }
@endjson

/playbook

The playbook endpoints are used to create playbooks in SOARCA, new playbooks can be added, and current ones edited and deleted.

GET /playbook

Get all playbook ids that are currently stored in SOARCA.

Call payload

None

Response

200/OK with payload:

@startjson
[
    {
        "type": "playbook",
        "etc" : "etc..."   
    }
]
@endjson
Error

400/BAD REQUEST with payload: General error

GET /playbook/meta

Get all playbook ids that are currently stored in SOARCA.

Call payload

None

Response

200/OK with payload:

@startjson

[
    {
        "id": "<playbook id>",
        "name": "<playbook name>",
        "description": "<playbook description>",
        "created": "<creation data time>",
        "valid_from": "<valid from date time>",
        "valid_until": "<valid until date time>",
        "labels": ["label 1","label 2"]
    }
]

@endjson
Error

400/BAD REQUEST with payload: General error

POST /playbook

Create a new playbook and store it in SOARCA. The format is

Payload
@startjson
{
    "type": "playbook",
    "etc" : "etc..."   
}
@endjson
Response

201/CREATED

@startjson
{
    "type": "playbook",
    "etc" : "etc..."   
}
@endjson
Error

400/BAD REQUEST with payload: General error, 409/CONFLICT if the entry already exists

GET /playbook/{playbook-id}

Get playbook details

Call payload

None

Response

200/OK with payload:

@startjson
{
    "<cacao-playbook> (json)"
}
@endjson
Error

400/BAD REQUEST


PUT `/playbook/{playbook-id}``

An existing playbook can be updated with PUT.

Call payload

A playbook like cacao playbook JSON

Response

200/OK with the edited playbook cacao playbook JSON

Error

400/BAD REQUEST for malformed request

When updated it will return 200/OK or General error in case of an error.


DELETE /playbook/{playbook-id}

An existing playbook can be deleted with DELETE. When removed it will return 200/OK or general error in case of an error.

Call payload

None

Response

200/OK if deleted

Error

400/BAD REQUEST if the resource does not exist


POST /trigger/playbook/xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

Execute playbook with a specific id

Call payload

None

Response

Will return 200/OK when finished with playbook playbook.

@startjson
{
    "execution-id": "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx",
    "playbook-id": "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx"
}
@endjson
Error

400/BAD REQUEST general error on error.


POST /trigger/playbook

Execute an ad-hoc playbook

Call payload

A playbook like cacao playbook JSON

Response

Will return 200/OK when finished with the playbook.

@startjson
{
    "execution-id": "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx",
    "playbook-id": "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx"
}
@endjson
Error

400/BAD REQUEST general error on error.


/step [NOT in SOARCA V1.0]

Get capable steps for SOARCA to allow a coa builder to generate or build valid coa’s

GET /step

Get all available steps for SOARCA.

Call payload

None

Response

200/OK

@startjson
{
    
    "steps": [{
        "module": "executor-module",
        "category" : "analyses",
        "context" : "external",
        "step--5ea28f63-ac32-4e5e-bd0c-757a50a3a0d7":{
                    "type": "single",
                    "name": "BI for CoAs",
                    "delay": 0,
                    "timeout": 30000,
                    "command": {
                        "type": "http-api",
                        "command": "hxxps://our.bi/key=VALUE"
                    },
                    "on_success": "step--71b15428-275a-49b5-9f09-3944972a0054",
                    "on_failure": "step--71b15428-275a-49b5-9f09-3944972a0054"
                }}]
}
@endjson

Module is the executing module name that will do the executer call.

Category defines what kind of step is executed:

@startuml
enum workflowType {
    analyses
    action
    asset-look-up
    etc...
}
@enduml

Context will define whether the call is internal or external:

@startuml
enum workflowType {
    internal
    external
}
@enduml
Error

400/BAD REQUEST general error on error.


/status

The status endpoints are used to get various statuses.

GET /status

Call this endpoint to see if SOARCA is up and ready. This call has no payload body.

Call payload

None

Response

200/OK

@startjson
{
    "version": "1.0.0",
    "runtime": "docker/windows/linux/macos/other",
    "mode" : "development/production",
    "time" : "2020-03-04T15:56:00.123456Z",
    "uptime": {
        "since": "2020-03-04T15:56:00.123456Z",
        "milis": "uptime in miliseconds"
    }
}
@endjson
Error

5XX/Internal error, 500/503/504 message.


GET /status/fins | not implemented

Call this endpoint to see if SOARCA Fins are up and ready. This call has no payload body.

Call payload

None

Response

200/OK

@startjson
{
    "fins": [
        {
            "name": "Fin name",
            "status": "ready/running/failed/stopped/...",
            "id": "The fin UUID",
            "version": "semver verison: 1.0.0"
        }
    ]
}
@endjson
Error

5XX/Internal error, 500/503/504 message.


GET /status/reporters | not implemented

Call this endpoint to see which SOARCA reportes are used. This call has no payload body.

Call payload

None

Response

200/OK

@startjson
{
    "reporters": [
        {
            "name": "Reporter name"
        }
    ]
}
@endjson
Error

5XX/Internal error, 500/503/504 message.


GET /status/ping

See if SOARCA is up this will only return if all SOARCA services are ready

Call payload

None

Response

200/OK

pong

Usage example flow

Stand alone

@startuml
participant "SWAGGER" as gui
control "SOARCA API" as api
control "controller" as controller
control "Executor" as exe
control "SSH-module" as ssh


gui -> api : /trigger/playbook with playbook body
api -> controller : execute playbook playload
controller -> exe : execute playbook
exe -> ssh : get url from log
exe <-- ssh : return result
controller <-- exe : results
api <-- controller: results

@enduml

Database load and execution

@startuml
participant "SWAGGER" as gui
control "SOARCA API" as api
control "controller" as controller
database "Mongo" as db
control "Executor" as exe
control "SSH-module" as ssh


gui -> api : /trigger/playbook/playbook--91220064-3c6f-4b58-99e9-196e64f9bde7
api -> controller : load playbook from database
controller -> db: retreive playbook
controller <-- db: playbook json
controller -> controller: validate playbook
controller -> exe : execute playbook
exe -> ssh : get url from log
exe <-- ssh : return result
controller <-- exe : results
api <-- controller: results

@enduml

5.3 - Reporter API Description

Descriptions for the Reporter REST API endpoints

Endpoint description

We will use HTTP status codes https://en.wikipedia.org/wiki/List_of_HTTP_status_codes

@startuml
protocol Reporter {
    GET     /reporter
    GET     /reporter/{execution-id}
}
@enduml

/reporter

The reporter endpoint is used to fetch information about ongoing playbook executions in SOARCA

GET /reporter

Get all execution IDs of currently ongoing executions.

Call payload

None

Response

200/OK with payload:

@startjson
[
    {
        "executions": [
            {"execution_id" : "1", "playbook_id" : "a", "started" : "<timestamp>", "..." : "..."}, 
            "..."]
    }
]
@endjson

Error

400/BAD REQUEST with payload: General error

GET /reporter/{execution-id}

Get information about ongoing execution

Call payload

None

Response

Response data model:

fieldcontenttypedescription
type“execution_status”stringThe type of this content
idUUIDstringThe id of the execution
execution_idUUIDstringThe id of the execution
playbook_idUUIDstringThe id of the CACAO playbook executed by the execution
startedtimestampstringThe time at which the execution of the playbook started
endedtimestampstringThe time at which the execution of the playbook ended (if so)
statusexecution-status-enumstringThe current status of the execution
status_textexplanationstringA natural language explanation of the current status or related info
step_resultsstep_resultsdictionaryMap of step-id to related step execution data
request_intervalsecondsintegerSuggests the polling interval for the next request (default suggested is 5 seconds).
Step execution data
fieldcontenttypedescription
step_idUUIDstringThe id of the step being executed
startedtimestampstringThe time at which the execution of the step started
endedtimestampstringThe time at which the execution of the step ended (if so)
statusexecution-status-enumstringThe current status of the execution of this step
status_textexplanationstringA natural language explanation of the current status or related info
executed_byentity-identifierstringThe entity executed the workflow step. This can be an organization, a team, a role, a defence component, etc.
commands_b64list of base64list of stringA list of Base64 encodings of the commands that were invoked during the execution of a workflow step, including any values stemming from variables. These are the actual commands executed.
errorerrorstringError raised along the execution of the step
variablescacao variablesdictionaryMap of cacao variables handled in the step (both in and out) with current values and definitions
automated_executionbooleanstringThis property identifies if the workflow step was executed manually or automatically. It is either true or false.
Execution stataus

Table from Cyentific RNI workflow Status Vocabulary Name: execution-status-enum

Property NameDescription
successfully_executedThe workflow step was executed successfully (completed).
failedThe workflow step failed.
ongoingThe workflow step is in progress.
server_side_errorA server-side error occurred.
client_side_errorA client-side error occurred.
timeout_errorA timeout error occurred. The timeout of a CACAO workflow step is specified in the “timeout” property.
exception_condition_errorA exception condition error ocurred. A CACAO playbook can incorporate an exception condition at the playbook level and, in particular, with the “workflow_exception” property.

If the execution has completed and no further steps need to be executed

200/OK with payload:

@startjson
[
    {
        "type" :        "execution-status",
        "id" :          "<execution-id>",
        "execution_id" : "<execution-id>",
        "playbook_id" :  "<playbook-id>",
        "started" :     "<time-string>",
        "ended" :       "<time-string>",
        "status" :      "<status-enum-value>",
        "status_text":  "<status description>",
        "errors" :      ["error1", "..."],
        "step_results" : {
            "<step-id-1>" : {
                "execution_id": "<execution-id>",
                "step_id" :     "<step-id>",
                "started" :     "<time-string>",
                "ended" :       "<time-string>",
                "status" :      "<status-enum-value>",
                "status_text":  "<status description>",
                "errors" :      ["error1", "..."],
                "variables":    {
                    "<variable-name-1>" : {
                        "type":         "<type>",
                        "name":         "<variable-name>",
                        "description":  "<description>",
                        "value":        "<value>",
                        "constant":     "<true/false>",
                        "external":     "<true/false>"
                    }
                }
            }
        },
        "request_interval" : "<n-seconds>"
    }
]
@endjson

The payload will include all information that the finished execution has created.


If the execution is still ongoing:

206/Partial Content
with payload equal to the 200 response, but impliclty not including all information from the execution, since the execution is still ongoing.

The step results object will list the steps that have been executed until the report request, and those that are being executed at the moment of the report request.

The "request_interval" suggests the polling interval for the next request (default suggested is 5 seconds).

#### Error
400/BAD REQUEST with payload:
General error

404/NOT FOUND
No execution with the specified ID was found.

5.4 - Decomposer

Playbook deconstructor architecture

Decomposer structure

The decomposer will parse playbook objects to individual steps. This allows it to schedule new executor tasks.

Each incoming playbook will executed individually. Decomposing is done up to the step level.


struct ExecutionDetails{
    uuid executionId 
    uuid playbookId
}

Interface IDecomposer{
ExecutionDetails, error Execute(cacao playbook)
error getStatus(uuid playbookId)    
}
Interface IExecutor

class Controller
class Decomposer

IDecomposer <- Controller
IDecomposer <|.. Decomposer
IExecutor <- Decomposer

IExecutor

Interface for interfacing with the Executor this will in turn select and execute the command on the right module or fin.

Execution details

The struct contains the details of the execution (execution id which is created for every execution) and the playbook id. The combination of these is unique.

Decomposition of playbook

participant caller 
participant "Playbook decomposer" as decomposer
participant "Playbook state" as queue
participant Executor as exe



caller -> decomposer: Execute
caller <-- decomposer: ExecutionStatus

decomposer -> queue: store state
decomposer <-- queue:

loop for all playbook steps
    decomposer -> queue: load state
    decomposer <-- queue: 
    decomposer -> decomposer: parse step
    decomposer -> decomposer: parse command
   
    decomposer -> exe: execute command
    note over exe: correct executer is selected
    ... Time has passed ...
    decomposer <-- exe
end loop

5.5 - Executer

Design of the SOARCA step executer

Components

The executor consists of the following components.

  • Action executor
  • Playbook action executor
  • if-condition executor
  • while-condition executor
  • parallel executor

The decomposer interacts with every executor type. They all have separate interfaces to handle new step types in the future without changing the current interfaces.


package action{
    interface IExecutor {
        ..., err Execute(...)
    }
}

package playbookaction{
    interface IExecutor {
        ..., err Execute(...)
    }
}

package ifcondition{
    interface IExecutor {
        ..., err Execute(...)
    }
}

package whilecondition{
    interface IExecutor {
        ..., err Execute(...)
    }
}

package parallel{
    interface IExecutor {
        ..., err Execute(...)
    }
}


interface ICapability{
    variables, error Execute(Metadata, command, variable[], target, agent)
    string GetModuleName()
}

class "Decomposer" as decomposer
class "Action Executor" as Executor 
class "Playbook Executor" as playbook
class "Parallel Executor" as parallelexecutor
class "While Executor" as while
class "If condition Executor" as condition

class "Ssh" as ssh
class "OpenC2" as openc2
class "HttpApi" as api
class "Fin" as fin


action.IExecutor <|.. Executor
ICapability <-up- Executor
ICapability <|.. ssh
ICapability <|.. openc2
ICapability <|.. api
ICapability <|.. fin

playbookaction.IExecutor <|.. playbook
ifcondition.IExecutor <|.. condition
whilecondition.IExecutor <|.. while
parallel.IExecutor <|.. parallelexecutor

decomposer -down-> playbookaction.IExecutor
decomposer -down-> ifcondition.IExecutor
decomposer -down-> whilecondition.IExecutor
decomposer -down-> parallel.IExecutor
decomposer -down-> action.IExecutor

Action executor

The action executor consist of the following components

  • The capability selector
  • Native capabilities (command executors)
  • MQTT capability to interact with: Fin capabilities (third-party executors)

The capability selector will select the implementation which is capable of executing the incoming command. There are native capabilities based on the CACAO command-type-ov:

  • Currently implemented
    • ssh
    • http-api
    • openc2-http
    • powershell
  • Coming soon
    • manual
  • Future (potentially)
    • bash
    • caldera-cmd
    • elastic
    • jupyter
    • kestrel
    • sigma
    • yara

Native capabilities

The executor will select a module that is capable of executing the command and pass the details to it. The capability selection is performed based on the agent type (see Agent and Target Common Properties in the CACAO 2.0 spec). The convention is that the agent type must equal soarca-<capability identifier>, e.g. soarca-ssh or soarca-openc2-http.

The result of the step execution will be returned to the decomposer. A result can be either output variables or error status.

MQTT executor -> Fin capabilities

The Executor will put the command on the MQTT topic that is offered by the module. How a module handles this is described in the link:modules.adoc[module documentation]

Component overview


package "Controller" {
component Decomposer as parser

}
package "Executor" {
    component SSH as exe2
    component "HTTP-API" as exe1
    component MQTT as exe3
}

package "Fins" {
    component "VirusTotal" as virustotal
    component "E-mail Sender" as email
}

parser -- Executor
exe3 -- Fins : " MQTT topics"

Sequences

Example execution for SSH commands with SOARCA native capability.

@startuml

participant Decomposer as decomposer
participant "Capability selector" as selector
participant "SSH executor" as ssh

decomposer -> selector : Execute(...)
alt capability in SOARCA
    selector -> ssh : execute ssh command
    ssh -> ssh : 
    selector <-- ssh : results
    decomposer <-- selector : OnCompletionCallback
else capability not available 
    decomposer <-- selector : Execution failure
    note right: No capability can handle command \nor capability crashed etc..
end

Playbook action executor

The playbook executor handles execution of playbook action steps. The variables from the top level playbook are injected into the be executed playbook. It could happen that in the downstream playbook the variables collide with the top level playbook. In this case the top level playbook variables are NOT transferred to the downstream playbook. Agents and Targets cannot be transferred between playbooks at this time. Playbooks are only loaded in the executor and then a new Decomposer is created to execute the playbook.

The result of the step execution will be returned to the decomposer. A result can be either output variables or error status.

package playbookaction{
    interface IExecutor {
        Variables, err Execute(meta, step, variables)
    }
}
class "Decomposer" as decomposer
class "Action Executor" as exe 
interface "IPlaybookController" as controller
interface "IDatabaseController" as database
  
playbookaction.IExecutor <|.. exe
decomposer -> playbookaction.IExecutor
exe -> controller
database <- exe

If condition executor

The if-condition executor will process a cacao if-condition step and determine it’s output.

The result of the step comparison will be returned to the decomposer. A result can be either a next step id and/or error status.

While condition executor

The if-condition executor will process a cacao while-condition step and determine it’s output.

The result of the step comparison will be returned to the decomposer. A result can be either a next step id and/or error status. Only STIX comparison expressions are implemented at this time.

Parallel step executor

The parallel executor will execute the parallel step. This wil be done in sequence to simplify implementation. As parallel steps must not be depended on each other sequential execution is possible. Later this will be changed.

5.6 - Executer Modules

Native executer modules

Executer modules are part of the SOARCA core. Executer modules perform the actual commands in CACAO playbook steps.

Native modules in SOARCA

The following capability modules are currently defined in SOARCA:

  • ssh
  • http-api
  • openc2-http
  • powershell

The capability will be selected based on the agent in the CACAO playbook step. The agent should be of type soarca and have a name corresponding to soarca-[capability name].

SSH capability

This capability executes SSH Commands on the specified targets.

This capability support User Authentication using the user-auth type. For SSH authentication username/password is authentication supported.

Success and failure

The SSH step is considered successful if a proper connection to each target can be initialized, the supplied command executes without error, and returns with zero exit status.

In every other circumstance the step is considered to have failed.

Variables

This module does not define specific variables as input, but variable interpolation is supported in the command and target definitions. It has the following output variables:

{
    "__soarca_ssh_result__": {
        "type": "string",
        "value": "<stdout of the last command>"
    }
}

Example

{
    "workflow": {
        "action--7777c6b6-e275-434e-9e0b-d68f72e691c1": {
            "type": "action",
            "agent": "soarca--00010001-1000-1000-a000-000100010001",
            "targets": ["linux--c7e6af1b-9e5a-4055-adeb-26b97e1c4db7"],
            "commands": [
                {
                    "type": "ssh",
                    "command": "ls -la"
                }
            ]
        }
    },
    "agent_definitions": {
        "soarca--00010001-1000-1000-a000-000100010001": {
            "type": "soarca",
            "name": "soarca-ssh"
        }
    },
    "target_definitions": {
        "linux--c7e6af1b-9e5a-4055-adeb-26b97e1c4db7": {
            "type": "linux",
            "name": "target",
            "address": { "ipv4": ["10.0.0.1"] }
        }
    }
}

HTTP-API capability

This capability implements the HTTP API Command.

Both HTTP Basic Authentication with user_id/password and token based OAuth2 Authentication are supported.

At this time, redirects are not supported.

Success and failure

The command is considered to have successfully completed if a successful HTTP response is returned from each target. An HTTP response is successful if it’s response code is in the range 200-299.

Variables

This capability supports variable interpolation in the command, port, authentication info, and target definitions.

The result of the step is stored in the following output variables:

{
    "__soarca_http_api_result__": {
        "type": "string",
        "value": "<http response body>"
    }
}

Example

{
    "workflow": {
        "action--8baa7c78-751b-4de9-81d4-775806cee0fb": {
            "type": "action",
            "agent": "soarca--00020001-1000-1000-a000-000100010001",
            "targets": ["http-api--4ebae9c3-9454-4e28-b25b-0f43cd97f9e0"],
            "commands": [
                {
                    "type": "http-api",
                    "command": "GET /overview HTTP/1.1",
                    "port": "8080"
                }
            ]
        }
    },
    "agent_definitions": {
        "soarca--00020001-1000-1000-a000-000100010001": {
            "type": "soarca",
            "name": "soarca-http-api"
        }
    },
    "target_definitions": {
        "http-api--4ebae9c3-9454-4e28-b25b-0f43cd97f9e0": {
            "type": "http-api",
            "name": "target",
            "address": { "dname": ["my.server.com"] }
        }
    }
}

OpenC2 capability

This capability implements the OpenC2 HTTP Command, by sending OpenC2 messages using the HTTPS transport method.

It supports the same authentication mechanisms as the HTTP-API capability.

Success and failure

Any successful HTTP response from an OpenC2 compliant endpoint (with a status code in the range 200-299) is considered a success. Connection failures and HTTP responses outside the 200-299 range are considered a failure.

Variables

It supports variable interpolation in the command, headers, and target definitions.

The result of the step is stored in the following output variables:

{
    "__soarca_openc2_http_result__": {
        "type": "string",
        "value": "<openc2-http response body>"
    }
}

Example

{
    "workflow": {
        "action--aa1470d8-57cc-4164-ae07-05745bef24f4": {
            "type": "action",
            "agent": "soarca--00030001-1000-1000-a000-000100010001",
            "targets": ["http-api--5a274b6d-dc65-41f7-987e-9717a7941876"],
            "commands": [{
                "type": "openc2-http",
                "command": "POST /openc2-api/ HTTP/1.1",
                "content_b64": "ewogICJoZWFkZXJzIjogewogICAgInJlcXVlc3RfaWQiOiAiZDFhYzA0ODktZWQ1MS00MzQ1LTkxNzUtZjMwNzhmMzBhZmU1IiwKICAgICJjcmVhdGVkIjogMTU0NTI1NzcwMDAwMCwKICAgICJmcm9tIjogInNvYXJjYS5ydW5uZXIubmV0IiwKICAgICJ0byI6IFsKICAgICAgImZpcmV3YWxsLmFwaS5jb20iCiAgICBdCiAgfSwKICAiYm9keSI6IHsKICAgICJvcGVuYzIiOiB7CiAgICAgICJyZXF1ZXN0IjogewogICAgICAgICJhY3Rpb24iOiAiZGVueSIsCiAgICAgICAgInRhcmdldCI6IHsKICAgICAgICAgICJmaWxlIjogewogICAgICAgICAgICAiaGFzaGVzIjogewogICAgICAgICAgICAgICJzaGEyNTYiOiAiMjJmZTcyYTM0ZjAwNmVhNjdkMjZiYjcwMDRlMmI2OTQxYjVjMzk1M2Q0M2FlN2VjMjRkNDFiMWE5MjhhNjk3MyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgfQogIH0KfQ==",
                "headers": {
                    "Content-Type": ["application/openc2+json;version=1.0"]
                }
            }]
        }
    },
    "agent_definitions": {
        "soarca--00030001-1000-1000-a000-000100010001": {
            "type": "soarca",
            "name": "soarca-openc2-http"
        }
    },
    "target_definitions": {
        "http-api--5a274b6d-dc65-41f7-987e-9717a7941876": {
            "type": "http-api",
            "name": "openc2-compliant actuator",
            "address": { "ipv4": ["187.0.2.12"] }
        }
    }
}

PowerShell capability

This capability implements the PowerShell Command, by sending PowerShell commands using the WinRM transport method.

It supports the username, password authentication mechanism.

Success and failure

Any successful command will have a __soarca_powershell_result__. If an error occurs on the target a __soarca_powershell_error__ populated will be returned and Error will be set.

Variables

It supports variable interpolation in the command, headers, and target definitions.

The result of the step is stored in the following output variables:

{
    "__soarca_powershell_result__": {
        "type": "string",
        "value": "<raw powershell output>"
    }, 
    "__soarca_powershell_error__": {
        "type": "string",
        "value": "<raw powershell error output>"
    },
}

Example

{
    "workflow": {
        "action--aa1470d8-57cc-4164-ae07-05745bef24f4": {
            "type": "action",
            "agent": "soarca--00040001-1000-1000-a000-000100010001",
            "targets": ["net-address--d42d6731-791d-41af-8fa4-7b5699dfe402"],
            "commands": [{
                "type": "powershell",
                "command": "pwd"
            }]
        }
    },
    "agent_definitions": {
        "soarca--00040001-1000-1000-a000-000100010001": {
            "type": "soarca",
            "name": "soarca-powershell"
        }
    },
    "target_definitions": {
        "net-address--d42d6731-791d-41af-8fa4-7b5699dfe402": {
            "type": "net-address",
            "name": "Windows Server or Client with WinRM enabled",
            "address": { "ipv4": ["187.0.2.12"] }
        }
    }
}

MQTT fin module

This module is used by SOARCA to communicate with fins (capabilities) see fin documentation for more information

5.7 - Database

Database details of SOARCA

OARCA Database architecture, SOARCA makes use of MongoDB It is used to store and retrieve playbooks. Later it will also store individual steps.

Mongo

SOARCA employs separate collections in Mongo, utilizing a dedicated database object for each of them:

  • Playbook
  • Step
Interface IDatabase{
    void create(JsonData playbook)
    JsonData read(Id playbookId)
    void update(Id playbookId, JsonData playbook)
    void remove(Id playbookId)
}
Interface IPlaybookDatabase
Interface IStepDatabase

class Controller
class PlaybookDatabase

class Mongo


Controller -> IPlaybookDatabase
IStepDatabase <- Controller
IPlaybookDatabase <|.. PlaybookDatabase
IStepDatabase <|.. StepDatabase
StepDatabase -> IDatabase
IDatabase <- PlaybookDatabase

IDatabase <|.. Mongo

Getting data

Getting playbook data

participant Controller as controller
participant "Playbook Database" as playbook
database Database as db

controller -> playbook : get(id)
playbook -> db : read(playbookId)
note right
    When the create fails an error will be thrown
end note
playbook <-- db : "playbook JSON"
controller <-- playbook: "CacaoPlaybook Object"

Writing playbook data

participant Controller as controller
participant "Playbook Database" as playbook
database Database as db

controller -> playbook : set(CacaoPlaybook Object)
playbook -> db : create(playbook JSON)
note right
    When the create fails an error will be thrown
end note

Update playbook data

participant Controller as controller
participant "Playbook Database" as playbook
database Database as db

controller -> playbook : update(CacaoPlaybook Object)
playbook -> db : update(playbook id,playbook JSON)
note right
    When the create fails an error will be thrown
end note
playbook <-- db : true
controller <-- playbook: true

Delete playbook data


participant Controller as controller
participant "Playbook Database" as playbook
database Database as db

controller -> playbook : remove(playbook id)
playbook -> db : remove(playbook id)
note right
    When the create fails an error will be thrown
end note

Handling an error

participant Controller as controller
participant "Playbook Database" as playbook
database Database as db

controller -> playbook : remove(playbook id)
playbook -> db : remove(playbook id)
playbook <-- db: error 
note right
    playbook does not exists
end note
controller <-- playbook: error 

5.8 - Reporter

Reporting of Playbook worfklow information and steps execution

SOARCA utilizes push-based reporting to provide information on the instantiation of a CACAO workflow, and information on the execution of workflow steps.

General Reporting Architecture

For the execution of a playbook, a Decomposer and invoked Executors are injected with a Reporter. The Reporter maintains the reporting logic that reports execution information to a set of specified and available reporting targets.

A reporting target can be internal to SOARCA, such as a Cache. A reporting target can also be a third-party tool, such as an external SOAR/ SIEM, or incident case management system (see connectors).

Upon execution trigger for a playbook, information about the chain of playbook steps to be executed will be pushed to the targets via dedicated reporting classes.

Along the execution of the workflow steps, the reporting classes will dynamically update the steps execution information such as output variables, and step execution success or failure.

The reporting modules enables the population and updating of views and data concerning workflow composition and its dynamic execution results. This data can be transmitted to SOARCA internal reporting components such as a cache, as well as to third-party tools (see connectors).

The schema below represents the architecture concept.

@startuml
set separator ::

interface IStepReporter{
   ReportStepStart() error
   ReportStepEnd() error
}

interface IWorkflowReporter{
    ReportWorkflowStart() error
    ReportWorkflowEnd() error
}


interface IDownStreamReporter {
    ReportWorkflowStart() error
    ReportWorkflowEnd() error
	ReportStepStart() error
	ReportStepEnd() error
}

class Reporter {
    reporters []IDownStreamReporter
    RegisterReporters() error
    ReportWorkflowStart()
    ReportWorkflowEnd()
    ReportStepStart()
    ReportStepEnd()
}

class Cache
class TheHiveReporter
class 3PToolReporter

class Decomposer
class Executor

Decomposer -right-> IWorkflowReporter
Executor -left-> IStepReporter

Reporter .up.|> IStepReporter
Reporter .up.|> IWorkflowReporter
Reporter -right-> IDownStreamReporter

Cache .up.|> IDownStreamReporter
TheHiveReporter .up.|> IDownStreamReporter
3PToolReporter .up.|> IDownStreamReporter

Reporting is Asynchronous

Reporting functionalities are triggered asynchronously by means of go routines, such that reporting logic does not affect the execution timings of the CACAO playbooks. Note that this also implies that there might be small inconsistencies between the actual status of an execution, and what can be found in the reporting. For instance, reporting on the status of a step N, might take longer than reporting on the status of step N+1. Typically, the actual playbook execution may be slightly ahead of the reporting - and generally within a seconds-wide window. Execution timings, though, are always reported correctly, since we generated them within the workflow execution itself, and not within the reporting module.

The flow diagram below highlights the asynchronous mechanisms of reporting.

@startuml

participant "Decomposer" as decomposer
participant "Executor" as executor
participant "Reporter" as reporter
participant "DonwstreamReporter" as ds_reporter

decomposer -> reporter : ReportWorkflowStart()
reporter -->> ds_reporter : go ReportWorkflowStart()
decomposer -> executor : Execute step
activate executor
executor -> reporter : ReportStepStart()
reporter -->> ds_reporter : go ReportStepStart()
executor -> reporter : ReportStepEnd()
reporter -->> ds_reporter : go ReportStepEnd()
decomposer <- executor : return (variables, errors)
deactivate executor
decomposer -> reporter : ReportWorkflowEnd()
reporter -->> ds_reporter : go ReportWorkflowEnd()

@enduml

Note that the main reporter module is invoked synchronously. In turn, the main reporter module calls all downstream reporters, which implement the actual reporting logic, asynchronously. Also note any eventual reporting error never stops an execution, and only logs a warning.

Interfaces

The reporting logic and extensibility is implemented in the SOARCA architecture by means of reporting interfaces. At this stage, we implement an IWorkflowReporter to push information about the entire workflow to be executed, and an IStepReporter to push step-specific information as the steps of the workflow are executed.

A high level Reporter component will implement both interfaces, and maintain the list of DownStreamRepporters activated for the SOARCA instance. The Reporter class will invoke all reporting functions for each active reporter. The Executer and Decomposer components will be injected each with the Reporter though, as interface of respectively workflow reporter, and step reporter, to keep the reporting scope separated.

The DownStream reporters will implement push-based reporting functions specific for the reporting target, as shown in the IDownStreamReporter interface. Internal components to SOARCA, and third-party tool reporters, will thus implement the IDownStreamReporter interface.

Native Reporters

SOARCA implements internally reporting modules to handle database and caches reporting.

Cache reporter

The Cache reporter mediates between decomposer and executors, database, and reporting APIs. As DownStreamReporter, the Cache stores workflow and step reports in-memory for an ongoing execution. As IExecutionInformant, the Cache provides information to the reporting API. The schema below shows how it is positioned in the SOARCA architecture.

@startuml

protocol /reporter

interface IDownStreamReporter {
    ReportWorkflow() error
	ReportStep() error
}
interface IDatabase

interface IExecutionInformer

class ReporterApi
class Reporter
class Cache {
    cache []ExecutionEntry
}


"/reporter" -right-> ReporterApi

Reporter -> IDownStreamReporter
Cache -left-> IDatabase

Cache .up.|> IDownStreamReporter
Cache .up.|> IExecutionInformer
ReporterApi -down-> IExecutionInformer

The Cache thus reports the execution information downstream both in the database, and in memory. Upon execution information requests from the /reporter API, the cache can provide information fetching either from memory, or querying the database.

Connectors and 3P tools reporting

Reporting towards a 3rd Party tool is implemented in SOARCA by means of two components.

  1. a 3PTool DownStreamReporter that organizes the execution information according to the data formats of the 3rd Party tool, and
  2. a Connector module that receives data in 3PTool-specific formats, and handles the connectivity calls towards the 3P tool

At the current stage, the following 3rd Party tool DownStreamReporters and Connectors will be implemented in SOARCA.

TheHive

The TheHiveReporter reports on the execution of a playbook to an instance of The Hive platform. In order to be used, The Hive’s address and api key need to be configured in the environment variables of the SOARCA instance, either in the .env file for a source-code-built SOARCA instance, or in the SOARCA docker-compose for Docker builds. SOARCA will negotiate the authorization with the The Hive instance upon SOARCA initialization.

The TheHiveConnector receives execution information from the TheHiveReporter, and performs the actual network calls to report such infromation to the The Hive instance.

@startuml
set separator ::

interface IDownStreamReporter {
    ReportWorkflowStart() error
    ReportWorkflowEnd() error
	ReportStepStart() error
	ReportStepEnd() error
}

interface ITheHiveConnector {
}


class TheHiveReporter
class TheHiveConnector

TheHiveReporter .up.|> IDownStreamReporter
TheHiveReporter -right-> ITheHiveConnector
TheHiveConnector .up.|> ITheHiveConnector

Future plans

At this stage, third-party tools integrations may be built in SOARCA via packages implementing reporting logic for the specific tools. Alternatively, third-party tools may implement pull-based mechanisms (via the API) to get information from the execution of a playbook via SOARCA.

In the near future, we will (also) make available a SOARCA Report API that can establish a WebSocket connection to a third-party tool. As such, this will thus allow SOARCA to push execution updates as they come to third-party tools, without external tools having to poll SOARCA.

Furthermore, we will define more Connector donwstream reporters which will implement workflow execution reporting for specific platforms and products.

5.9 - Logging

SOARCA support extensive logging. Logging is based on the logrus framework.

SOARCA supports extensive logging. Logging is based on the logrus framework.

Format

Logging can be done in different formats suitable for your application. The following formats are available:

  • JSON default
  • Plain text

Destination

  • std::out default (terminal)
  • To file (to log file path)

Later:

  • syslog (NOT YET IMPLEMENTED)

Log levels

SOARCA supports the following log levels. Also is indicated how they are used.

  • PANIC (non fixable error system crash)
  • FATAL (non fixable error, restart would fix)
  • ERROR (operation went wrong but can be caught by other higher component)
  • WARNING (let the user know some operation might not have the expected result but execution can continue on normal path)
  • INFO default (let the user know that a major event has occurred)
  • DEBUG (add some extra detail to normal execution paths)
  • TRACE (get some fine grained detail from the logging)

Types of logging

SOARCA will log different information, these will be combined in the same output.

Runtime logging

Runtime logging wil include the running state of SOARCA, errors encountered when registering modules etc.

Security event logging

Will log the status of the execution of an playbook, database updates of playbooks

Using the logger (developer)

To use SOARCA logging you can add the following to your module.

type YourModule struct {
}

var component = reflect.TypeOf(YourModule{}).PkgPath()
var log *logger.Log

func init() {
	log = logger.Logger(component, logger.Info, "", logger.Json)
}

Changing log level

To change logging for your SOARCA instance you can use the following environment variables

variablecontentdescription
LOG_GLOBAL_LEVEL[Log levels]One of the specified log levels. Defaults to info
LOG_MODEdevelopment | productionIf production is chosen the LOG_GLOBAL_LEVEL is used for all modules defaults to production
LOG_FILE_PATHfilepathPath to the logfile you want to use for all logging. Defaults to "" (empty string)
LOG_FORMATtext | jsonThe logging can be in plain text format or in JSON format. Defaults to json

This can be set as environment variables or loaded through the .env

LOG_GLOBAL_LEVEL: "info"
LOG_MODE: "production"
LOG_FILE_PATH: ""
LOG_FORMAT: "json"

6 - Release Notes

V1.0


Finally, the first release of the alpha release of SOARCA is here! In the first release, we emphasize laying the initial foundation and design of SOARCA to enable proof-of-concept demonstrations. Included in this release:

  • Execution of CACAO action steps via:
    • SSH
    • HTTP API
    • OpenC2
  • Logging capabilities
  • Efficient storage for playbooks
  • Design fin-protocol: the protocol description between SOARCA and the TO BE ADDED

7 - About

About COSSAS

SOARCA finds its home within the Community for Open Source Security Automation Software (COSSAS). COSSAS offers a continuously expanding base of novel software components for cyber security automation that SOC, CERT and CTI professionals can deploy and trial in their operational environments. COSSAS is an initiative of TNO.

About TNO

SOARCA is a project initiated and supported by TNO, the Netherlands Organisation for Applied Scientific Research. TNO, an independent research, development, and consultancy organization in the Netherlands, is dedicated to driving innovation for a safer, healthier, and more sustainable life. TNO focuses on areas such as sustainability, health, safety, and digital transformation. Its mission is to make knowledge serve the common good, connecting expertise to create impactful innovations. TNO actively engages with society, aiming for a positive impact through its work and insights. Learn more about TNO on their official website.

Core Team

  • Maarten de Kruijf (TNO)
  • Jan-Paul Konijn (TNO)
  • Hidde-Jan Jongsma (TNO)
  • Luca Morgese (TNO)
  • Richard Kerkdijk (TNO)
  • Frank Fransen (TNO)
  • Shari Finner (TNO)

Special Thanks

A special thanks to:

  • Sebastiaan Huskins for laying the initial and technical foundation for SOARCA.
  • Myla Fransen for the logo designs.
  • David Roefs as part of the AInception team

About SOARCA

This work has received funding from the European Union’s Horizon Europe Energy Research and Innovation programme under Grant Agreement No. 101075665 (eFORT project) and the European Defence Fund (EDF) under Grant Agreement No. 101103385 (AInception project). Views and opinions expressed are those of the authors only and do not necessarily reflect those of the European Union or European Climate, Infrastructure and Environment Executive Agency (CINEA). Neither the European Union nor the granting authority can be held responsible for them.

eu-funded

8 - Contribution Guidelines

How to contribute to SOARCA

SOARCA is an open-source project written in Golang and we love getting patches and contributions, and feature suggestions to make SOARCA and its docs even better. We welcome participation from anyone, regardless of their affiliation with OASIS. We invite constructive contributions and feedback from all contributors, following the standard practices for participation in GitHub public repository projects.

We expect everyone to follow our Code of Conduct, the licenses for each repository, and agree to our Contributor License Agreement when you make your first contribution.

Thank you for contributing to our project! Your efforts make a difference.

Contributing to SOARCA

The SOARCA itself lives on github.

How to contribute

Before making contributions to the project repositories, please follow these general steps for GitHub contribution.

I found a bug / Creating issues

If there’s something you’d like to see in SOARCA (or if you’ve found something that isn’t working the way you’d expect), but you’re not sure how to fix it yourself, please create an issue. Make sure to adhere to the structure of an issue submission. Fully comprehend the problem at hand and provide comprehensive details in your issue description.

Feature additions or requests

You can submit feature requests either through GitHub issues or the discussion pages.

Code reviews

Every submission, including those from project members, must undergo review and approval from at least one core maintainer. GitHub pull requests are utilized for this process. Consult GitHub Help for more information on using pull requests.

Branch naming

The CI is configured to only allow for certain branch naming namely:

  • master
  • development
  • feature/
  • feature/docs/
  • bugfix/
  • release/x.x
  • hotfix/

Coding style

The project has opted to select the go style guide with some exceptions:

Communication channels

Feel free to engage with the community for discussions and assistance via one of the following channels:

Contributing to these docs

Would you like to enhance our documentation? Our documentation is built using the Hugo framework along with the Docsy theme template.

Quick start with Hugo and Docsy

  1. Install Hugo; the installation guide can be found here.
  2. Clone our repository, and if you make changes, fork our repository. Use the following command to clone: git clone <repository_url>.
  3. All the documentation for the GitHub Pages lives under /documentation. Use the cd documentation && hugo serve command to preview the documentation locally. Open http://localhost:1313 in your web browser to view the documentation. In most cases, docsy will automatically reload the site to reflect any changes to the documentation or the code. Changes to some parts of the docsy code may require manually reloading the page or restarting the container.
  4. Continue with the usual GitHub workflow to edit files, commit them, push the changes up to your fork, and create a pull request.

Updating a single page

If you’ve just spotted something you’d like to change while using the docs, Docsy has a shortcut for you:

  1. Click Edit this page in the top right-hand corner of the page.
  2. If you don’t already have an up-to-date fork of the project repo, you are prompted to get one - click Fork this repository and propose changes or Update your Fork to get an up-to-date version of the project to edit. The appropriate page in your fork is displayed in edit mode.

License

The project is licensed under the Apache License 2.0. See full license here.

8.1 - Contributor Covenant Code of Conduct

Our Pledge

We, as contributors and maintainers, pledge to make participation in our project and community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

Our Standards

Examples of behavior that contributes to creating a positive environment include:

  • Using welcoming and inclusive language
  • Respecting differing viewpoints and experiences
  • Gracefully accepting constructive criticism
  • Focusing on what is best for the community
  • Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

  • The use of sexualized language or imagery and unwelcome sexual attention or advances
  • Trolling, insulting/derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others’ private information, such as a physical or electronic address, without explicit permission
  • Any other conduct that could reasonably be considered inappropriate in a professional setting

Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team via Github or join our Slack channel. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership.

Attribution

This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at here For answers to common questions about this code of conduct, see

9 - Frequently asked questions

General questions

Does SOARCA have a GUI?

SOARCA currently does not feature a GUI for tracking the progress of playbook execution. To edit CACAO v2.0 playbooks, consider using the CACAO Roaster project.