Skip to content

Commit

Permalink
Steady state for analysis & experimentation (#11)
Browse files Browse the repository at this point in the history
* feat: basic development environment via `Makefile` & sample benchmark

* feat: rendered Jupyter Notebook for logbook + creating diagrams

* docs: section headings for risk matrix, TTP, & design

* docs: add supporting footnote to NIST (CSRC)

* feat: sample ASCCI line graph & rolling hash base

* feat: external system configuration file

* refactor: diode config from YAML file

* fix: input, output, & broker connections

* docs: update directory tree structure names
  • Loading branch information
TechSolomon authored Feb 4, 2024
1 parent 4c9f505 commit 551e1cf
Show file tree
Hide file tree
Showing 12 changed files with 690 additions and 69 deletions.
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
build:
go build -o diode diode.go

test:
go test -v

run:
go run diode.go

2 changes: 2 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ name = "pypi"

[packages]
paho-mqtt = "*"
pyyaml = "*"

[dev-packages]
ipykernel = "*"

[requires]
python_version = "3.10"
421 changes: 419 additions & 2 deletions Pipfile.lock

Large diffs are not rendered by default.

68 changes: 66 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,74 @@
# Data Diode

Scripts for verifying TCP passthrough functionality.

## Development Environment
## Development Instructions

> [!NOTE]
> This project utilizes [`go`](https://go.dev/) for module management.
> You can find installation instructions for `1.21.6` via https://go.dev/doc/install.
- Clone repository: `gh repo clone acep-uaf/data-diode`
- Source navigation: `cd data-diode`
- Build binary: `make`
- CLI: `./diode [options...]`

#### Architecture Diagram

###### Energy Testbed

## Architecture Diagram
###### Device Configuration

#### Directory Structure

```zsh
.
├── benchmark
├── config.yaml
├── data
├── diode.go
├── diode_test.go
├── go.mod
├── go.sum
├── Makefile
├── mqtt
├── Pipfile
├── Pipfile.lock
├── README.md
└── sample

4 directories, 9 files
```

## User Stories

#### Scenario Planning

1. Power Plant Operator
1. Information Security Auditor
1. Energy Awareness Application Developer
1. Community Member

#### Threat Model[^1]

- [ ] Tactics
- [ ] Techniques
- [ ] Procedures

## System Benchmarking

#### Risk Matrix ([5x5](https://safetyculture.com/topics/risk-assessment/5x5-risk-matrix/))

| ↔ Probability <br> Impact ↕ | **Insignificant** | **Minor** | **Significant** | **Major** | **Severe** |
| --------------------------- | ----------------- | --------- | --------------- | --------- | ---------- |
| **Almost Certain** | R01 | R02 | R03 | R04 | R05 |
| **Likely** | R06 | R07 | R08 | R09 | R10 |
| **Moderate** | R11 | R12 | R13 | R14 | R15 |
| **Unlikely** | R16 | R17 | R18 | R19 | R20 |
| **Rare** | R21 | R22 | R23 | R24 | R25 |

#### Experimental Design

- [data/logbook.ipynb](data/logbook.ipynb)

[^1]: https://csrc.nist.gov/glossary/term/tactics_techniques_and_procedures
36 changes: 36 additions & 0 deletions benchmark/utility.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"crypto/sha256"
"fmt"

"github.com/guptarohit/asciigraph"
)

func checksum() {
// https://en.wikipedia.org/wiki/Rolling_hash

passthrough := []string{
"hello",
"world",
}

fmt.Println(">> Words: ", passthrough)

for _, word := range passthrough {
sum := sha256.Sum256([]byte(word))
fmt.Printf("%x\n", sum)
}
}

func example() {
definition := []float64{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144}
graph := asciigraph.Plot(definition)

fmt.Println(graph)
}

func main() {
checksum()
example()
}
11 changes: 11 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
input:
ip: "192.168.1.99"
port: 50000
output:
ip: "192.168.1.20"
port: 503
broker:
server: "test.mosquitto.org"
port: 1883
topic: "test/message"
message: "Hello, world."
50 changes: 50 additions & 0 deletions data/logbook.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 📶 Data Diode\n",
"#### Solomon Himelbloom (2024-01-24)"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n"
]
}
],
"source": [
"print(1)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "data-diode-iul9FwkF",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
92 changes: 58 additions & 34 deletions testbed.go → diode.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,30 @@ import (

mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/urfave/cli/v2"
"rsc.io/quote"
"gopkg.in/yaml.v2"
)

func newClient(diodeInputSideIP string, diodeTcpPassthroughPort int) {
type Configuration struct {
Input struct {
IP string
Port int
}
Output struct {
IP string
Port int
}
Broker struct {
Server string
Port int
Topic string
Message string
}
}

func newClient(ip string, port int) {
// Create a socket

client, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", diodeInputSideIP, diodeTcpPassthroughPort), time.Second)
client, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), time.Second)

if err != nil {
fmt.Println(">> Error establishing connection to the diode input side: ", err.Error())
Expand Down Expand Up @@ -51,18 +68,18 @@ func newClient(diodeInputSideIP string, diodeTcpPassthroughPort int) {
}
}

func newServer(targetTcpServerIP string, targetTcpServerPort int) {
func newServer(ip string, port int) {
// Begin listening for incoming connections

server, err := net.Listen("tcp", fmt.Sprintf("%s:%d", targetTcpServerIP, targetTcpServerPort))
server, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ip, port))

if err != nil {
fmt.Println(">> Error listening for incoming connections: ", err.Error())
return
}
defer server.Close()

fmt.Printf(">> Server listening on %s:%d\n", targetTcpServerIP, targetTcpServerPort)
fmt.Printf(">> Server listening on %s:%d\n", ip, port)

for {
// Wait for connection
Expand Down Expand Up @@ -112,15 +129,10 @@ func sampleMetrics() {
fmt.Println(">> UTC time: ", time.Now().UTC())
}

func demo() {
mqttBrokerIP := "test.mosquitto.org"
mqttBrokerPort := 1883
mqttBrokerMessage := "Hello, world."
mqttBrokerTopic := "test/message"

func demoRepublisher(server string, port int, topic string, message string) {
fmt.Println(">> MQTT")
fmt.Println(">> Broker: ", mqttBrokerIP)
fmt.Println(">> Port: ", mqttBrokerPort)
fmt.Println(">> Broker: ", server)
fmt.Println(">> Port: ", port)

// Source: https://github.com/eclipse/paho.mqtt.golang/blob/master/cmd/simple/main.go
var example mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
Expand All @@ -132,7 +144,7 @@ func demo() {
mqtt.ERROR = log.New(os.Stdout, "", 0)

// Initial Connection
opts := mqtt.NewClientOptions().AddBroker(fmt.Sprintf("tcp://%s:%d", mqttBrokerIP, mqttBrokerPort))
opts := mqtt.NewClientOptions().AddBroker(fmt.Sprintf("tcp://%s:%d", server, port))
opts.SetKeepAlive(2 * time.Second)
opts.SetDefaultPublishHandler(example)
opts.SetPingTimeout(1 * time.Second)
Expand All @@ -144,19 +156,19 @@ func demo() {
}

// Subscribe to a topic
if token := client.Subscribe(mqttBrokerTopic, 0, nil); token.Wait() && token.Error() != nil {
if token := client.Subscribe(topic, 0, nil); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
os.Exit(1)
}

// Publish to a topic
token := client.Publish(mqttBrokerTopic, 0, false, mqttBrokerMessage)
token := client.Publish(topic, 0, false, message)
token.Wait()

time.Sleep(6 * time.Second)

// Disconnect from the broker
if token := client.Unsubscribe(mqttBrokerTopic); token.Wait() && token.Error() != nil {
if token := client.Unsubscribe(topic); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
os.Exit(1)
}
Expand All @@ -168,20 +180,35 @@ func demo() {
}

func main() {
data, err := os.ReadFile("config.yaml")

if err != nil {
panic(err)
}

var config Configuration

if err := yaml.Unmarshal(data, &config); err != nil {
panic(err)
}

// Configuration Options
// Configuration Settings

diodeInputSideIP := "192.168.1.99"
diodeTcpPassthroughPort := 50000
diodeInputSideIP := config.Input.IP
diodeTCPPassthroughPort := config.Input.Port
targetTCPServerIP := config.Output.IP
targetTCPServerPort := config.Output.Port

targetTcpServerIP := "192.168.1.20"
targetTcpServerPort := 503
mqttBrokerIP := config.Broker.Server
mqttBrokerPort := config.Broker.Port
mqttBrokerMessage := config.Broker.Message
mqttBrokerTopic := config.Broker.Topic

app := &cli.App{
Name: "diode",
Usage: "A command line tool for interacting with data diodes.",
Usage: "Tool for interacting with data diode(s) via command-line interface (CLI).",
Action: func(cCtx *cli.Context) error {
fmt.Println(quote.Go())
fmt.Println("diode: try 'diode --help' for more information")
return nil
},
Commands: []*cli.Command{
Expand All @@ -191,7 +218,7 @@ func main() {
Usage: "Input side of the data diode",
Action: func(cCtx *cli.Context) error {
fmt.Println("----- INPUT -----")
newClient(diodeInputSideIP, diodeTcpPassthroughPort)
newClient(diodeInputSideIP, diodeTCPPassthroughPort)
return nil
},
},
Expand All @@ -201,7 +228,7 @@ func main() {
Usage: "Output side of the data diode",
Action: func(sCtx *cli.Context) error {
fmt.Println("----- OUTPUT -----")
newServer(targetTcpServerIP, targetTcpServerPort)
newServer(targetTCPServerIP, targetTCPServerPort)
return nil
},
},
Expand All @@ -211,10 +238,7 @@ func main() {
Usage: "Debug diagnostics via configuration settings",
Action: func(dCtx *cli.Context) error {
fmt.Println("----- DIAGNOSTICS -----")
input := fmt.Sprintf("%s:%d", diodeInputSideIP, diodeTcpPassthroughPort)
output := fmt.Sprintf("%s:%d", targetTcpServerIP, targetTcpServerPort)
fmt.Println(">> Client: ", input)
fmt.Println(">> Server: ", output)
fmt.Printf("%+v\n", config)
return nil
},
},
Expand All @@ -231,10 +255,10 @@ func main() {
{
Name: "mqtt",
Aliases: []string{"m"},
Usage: "MQTT (republisher) demo",
Usage: "MQTT (TCP stream) demo",
Action: func(mCtx *cli.Context) error {
fmt.Println("----- MQTT -----")
demo()
demoRepublisher(mqttBrokerIP, mqttBrokerPort, mqttBrokerTopic, mqttBrokerMessage)
return nil
},
},
Expand All @@ -243,7 +267,7 @@ func main() {
Aliases: []string{"v"},
Usage: "Print the version of the diode CLI",
Action: func(vCtx *cli.Context) error {
fmt.Println(">> diode version 0.0.3")
fmt.Println(">> diode version 0.0.4")
return nil
},
},
Expand Down
Loading

0 comments on commit 551e1cf

Please sign in to comment.