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
Docker CLI
Installed Tirreno platform.
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
.