diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 92bd393..17e0a29 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,29 +1,30 @@ # This workflow will build a golang project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go -name: Go +name: ARCTIC Energy Testbed on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] jobs: - build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: "1.21.x" - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.20' - # FIXME: match version format (actions/setup-go/pull/410). + - name: Install dependencies + run: go get . - # - name: Build - # run: go build -v ./... + - name: Build + run: go build -o diode . - # - name: Test - # run: go test -v ./... + - name: Test with the Go CLI + run: go test diff --git a/sample/Pipfile b/Pipfile similarity index 74% rename from sample/Pipfile rename to Pipfile index 0757494..5ba0be6 100644 --- a/sample/Pipfile +++ b/Pipfile @@ -4,8 +4,9 @@ verify_ssl = true name = "pypi" [packages] +paho-mqtt = "*" [dev-packages] [requires] -python_version = "3.11" +python_version = "3.10" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..82146b7 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,28 @@ +{ + "_meta": { + "hash": { + "sha256": "e97fd5d39f2458bf405e2bf1e99f29c16241e0ac7bf8262a2cf8a13bcb95fe37" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.10" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "paho-mqtt": { + "hashes": [ + "sha256:2a8291c81623aec00372b5a85558a372c747cbca8e9934dfe218638b8eefc26f" + ], + "index": "pypi", + "version": "==1.6.1" + } + }, + "develop": {} +} diff --git a/README.md b/README.md index e106ad3..589037f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Data Diode Scripts for verifying TCP passthrough functionality. +## Development Environment + ## Architecture Diagram ## User Stories diff --git a/go.mod b/go.mod index f234e9c..b418696 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,20 @@ -module acep-uaf/cli/diode +module github.com/acep-uaf/data-diode -go 1.21.4 +go 1.21.6 require ( - github.com/urfave/cli/v2 v2.25.7 + github.com/eclipse/paho.mqtt.golang v1.4.3 + github.com/urfave/cli/v2 v2.27.1 rsc.io/quote v1.5.2 ) require ( github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sync v0.6.0 // indirect golang.org/x/text v0.14.0 // indirect rsc.io/sampler v1.99.99 // indirect ) diff --git a/go.sum b/go.sum index aec0826..d8e8481 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,19 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik= +github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= +github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= +github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/mqtt/republish.py b/mqtt/republish.py new file mode 100644 index 0000000..b1bcd9f --- /dev/null +++ b/mqtt/republish.py @@ -0,0 +1,49 @@ +# Data Diode TCP Stream Republisher + +import socket +import paho.mqtt.client as mqtt +import json +from datetime import datetime + +# Configuration Settings + +targetTcpServerIP = "192.168.1.20" +targetTcpServerPort = 503 + +mqttBrokerIP = "test.mosquitto.org" +mqttBrokerPort = 1883 +mqttTopic = "test/message" + +server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +server.bind((targetTcpServerIP, targetTcpServerPort)) +server.listen(1) + +mqtt_client = mqtt.Client() +mqtt_client.connect(mqttBrokerIP, mqttBrokerPort) + +while True: + print(">> Waiting for connection...") + connection, client = server.accept() + + try: + print(">> Connected to client IP: {}".format(client)) + + while True: + data = connection.recv(10240) + if not data: + break + + # print(f">> Received data: {data.decode()}") + + message = { + "timestamp": datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S"), + "topic": mqttTopic, + "data": data.decode() + } + + json_message = json.dumps(message) + + mqtt_client.publish(mqttTopic, json_message) + + finally: + connection.close() diff --git a/testbed.go b/testbed.go index a0328d0..9cf7c40 100644 --- a/testbed.go +++ b/testbed.go @@ -14,6 +14,7 @@ import ( "os" "time" + mqtt "github.com/eclipse/paho.mqtt.golang" "github.com/urfave/cli/v2" "rsc.io/quote" ) @@ -111,6 +112,61 @@ func sampleMetrics() { fmt.Println(">> UTC time: ", time.Now().UTC()) } +func demo() { + mqttBrokerIP := "test.mosquitto.org" + mqttBrokerPort := 1883 + mqttBrokerMessage := "Hello, world." + mqttBrokerTopic := "test/message" + + fmt.Println(">> MQTT") + fmt.Println(">> Broker: ", mqttBrokerIP) + fmt.Println(">> Port: ", mqttBrokerPort) + + // 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) { + fmt.Printf(">> Topic: %s\n", msg.Topic()) + fmt.Printf(">> Message: %s\n", msg.Payload()) + } + + mqtt.DEBUG = log.New(os.Stdout, "", 0) + mqtt.ERROR = log.New(os.Stdout, "", 0) + + // Initial Connection + opts := mqtt.NewClientOptions().AddBroker(fmt.Sprintf("tcp://%s:%d", mqttBrokerIP, mqttBrokerPort)) + opts.SetKeepAlive(2 * time.Second) + opts.SetDefaultPublishHandler(example) + opts.SetPingTimeout(1 * time.Second) + + // Create and start a client using the above ClientOptions + client := mqtt.NewClient(opts) + if token := client.Connect(); token.Wait() && token.Error() != nil { + panic(token.Error()) + } + + // Subscribe to a topic + if token := client.Subscribe(mqttBrokerTopic, 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.Wait() + + time.Sleep(6 * time.Second) + + // Disconnect from the broker + if token := client.Unsubscribe(mqttBrokerTopic); token.Wait() && token.Error() != nil { + fmt.Println(token.Error()) + os.Exit(1) + } + + client.Disconnect(250) + + time.Sleep(1 * time.Second) + +} + func main() { // Configuration Options @@ -172,12 +228,22 @@ func main() { return nil }, }, + { + Name: "mqtt", + Aliases: []string{"m"}, + Usage: "MQTT (republisher) demo", + Action: func(mCtx *cli.Context) error { + fmt.Println("----- MQTT -----") + demo() + return nil + }, + }, { Name: "version", Aliases: []string{"v"}, Usage: "Print the version of the diode CLI", Action: func(vCtx *cli.Context) error { - fmt.Println(">> diode version 0.0.2") + fmt.Println(">> diode version 0.0.3") return nil }, }, diff --git a/testbed_test.go b/testbed_test.go new file mode 100644 index 0000000..acc1b58 --- /dev/null +++ b/testbed_test.go @@ -0,0 +1,19 @@ +package main + +import ( + "testing" +) + +func TestNewClient(t *testing.T) { + name := "Input" + t.Run(name, func(t *testing.T) { + t.Parallel() + }) +} + +func TestNewServer(t *testing.T) { + name := "Output" + t.Run(name, func(t *testing.T) { + t.Parallel() + }) +}