OpenC3 Security Layer

We take OpenC3 — an open-source command, control, and communication platform, as an example of a mission-critical application. Tirreno, in this case, acts as a forensic layer to track user actions, prevent account takeovers, help detect insider threats, and identify unusual access patterns.

Setting up data capture on an OpenC3 cluster requires only adding a Ruby middleware and building a custom image of one container.

Prerequisites

Attention

Before implementing the provided Docker configuration in a production environment, testing it in a development or staging environment is crucial. Using these settings without prior testing and validation is strongly discouraged and done entirely at your own risk.

Tirreno Integration

Basics

In order to enable integration with Tirreno, information about the original OpenC3 requests has to be transmitted to the Tirreno API.

To achieve this we need to add a middleware to openc3-cosmos-cmd-tlm-api container, build own image and use it in Docker compose file.

Configuration Details

Firstly we need to clone OpenC3/cosmos repository

git clone https://github.com/OpenC3/cosmos

Navigate to cloned repository, copy the following into openc3-cosmos-cmd-tlm-api/lib/collect.rb and substitute Sensor URL and Api-Key:

 1require 'uri'
 2require 'net/http'
 3require 'json'
 4
 5class CollectMiddleware
 6  def initialize(app)
 7    @app = app
 8  end
 9
10  def call(env)
11    req = Rack::Request.new(env)
12
13    body = req.body.read rescue ''
14    req.body.rewind if req.body.respond_to?(:rewind)
15    rpc = JSON.parse(body, allow_nan: true) rescue {}
16
17    user                = env['HTTP_AUTHORIZATION'] || 'anonymus'
18    event_time          = Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")
19    client_ip           = req.ip
20    event_type_local    = rpc['method'] || ''
21    http_method         = req.request_method
22    user_agent          = env['HTTP_USER_AGENT'] || req.user_agent
23    resource_path       = req.fullpath
24    accept_lang         = env['HTTP_ACCEPT_LANGUAGE'] || req.accept_language
25    event_type          = 'page_view'
26
27    status, headers, response = @app.call(env)
28
29    form = URI.encode_www_form(
30      userName:           user,
31      eventTime:          event_time,
32      ipAddress:          client_ip,
33      httpCode:           status,
34      httpMethod:         http_method,
35      eventType:          event_type,
36      userAgent:          user_agent,
37      url:                resource_path,
38      browserLanguage:    accept_lang,
39    )
40
41    uri  = URI.parse('http://localhost/tirreno/sensor/')
42    http = Net::HTTP.new(uri.host, uri.port)
43    http.use_ssl = (uri.scheme == 'https')
44    headers = {
45      'Content-Type'  => 'application/x-www-form-urlencoded',
46      'Api-Key'       => 'XXXXXXXXXXXXXXXXXXXXXXXXX',
47    }
48    begin
49      http.post(uri.path, form, headers)
50      Rails.logger.info("Audit sent: #{resp.code} #{resp.body}")
51    rescue => e
52      Rails.logger.error("Audit failed: #{e.class.name} #{e.message}")
53    end
54
55    [status, headers, response]
56  end
57end

Add these two lines into openc3-cosmos-cmd-tlm-api/config.ru:

1require_relative 'lib/collect'
2
3use CollectMiddleware

So full updated file should look like:

 1# This file is used by Rack-based servers to start the application.
 2
 3require_relative 'config/environment'
 4require 'prometheus/middleware/collector'
 5require 'prometheus/middleware/exporter'
 6require_relative 'lib/collect'
 7
 8use CollectMiddleware
 9use Prometheus::Middleware::Collector
10use Prometheus::Middleware::Exporter, {:path => '/openc3-api/metrics'}
11
12run Rails.application
13Rails.application.load_server

Then navigate to openc3-cosmos-cmd-tlm-api directory (cd openc3-cosmos-cmd-tlm-api).

And run docker build -t tirreno-custom/openc3-cosmos-cmd-tlm-api:latest .

Final Steps

After building custom image of openc3-cosmos-cmd-tlm-api container we should start regular Docker OpenC3 COSMOS build.

Clone OpenC3/cosmos-project repository:

git clone https://github.com/OpenC3/cosmos-project

Navigate to cloned repository and edit compose.yaml file by replacing line

image: "${OPENC3_REGISTRY}/${OPENC3_NAMESPACE}/openc3-cosmos-cmd-tlm-api${OPENC3_IMAGE_SUFFIX}:${OPENC3_TAG}"

in openc3-cosmos-cmd-tlm-api section with

image: "tirreno-custom/openc3-cosmos-cmd-tlm-api:latest"

so docker compose will use custom image instead of one from OpenC3 registry.

That’s it! The only step left is starting OpenC3 build as in main installation instruction with ./openc3.sh run.