mirror of
https://github.com/mariocandela/beelzebub.git
synced 2025-07-01 18:47:26 -04:00
Compare commits
77 Commits
Author | SHA1 | Date | |
---|---|---|---|
f1b35e9e43 | |||
d74499bb37 | |||
5bba406e6b | |||
ec931bd6f9 | |||
b7f7aa0170 | |||
79f9162f24 | |||
24828e503b | |||
acd5aa0e9c | |||
480b734834 | |||
7556c76652 | |||
11421665db | |||
cce0dcfa30 | |||
4740b2b6f5 | |||
27eb88e050 | |||
9eaa503def | |||
f07ce7d3be | |||
a924b2cb8b | |||
8e81a8721e | |||
f40ce9215e | |||
0fc9ebb05e | |||
0b5486964b | |||
fa472effb9 | |||
628e20e01f | |||
0d4aa5f52e | |||
58f27ab076 | |||
c4132f2d75 | |||
f4ec6dcefb | |||
a1e96738fb | |||
cd284877cf | |||
2088163b54 | |||
0af1a05ae9 | |||
5086f5ba08 | |||
e1f82db2d0 | |||
59f40a166b | |||
93d7804ba3 | |||
24b4153e77 | |||
1d90c83678 | |||
67829655f4 | |||
9ad21e138b | |||
8ab11e6ac2 | |||
965942609d | |||
b8d77983ee | |||
b332f85230 | |||
b1de020de8 | |||
05b49051db | |||
3555ea9d3b | |||
d4fe0f96bd | |||
6e26f76c51 | |||
b2a7a527ff | |||
1c650882b6 | |||
ada0a9b8f0 | |||
ccd160f7b0 | |||
67d9ea7168 | |||
b441f8f9ab | |||
0b9aa8b965 | |||
c7bd863b36 | |||
e2d1cc6087 | |||
5bb7a96b39 | |||
ab7cefdb18 | |||
34159ca06f | |||
fd2bbe6be6 | |||
9076dfa47c | |||
2b90c4a1f5 | |||
90004e7d84 | |||
9e042e33f5 | |||
5e5d0494a9 | |||
d77aa0c8a0 | |||
3733c902b9 | |||
07ffdd839f | |||
1f48f4dff5 | |||
88a96a7efd | |||
34a80b06f3 | |||
a9584f0ac6 | |||
fd57d6ed92 | |||
7badd5b655 | |||
9b4b399df0 | |||
abb956ec7a |
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@ -51,6 +51,11 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Upload coverage reports to Codecov
|
||||||
|
uses: codecov/codecov-action@v3
|
||||||
|
env:
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
- name: Start integration test dependencies
|
- name: Start integration test dependencies
|
||||||
run: |
|
run: |
|
||||||
make test.dependencies.start
|
make test.dependencies.start
|
||||||
|
@ -2,8 +2,7 @@ FROM golang:alpine AS builder
|
|||||||
|
|
||||||
ENV GO111MODULE=on \
|
ENV GO111MODULE=on \
|
||||||
CGO_ENABLED=0 \
|
CGO_ENABLED=0 \
|
||||||
GOOS=linux \
|
GOOS=linux
|
||||||
GOARCH=amd64
|
|
||||||
|
|
||||||
RUN apk add git
|
RUN apk add git
|
||||||
|
|
||||||
|
48
README.md
48
README.md
@ -1,16 +1,21 @@
|
|||||||
# Beelzebub
|
# Beelzebub
|
||||||
|
|
||||||
[](https://github.com/mariocandela/beelzebub/actions/workflows/ci.yml) [](https://github.com/mariocandela/beelzebub/actions/workflows/docker-image.yml) [](https://github.com/mariocandela/beelzebub/actions/workflows/codeql.yml)
|
[](https://github.com/mariocandela/beelzebub/actions/workflows/ci.yml) [](https://github.com/mariocandela/beelzebub/actions/workflows/docker-image.yml) [](https://github.com/mariocandela/beelzebub/actions/workflows/codeql.yml)
|
||||||
|
[](https://goreportcard.com/report/github.com/mariocandela/beelzebub/v3)
|
||||||
|
[](https://codecov.io/gh/mariocandela/beelzebub)
|
||||||
|
[](https://pkg.go.dev/github.com/mariocandela/beelzebub/v3)
|
||||||
|
[](https://github.com/avelino/awesome-go)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Beelzebub is an advanced honeypot framework designed to provide a highly secure environment for detecting and analyzing cyber attacks. It offers a low code approach for easy implementation and utilizes virtualization techniques powered by OpenAI Generative Pre-trained Transformer.
|
Beelzebub is an advanced honeypot framework designed to provide a highly secure environment for detecting and analyzing cyber attacks. It offers a low code approach for easy implementation and uses AI to mimic the behavior of a high-interaction honeypot.
|
||||||
|
|
||||||
<img src="https://beelzebub.netlify.app/go-beelzebub.png" alt="Beelzebub Logo" width="200"/>
|
<img src="https://beelzebub.netlify.app/go-beelzebub.png" alt="Beelzebub Logo" width="200"/>
|
||||||
|
|
||||||
## OpenAI GPT Integration
|
## LLM Honeypot
|
||||||
|
|
||||||
|
[](https://asciinema.org/a/665295)
|
||||||
|
|
||||||
Learn how to integrate Beelzebub with OpenAI GPT-3 by referring to our comprehensive guide on Medium: [Medium Article](https://medium.com/@mario.candela.personal/how-to-build-a-highly-effective-honeypot-with-beelzebub-and-chatgpt-a2f0f05b3e1)
|
|
||||||
|
|
||||||
## Telegram Bot for Real-Time Attacks
|
## Telegram Bot for Real-Time Attacks
|
||||||
|
|
||||||
@ -90,14 +95,17 @@ $ make test.unit
|
|||||||
To run integration tests:
|
To run integration tests:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
$ make test.dependencies.start
|
||||||
$ make test.integration
|
$ make test.integration
|
||||||
|
$ make test.dependencies.down
|
||||||
```
|
```
|
||||||
|
|
||||||
## Key Features
|
## Key Features
|
||||||
|
|
||||||
Beelzebub offers a wide range of features to enhance your honeypot environment:
|
Beelzebub offers a wide range of features to enhance your honeypot environment:
|
||||||
|
|
||||||
- OpenAI Generative Pre-trained Transformer act as Linux virtualization
|
- Support for Ollama
|
||||||
|
- Support for OpenAI
|
||||||
- SSH Honeypot
|
- SSH Honeypot
|
||||||
- HTTP Honeypot
|
- HTTP Honeypot
|
||||||
- TCP Honeypot
|
- TCP Honeypot
|
||||||
@ -203,22 +211,44 @@ commands:
|
|||||||
|
|
||||||
#### Example SSH Honeypot
|
#### Example SSH Honeypot
|
||||||
|
|
||||||
###### Honeypot with GPT-3 on Port 2222
|
###### Honeypot LLM Honeypots
|
||||||
|
|
||||||
|
Example with OpenAI GPT-4:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: "v1"
|
apiVersion: "v1"
|
||||||
protocol: "ssh"
|
protocol: "ssh"
|
||||||
address: ":2222"
|
address: ":2222"
|
||||||
description: "SSH interactive ChatGPT"
|
description: "SSH interactive OpenAI GPT-4"
|
||||||
commands:
|
commands:
|
||||||
- regex: "^(.+)$"
|
- regex: "^(.+)$"
|
||||||
plugin: "OpenAIGPTLinuxTerminal"
|
plugin: "LLMHoneypot"
|
||||||
serverVersion: "OpenSSH"
|
serverVersion: "OpenSSH"
|
||||||
serverName: "ubuntu"
|
serverName: "ubuntu"
|
||||||
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
|
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
|
||||||
deadlineTimeoutSeconds: 60
|
deadlineTimeoutSeconds: 60
|
||||||
plugin:
|
plugin:
|
||||||
openAPIChatGPTSecretKey: "Your OpenAI Secret Key"
|
llmModel: "gpt4-o"
|
||||||
|
openAISecretKey: "sk-proj-123456"
|
||||||
|
```
|
||||||
|
|
||||||
|
Example with Ollama Llama3:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: "v1"
|
||||||
|
protocol: "ssh"
|
||||||
|
address: ":2222"
|
||||||
|
description: "SSH Ollama Llama3"
|
||||||
|
commands:
|
||||||
|
- regex: "^(.+)$"
|
||||||
|
plugin: "LLMHoneypot"
|
||||||
|
serverVersion: "OpenSSH"
|
||||||
|
serverName: "ubuntu"
|
||||||
|
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
|
||||||
|
deadlineTimeoutSeconds: 60
|
||||||
|
plugin:
|
||||||
|
llmModel: "llama3"
|
||||||
|
host: "http://example.com/api/chat" #default http://localhost:11434/api/chat
|
||||||
```
|
```
|
||||||
|
|
||||||
###### SSH Honeypot on Port 22
|
###### SSH Honeypot on Port 22
|
||||||
@ -255,8 +285,6 @@ passwordRegex: "^(root|qwerty|Smoker666)$"
|
|||||||
deadlineTimeoutSeconds: 60
|
deadlineTimeoutSeconds: 60
|
||||||
```
|
```
|
||||||
|
|
||||||
[](https://asciinema.org/a/604522)
|
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
Our future plans for Beelzebub include developing it into a robust PaaS platform.
|
Our future plans for Beelzebub include developing it into a robust PaaS platform.
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"beelzebub/parser"
|
|
||||||
"beelzebub/protocols"
|
|
||||||
"beelzebub/protocols/strategies"
|
|
||||||
"beelzebub/tracer"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/plugins"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/protocols"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/protocols/strategies"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -112,6 +113,21 @@ Honeypot Framework, happy hacking!`)
|
|||||||
// Init Tracer strategies, and set the trace strategy default HTTP
|
// Init Tracer strategies, and set the trace strategy default HTTP
|
||||||
protocolManager := protocols.InitProtocolManager(b.traceStrategy, hypertextTransferProtocolStrategy)
|
protocolManager := protocols.InitProtocolManager(b.traceStrategy, hypertextTransferProtocolStrategy)
|
||||||
|
|
||||||
|
if b.beelzebubCoreConfigurations.Core.BeelzebubCloud.Enabled {
|
||||||
|
conf := b.beelzebubCoreConfigurations.Core.BeelzebubCloud
|
||||||
|
|
||||||
|
beelzebubCloud := plugins.InitBeelzebubCloud(conf.URI, conf.AuthToken)
|
||||||
|
|
||||||
|
if honeypotsConfiguration, err := beelzebubCloud.GetHoneypotsConfigurations(); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
if len(honeypotsConfiguration) == 0 {
|
||||||
|
return errors.New("No honeypots configuration found")
|
||||||
|
}
|
||||||
|
b.beelzebubServicesConfiguration = honeypotsConfiguration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, beelzebubServiceConfiguration := range b.beelzebubServicesConfiguration {
|
for _, beelzebubServiceConfiguration := range b.beelzebubServicesConfiguration {
|
||||||
switch beelzebubServiceConfiguration.Protocol {
|
switch beelzebubServiceConfiguration.Protocol {
|
||||||
case "http":
|
case "http":
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"beelzebub/parser"
|
|
||||||
"beelzebub/tracer"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/plugins"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
|
|
||||||
amqp "github.com/rabbitmq/amqp091-go"
|
amqp "github.com/rabbitmq/amqp091-go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -37,6 +38,10 @@ func (d *Director) BuildBeelzebub(beelzebubCoreConfigurations *parser.BeelzebubC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if beelzebubCoreConfigurations.Core.BeelzebubCloud.Enabled {
|
||||||
|
d.builder.setTraceStrategy(d.beelzebubCloudStrategy)
|
||||||
|
}
|
||||||
|
|
||||||
return d.builder.build(), nil
|
return d.builder.build(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,6 +52,27 @@ func (d *Director) standardOutStrategy(event tracer.Event) {
|
|||||||
}).Info("New Event")
|
}).Info("New Event")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Director) beelzebubCloudStrategy(event tracer.Event) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"status": event.Status,
|
||||||
|
"event": event,
|
||||||
|
}).Info("New Event")
|
||||||
|
|
||||||
|
conf := d.builder.beelzebubCoreConfigurations.Core.BeelzebubCloud
|
||||||
|
|
||||||
|
beelzebubCloud := plugins.InitBeelzebubCloud(conf.URI, conf.AuthToken)
|
||||||
|
|
||||||
|
result, err := beelzebubCloud.SendEvent(event)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
} else {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"status": result,
|
||||||
|
"event": event,
|
||||||
|
}).Debug("Event published on beelzebub cloud")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Director) rabbitMQTraceStrategy(event tracer.Event) {
|
func (d *Director) rabbitMQTraceStrategy(event tracer.Event) {
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"status": event.Status,
|
"status": event.Status,
|
||||||
|
@ -11,4 +11,7 @@ core:
|
|||||||
prometheus:
|
prometheus:
|
||||||
path: "/metrics"
|
path: "/metrics"
|
||||||
port: ":2112"
|
port: ":2112"
|
||||||
|
beelzebub-cloud:
|
||||||
|
enabled: false
|
||||||
|
uri: ""
|
||||||
|
auth-token: ""
|
||||||
|
@ -18,41 +18,9 @@ commands:
|
|||||||
- "Server: Apache/2.4.53 (Debian)"
|
- "Server: Apache/2.4.53 (Debian)"
|
||||||
- "X-Powered-By: PHP/7.4.29"
|
- "X-Powered-By: PHP/7.4.29"
|
||||||
statusCode: 200
|
statusCode: 200
|
||||||
- regex: "^(/wp-login.php|/wp-admin)$"
|
|
||||||
handler:
|
|
||||||
<html>
|
|
||||||
<header>
|
|
||||||
<title>Wordpress 6 test page</title>
|
|
||||||
</header>
|
|
||||||
<body>
|
|
||||||
<form action="" method="post">
|
|
||||||
<label for="uname"><b>Username</b></label>
|
|
||||||
<input type="text" placeholder="Enter Username" name="uname" required>
|
|
||||||
|
|
||||||
<label for="psw"><b>Password</b></label>
|
|
||||||
<input type="password" placeholder="Enter Password" name="psw" required>
|
|
||||||
|
|
||||||
<button type="submit">Login</button>
|
|
||||||
</form>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
headers:
|
|
||||||
- "Content-Type: text/html"
|
|
||||||
- "Server: Apache/2.4.53 (Debian)"
|
|
||||||
- "X-Powered-By: PHP/7.4.29"
|
|
||||||
statusCode: 200
|
|
||||||
- regex: "^.*$"
|
- regex: "^.*$"
|
||||||
handler:
|
plugin: "LLMHoneypot"
|
||||||
<html>
|
statusCode: 200
|
||||||
<header>
|
plugin:
|
||||||
<title>404</title>
|
llmModel: "gpt4-o"
|
||||||
</header>
|
openAISecretKey: "sk-proj-123456"
|
||||||
<body>
|
|
||||||
<h1>Not found!</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
headers:
|
|
||||||
- "Content-Type: text/html"
|
|
||||||
- "Server: Apache/2.4.53 (Debian)"
|
|
||||||
- "X-Powered-By: PHP/7.4.29"
|
|
||||||
statusCode: 404
|
|
@ -4,10 +4,10 @@ address: ":2222"
|
|||||||
description: "SSH interactive ChatGPT"
|
description: "SSH interactive ChatGPT"
|
||||||
commands:
|
commands:
|
||||||
- regex: "^(.+)$"
|
- regex: "^(.+)$"
|
||||||
plugin: "OpenAIGPTLinuxTerminal"
|
plugin: "LLMHoneypot"
|
||||||
serverVersion: "OpenSSH"
|
serverVersion: "OpenSSH"
|
||||||
serverName: "ubuntu"
|
serverName: "ubuntu"
|
||||||
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
|
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
|
||||||
deadlineTimeoutSeconds: 60
|
deadlineTimeoutSeconds: 6000
|
||||||
plugin:
|
plugin:
|
||||||
openAPIChatGPTSecretKey: ""
|
llmModel: "llama3"
|
@ -10,6 +10,7 @@ services:
|
|||||||
- "22:22"
|
- "22:22"
|
||||||
- "2222:2222"
|
- "2222:2222"
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
|
- "8081:8081"
|
||||||
- "80:80"
|
- "80:80"
|
||||||
- "3306:3306"
|
- "3306:3306"
|
||||||
- "2112:2112" # Prometheus openmetrics
|
- "2112:2112" # Prometheus openmetrics
|
||||||
|
37
go.mod
37
go.mod
@ -1,37 +1,38 @@
|
|||||||
module beelzebub
|
module github.com/mariocandela/beelzebub/v3
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gliderlabs/ssh v0.3.5
|
github.com/gliderlabs/ssh v0.3.8
|
||||||
github.com/go-resty/resty/v2 v2.9.1
|
github.com/go-resty/resty/v2 v2.16.3
|
||||||
github.com/google/uuid v1.3.1
|
github.com/google/uuid v1.6.0
|
||||||
github.com/jarcoal/httpmock v1.3.1
|
github.com/jarcoal/httpmock v1.3.1
|
||||||
github.com/melbahja/goph v1.4.0
|
github.com/melbahja/goph v1.4.0
|
||||||
github.com/prometheus/client_golang v1.17.0
|
github.com/prometheus/client_golang v1.20.5
|
||||||
github.com/rabbitmq/amqp091-go v1.8.1
|
github.com/rabbitmq/amqp091-go v1.10.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.10.0
|
||||||
golang.org/x/crypto v0.13.0
|
golang.org/x/crypto v0.32.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/kr/fs v0.1.0 // indirect
|
github.com/kr/fs v0.1.0 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pkg/sftp v1.13.5 // indirect
|
github.com/pkg/sftp v1.13.5 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.44.0 // indirect
|
github.com/prometheus/common v0.55.0 // indirect
|
||||||
github.com/prometheus/procfs v0.11.1 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
golang.org/x/net v0.15.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/sys v0.12.0 // indirect
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
golang.org/x/term v0.12.0 // indirect
|
golang.org/x/term v0.28.0 // indirect
|
||||||
google.golang.org/protobuf v1.31.0 // indirect
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
)
|
)
|
||||||
|
108
go.sum
108
go.sum
@ -2,88 +2,75 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
|
|||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||||
github.com/go-resty/resty/v2 v2.9.1 h1:PIgGx4VrHvag0juCJ4dDv3MiFRlDmP0vicBucwf+gLM=
|
github.com/go-resty/resty/v2 v2.16.3 h1:zacNT7lt4b8M/io2Ahj6yPypL7bqx9n1iprfQuodV+E=
|
||||||
github.com/go-resty/resty/v2 v2.9.1/go.mod h1:4/GYJVjh9nhkhGR6AUNW3XhpDYNUr+Uvy9gV/VGZIy4=
|
github.com/go-resty/resty/v2 v2.16.3/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
|
||||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
|
||||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
|
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
|
||||||
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||||
|
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||||
|
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
|
||||||
github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
|
github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
|
||||||
github.com/melbahja/goph v1.4.0 h1:z0PgDbBFe66lRYl3v5dGb9aFgPy0kotuQ37QOwSQFqs=
|
github.com/melbahja/goph v1.4.0 h1:z0PgDbBFe66lRYl3v5dGb9aFgPy0kotuQ37QOwSQFqs=
|
||||||
github.com/melbahja/goph v1.4.0/go.mod h1:uG+VfK2Dlhk+O32zFrRlc3kYKTlV6+BtvPWd/kK7U68=
|
github.com/melbahja/goph v1.4.0/go.mod h1:uG+VfK2Dlhk+O32zFrRlc3kYKTlV6+BtvPWd/kK7U68=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
|
github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
|
||||||
github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
|
github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
|
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||||
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
|
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||||
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/rabbitmq/amqp091-go v1.8.1 h1:RejT1SBUim5doqcL6s7iN6SBmsQqyTgXb1xMlH0h1hA=
|
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||||
github.com/rabbitmq/amqp091-go v1.8.1/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
|
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -92,40 +79,27 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||||
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
|
|
||||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
|
||||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"beelzebub/builder"
|
|
||||||
"beelzebub/parser"
|
|
||||||
"beelzebub/tracer"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/builder"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
4
main.go
4
main.go
@ -1,9 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"beelzebub/builder"
|
|
||||||
"beelzebub/parser"
|
|
||||||
"flag"
|
"flag"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/builder"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
// Package parser is responsible for parsing the configurations of the core and honeypot service
|
||||||
package parser
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@ -11,14 +11,17 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BeelzebubCoreConfigurations is the struct that contains the configurations of the core
|
||||||
type BeelzebubCoreConfigurations struct {
|
type BeelzebubCoreConfigurations struct {
|
||||||
Core struct {
|
Core struct {
|
||||||
Logging Logging `yaml:"logging"`
|
Logging Logging `yaml:"logging"`
|
||||||
Tracings Tracings `yaml:"tracings"`
|
Tracings Tracings `yaml:"tracings"`
|
||||||
Prometheus Prometheus `yaml:"prometheus"`
|
Prometheus Prometheus `yaml:"prometheus"`
|
||||||
|
BeelzebubCloud BeelzebubCloud `yaml:"beelzebub-cloud"`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logging is the struct that contains the configurations of the logging
|
||||||
type Logging struct {
|
type Logging struct {
|
||||||
Debug bool `yaml:"debug"`
|
Debug bool `yaml:"debug"`
|
||||||
DebugReportCaller bool `yaml:"debugReportCaller"`
|
DebugReportCaller bool `yaml:"debugReportCaller"`
|
||||||
@ -26,10 +29,16 @@ type Logging struct {
|
|||||||
LogsPath string `yaml:"logsPath,omitempty"`
|
LogsPath string `yaml:"logsPath,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tracings is the struct that contains the configurations of the tracings
|
||||||
type Tracings struct {
|
type Tracings struct {
|
||||||
RabbitMQ `yaml:"rabbit-mq"`
|
RabbitMQ `yaml:"rabbit-mq"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BeelzebubCloud struct {
|
||||||
|
Enabled bool `yaml:"enabled"`
|
||||||
|
URI string `yaml:"uri"`
|
||||||
|
AuthToken string `yaml:"auth-token"`
|
||||||
|
}
|
||||||
type RabbitMQ struct {
|
type RabbitMQ struct {
|
||||||
Enabled bool `yaml:"enabled"`
|
Enabled bool `yaml:"enabled"`
|
||||||
URI string `yaml:"uri"`
|
URI string `yaml:"uri"`
|
||||||
@ -40,9 +49,12 @@ type Prometheus struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Plugin struct {
|
type Plugin struct {
|
||||||
OpenAPIChatGPTSecretKey string `yaml:"openAPIChatGPTSecretKey"`
|
OpenAISecretKey string `yaml:"openAISecretKey"`
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
LLMModel string `yaml:"llmModel"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeelzebubServiceConfiguration is the struct that contains the configurations of the honeypot service
|
||||||
type BeelzebubServiceConfiguration struct {
|
type BeelzebubServiceConfiguration struct {
|
||||||
ApiVersion string `yaml:"apiVersion"`
|
ApiVersion string `yaml:"apiVersion"`
|
||||||
Protocol string `yaml:"protocol"`
|
Protocol string `yaml:"protocol"`
|
||||||
@ -57,6 +69,7 @@ type BeelzebubServiceConfiguration struct {
|
|||||||
Plugin Plugin `yaml:"plugin"`
|
Plugin Plugin `yaml:"plugin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command is the struct that contains the configurations of the commands
|
||||||
type Command struct {
|
type Command struct {
|
||||||
Regex string `yaml:"regex"`
|
Regex string `yaml:"regex"`
|
||||||
Handler string `yaml:"handler"`
|
Handler string `yaml:"handler"`
|
||||||
@ -86,6 +99,7 @@ func Init(configurationsCorePath, configurationsServicesDirectory string) *confi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadConfigurationsCore is the method that reads the configurations of the core from files
|
||||||
func (bp configurationsParser) ReadConfigurationsCore() (*BeelzebubCoreConfigurations, error) {
|
func (bp configurationsParser) ReadConfigurationsCore() (*BeelzebubCoreConfigurations, error) {
|
||||||
buf, err := bp.readFileBytesByFilePathDependency(bp.configurationsCorePath)
|
buf, err := bp.readFileBytesByFilePathDependency(bp.configurationsCorePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -101,6 +115,7 @@ func (bp configurationsParser) ReadConfigurationsCore() (*BeelzebubCoreConfigura
|
|||||||
return beelzebubConfiguration, nil
|
return beelzebubConfiguration, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadConfigurationsServices is the method that reads the configurations of the honeypot services from files
|
||||||
func (bp configurationsParser) ReadConfigurationsServices() ([]BeelzebubServiceConfiguration, error) {
|
func (bp configurationsParser) ReadConfigurationsServices() ([]BeelzebubServiceConfiguration, error) {
|
||||||
services, err := bp.gelAllFilesNameByDirNameDependency(bp.configurationsServicesDirectory)
|
services, err := bp.gelAllFilesNameByDirNameDependency(bp.configurationsServicesDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -127,7 +142,7 @@ func (bp configurationsParser) ReadConfigurationsServices() ([]BeelzebubServiceC
|
|||||||
}
|
}
|
||||||
|
|
||||||
func gelAllFilesNameByDirName(dirName string) ([]string, error) {
|
func gelAllFilesNameByDirName(dirName string) ([]string, error) {
|
||||||
files, err := ioutil.ReadDir(dirName)
|
files, err := os.ReadDir(dirName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package parser
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -18,7 +19,11 @@ core:
|
|||||||
tracings:
|
tracings:
|
||||||
rabbit-mq:
|
rabbit-mq:
|
||||||
enabled: true
|
enabled: true
|
||||||
uri: "amqp://user:password@localhost/"`)
|
uri: "amqp://user:password@localhost/"
|
||||||
|
beelzebub-cloud:
|
||||||
|
enabled: true
|
||||||
|
uri: "amqp://user:password@localhost/"
|
||||||
|
auth-token: "iejfdjsl-aosdajosoidaj-dunfkjnfkjsdnkn"`)
|
||||||
return configurationsCoreBytes, nil
|
return configurationsCoreBytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +53,12 @@ commands:
|
|||||||
- regex: "wp-admin"
|
- regex: "wp-admin"
|
||||||
handler: "login"
|
handler: "login"
|
||||||
headers:
|
headers:
|
||||||
- "Content-Type: text/html"`)
|
- "Content-Type: text/html"
|
||||||
|
plugin:
|
||||||
|
openAISecretKey: "qwerty"
|
||||||
|
llmModel: "llama3"
|
||||||
|
host: "localhost:1563"
|
||||||
|
`)
|
||||||
return beelzebubServiceConfiguration, nil
|
return beelzebubServiceConfiguration, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +94,9 @@ func TestReadConfigurationsCoreValid(t *testing.T) {
|
|||||||
assert.Equal(t, coreConfigurations.Core.Logging.LogsPath, "./logs")
|
assert.Equal(t, coreConfigurations.Core.Logging.LogsPath, "./logs")
|
||||||
assert.Equal(t, coreConfigurations.Core.Tracings.RabbitMQ.Enabled, true)
|
assert.Equal(t, coreConfigurations.Core.Tracings.RabbitMQ.Enabled, true)
|
||||||
assert.Equal(t, coreConfigurations.Core.Tracings.RabbitMQ.URI, "amqp://user:password@localhost/")
|
assert.Equal(t, coreConfigurations.Core.Tracings.RabbitMQ.URI, "amqp://user:password@localhost/")
|
||||||
|
assert.Equal(t, coreConfigurations.Core.BeelzebubCloud.Enabled, true)
|
||||||
|
assert.Equal(t, coreConfigurations.Core.BeelzebubCloud.URI, "amqp://user:password@localhost/")
|
||||||
|
assert.Equal(t, coreConfigurations.Core.BeelzebubCloud.AuthToken, "iejfdjsl-aosdajosoidaj-dunfkjnfkjsdnkn")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadConfigurationsServicesFail(t *testing.T) {
|
func TestReadConfigurationsServicesFail(t *testing.T) {
|
||||||
@ -104,10 +117,10 @@ func TestReadConfigurationsServicesValid(t *testing.T) {
|
|||||||
configurationsParser.gelAllFilesNameByDirNameDependency = mockReadDirValid
|
configurationsParser.gelAllFilesNameByDirNameDependency = mockReadDirValid
|
||||||
|
|
||||||
beelzebubServicesConfiguration, err := configurationsParser.ReadConfigurationsServices()
|
beelzebubServicesConfiguration, err := configurationsParser.ReadConfigurationsServices()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
firstBeelzebubServiceConfiguration := beelzebubServicesConfiguration[0]
|
firstBeelzebubServiceConfiguration := beelzebubServicesConfiguration[0]
|
||||||
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, firstBeelzebubServiceConfiguration.Protocol, "http")
|
assert.Equal(t, firstBeelzebubServiceConfiguration.Protocol, "http")
|
||||||
assert.Equal(t, firstBeelzebubServiceConfiguration.ApiVersion, "v1")
|
assert.Equal(t, firstBeelzebubServiceConfiguration.ApiVersion, "v1")
|
||||||
assert.Equal(t, firstBeelzebubServiceConfiguration.Address, ":8080")
|
assert.Equal(t, firstBeelzebubServiceConfiguration.Address, ":8080")
|
||||||
@ -117,4 +130,58 @@ func TestReadConfigurationsServicesValid(t *testing.T) {
|
|||||||
assert.Equal(t, firstBeelzebubServiceConfiguration.Commands[0].Handler, "login")
|
assert.Equal(t, firstBeelzebubServiceConfiguration.Commands[0].Handler, "login")
|
||||||
assert.Equal(t, len(firstBeelzebubServiceConfiguration.Commands[0].Headers), 1)
|
assert.Equal(t, len(firstBeelzebubServiceConfiguration.Commands[0].Headers), 1)
|
||||||
assert.Equal(t, firstBeelzebubServiceConfiguration.Commands[0].Headers[0], "Content-Type: text/html")
|
assert.Equal(t, firstBeelzebubServiceConfiguration.Commands[0].Headers[0], "Content-Type: text/html")
|
||||||
|
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.OpenAISecretKey, "qwerty")
|
||||||
|
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.LLMModel, "llama3")
|
||||||
|
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.Host, "localhost:1563")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGelAllFilesNameByDirName(t *testing.T) {
|
||||||
|
|
||||||
|
var dir = t.TempDir()
|
||||||
|
|
||||||
|
files, err := gelAllFilesNameByDirName(dir)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 0, len(files))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGelAllFilesNameByDirNameFiles(t *testing.T) {
|
||||||
|
|
||||||
|
var dir = t.TempDir()
|
||||||
|
|
||||||
|
testFiles := []string{"file1.yaml", "file2.yaml", "file3.txt", "subdir", "file4.yaml"}
|
||||||
|
for _, filename := range testFiles {
|
||||||
|
filePath := dir + "/" + filename
|
||||||
|
file, err := os.Create(filePath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := gelAllFilesNameByDirName(dir)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 3, len(files))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGelAllFilesNameByDirNameError(t *testing.T) {
|
||||||
|
|
||||||
|
files, err := gelAllFilesNameByDirName("nosuchfile")
|
||||||
|
|
||||||
|
assert.Nil(t, files)
|
||||||
|
assert.Equal(t, "open nosuchfile: no such file or directory", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadFileBytesByFilePath(t *testing.T) {
|
||||||
|
|
||||||
|
var dir = t.TempDir()
|
||||||
|
filePath := dir + "/test.yaml"
|
||||||
|
|
||||||
|
f, err := os.Create(filePath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
bytes, err := readFileBytesByFilePath(filePath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "", string(bytes))
|
||||||
}
|
}
|
||||||
|
100
plugins/beelzebub-cloud.go
Normal file
100
plugins/beelzebub-cloud.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type beelzebubCloud struct {
|
||||||
|
URI string
|
||||||
|
AuthToken string
|
||||||
|
client *resty.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type HoneypotConfigResponseDTO struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Config string `json:"config"`
|
||||||
|
TokenID string `json:"tokenId"`
|
||||||
|
LastUpdatedOn string `json:"lastUpdatedOn"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitBeelzebubCloud(uri, authToken string) *beelzebubCloud {
|
||||||
|
return &beelzebubCloud{
|
||||||
|
URI: uri,
|
||||||
|
AuthToken: authToken,
|
||||||
|
client: resty.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (beelzebubCloud *beelzebubCloud) SendEvent(event tracer.Event) (bool, error) {
|
||||||
|
requestJson, err := json.Marshal(event)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if beelzebubCloud.AuthToken == "" {
|
||||||
|
return false, errors.New("authToken is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := beelzebubCloud.client.R().
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetBody(requestJson).
|
||||||
|
SetHeader("Authorization", beelzebubCloud.AuthToken).
|
||||||
|
SetResult(&tracer.Event{}).
|
||||||
|
Post(fmt.Sprintf("%s/events", beelzebubCloud.URI))
|
||||||
|
|
||||||
|
log.Debug(response)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.StatusCode() == 200, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (beelzebubCloud *beelzebubCloud) GetHoneypotsConfigurations() ([]parser.BeelzebubServiceConfiguration, error) {
|
||||||
|
if beelzebubCloud.AuthToken == "" {
|
||||||
|
return nil, errors.New("authToken is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := beelzebubCloud.client.R().
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetHeader("Authorization", beelzebubCloud.AuthToken).
|
||||||
|
SetResult([]HoneypotConfigResponseDTO{}).
|
||||||
|
Get(fmt.Sprintf("%s/honeypots", beelzebubCloud.URI))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.StatusCode() != 200 {
|
||||||
|
return nil, errors.New(fmt.Sprintf("Response code: %v, error: %s", response.StatusCode(), string(response.Body())))
|
||||||
|
}
|
||||||
|
|
||||||
|
var honeypotsConfig []HoneypotConfigResponseDTO
|
||||||
|
|
||||||
|
if err = json.Unmarshal(response.Body(), &honeypotsConfig); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var servicesConfiguration = make([]parser.BeelzebubServiceConfiguration, 0)
|
||||||
|
|
||||||
|
for _, honeypotConfig := range honeypotsConfig {
|
||||||
|
var honeypotsConfig parser.BeelzebubServiceConfiguration
|
||||||
|
|
||||||
|
if err = yaml.Unmarshal([]byte(honeypotConfig.Config), &honeypotsConfig); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
servicesConfiguration = append(servicesConfiguration, honeypotsConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug(servicesConfiguration)
|
||||||
|
|
||||||
|
return servicesConfiguration, nil
|
||||||
|
}
|
230
plugins/beelzebub-cloud_test.go
Normal file
230
plugins/beelzebub-cloud_test.go
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/jarcoal/httpmock"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuildSendEventFailValidation(t *testing.T) {
|
||||||
|
beelzebubCloud := InitBeelzebubCloud("", "")
|
||||||
|
|
||||||
|
_, err := beelzebubCloud.SendEvent(tracer.Event{})
|
||||||
|
|
||||||
|
assert.Equal(t, "authToken is empty", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildSendEventWithResults(t *testing.T) {
|
||||||
|
client := resty.New()
|
||||||
|
httpmock.ActivateNonDefault(client.GetClient())
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
|
uri := "localhost:8081"
|
||||||
|
|
||||||
|
// Given
|
||||||
|
httpmock.RegisterResponder("POST", fmt.Sprintf("%s/events", uri),
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
resp, err := httpmock.NewJsonResponse(200, &tracer.Event{})
|
||||||
|
if err != nil {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk")
|
||||||
|
beelzebubCloud.client = client
|
||||||
|
|
||||||
|
//When
|
||||||
|
result, err := beelzebubCloud.SendEvent(tracer.Event{})
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Equal(t, true, result)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildSendEventErro(t *testing.T) {
|
||||||
|
client := resty.New()
|
||||||
|
httpmock.ActivateNonDefault(client.GetClient())
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
|
uri := "localhost:8081/events"
|
||||||
|
|
||||||
|
// Given
|
||||||
|
httpmock.RegisterResponder("POST", uri,
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk")
|
||||||
|
beelzebubCloud.client = client
|
||||||
|
|
||||||
|
//When
|
||||||
|
result, _ := beelzebubCloud.SendEvent(tracer.Event{})
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Equal(t, false, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetHoneypotsConfigurationsWithResults(t *testing.T) {
|
||||||
|
client := resty.New()
|
||||||
|
httpmock.ActivateNonDefault(client.GetClient())
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
|
uri := "localhost:8081"
|
||||||
|
|
||||||
|
// Given
|
||||||
|
httpmock.RegisterResponder("GET", fmt.Sprintf("%s/honeypots", uri),
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
resp, err := httpmock.NewJsonResponse(200, &[]HoneypotConfigResponseDTO{
|
||||||
|
{
|
||||||
|
ID: "123456",
|
||||||
|
Config: "apiVersion: \"v1\"\nprotocol: \"ssh\"\naddress: \":2222\"\ndescription: \"SSH interactive ChatGPT\"\ncommands:\n - regex: \"^(.+)$\"\n plugin: \"LLMHoneypot\"\nserverVersion: \"OpenSSH\"\nserverName: \"ubuntu\"\npasswordRegex: \"^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$\"\ndeadlineTimeoutSeconds: 60\nplugin:\n llmModel: \"gpt4-o\"\n openAISecretKey: \"1234\"\n",
|
||||||
|
TokenID: "1234567",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk")
|
||||||
|
beelzebubCloud.client = client
|
||||||
|
|
||||||
|
//When
|
||||||
|
result, err := beelzebubCloud.GetHoneypotsConfigurations()
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Equal(t, &[]parser.BeelzebubServiceConfiguration{
|
||||||
|
{
|
||||||
|
ApiVersion: "v1",
|
||||||
|
Protocol: "ssh",
|
||||||
|
Address: ":2222",
|
||||||
|
Description: "SSH interactive ChatGPT",
|
||||||
|
Commands: []parser.Command{
|
||||||
|
{
|
||||||
|
Regex: "^(.+)$",
|
||||||
|
Plugin: "LLMHoneypot",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ServerVersion: "OpenSSH",
|
||||||
|
ServerName: "ubuntu",
|
||||||
|
PasswordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$",
|
||||||
|
DeadlineTimeoutSeconds: 60,
|
||||||
|
Plugin: parser.Plugin{
|
||||||
|
LLMModel: "gpt4-o",
|
||||||
|
OpenAISecretKey: "1234",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, &result)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetHoneypotsConfigurationsWithErrorValidation(t *testing.T) {
|
||||||
|
//Given
|
||||||
|
beelzebubCloud := InitBeelzebubCloud("", "")
|
||||||
|
|
||||||
|
//When
|
||||||
|
result, err := beelzebubCloud.GetHoneypotsConfigurations()
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Nil(t, result)
|
||||||
|
assert.Equal(t, "authToken is empty", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetHoneypotsConfigurationsWithErrorAPI(t *testing.T) {
|
||||||
|
client := resty.New()
|
||||||
|
httpmock.ActivateNonDefault(client.GetClient())
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
|
uri := "localhost:8081"
|
||||||
|
|
||||||
|
// Given
|
||||||
|
httpmock.RegisterResponder("GET", fmt.Sprintf("%s/honeypots", uri),
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk")
|
||||||
|
beelzebubCloud.client = client
|
||||||
|
|
||||||
|
//When
|
||||||
|
result, err := beelzebubCloud.GetHoneypotsConfigurations()
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Nil(t, result)
|
||||||
|
assert.Equal(t, "Response code: 500, error: ", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetHoneypotsConfigurationsWithErrorUnmarshal(t *testing.T) {
|
||||||
|
client := resty.New()
|
||||||
|
httpmock.ActivateNonDefault(client.GetClient())
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
|
uri := "localhost:8081"
|
||||||
|
|
||||||
|
// Given
|
||||||
|
httpmock.RegisterResponder("GET", fmt.Sprintf("%s/honeypots", uri),
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
resp, err := httpmock.NewJsonResponse(200, "error")
|
||||||
|
if err != nil {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk")
|
||||||
|
beelzebubCloud.client = client
|
||||||
|
|
||||||
|
//When
|
||||||
|
result, err := beelzebubCloud.GetHoneypotsConfigurations()
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Nil(t, result)
|
||||||
|
assert.Equal(t, "json: cannot unmarshal string into Go value of type []plugins.HoneypotConfigResponseDTO", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetHoneypotsConfigurationsWithErrorDeserializeYaml(t *testing.T) {
|
||||||
|
client := resty.New()
|
||||||
|
httpmock.ActivateNonDefault(client.GetClient())
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
|
uri := "localhost:8081"
|
||||||
|
|
||||||
|
// Given
|
||||||
|
httpmock.RegisterResponder("GET", fmt.Sprintf("%s/honeypots", uri),
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
resp, err := httpmock.NewJsonResponse(200, &[]HoneypotConfigResponseDTO{
|
||||||
|
{
|
||||||
|
ID: "123456",
|
||||||
|
Config: "error",
|
||||||
|
TokenID: "1234567",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk")
|
||||||
|
beelzebubCloud.client = client
|
||||||
|
|
||||||
|
//When
|
||||||
|
result, err := beelzebubCloud.GetHoneypotsConfigurations()
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Nil(t, result)
|
||||||
|
assert.Equal(t, "yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `error` into parser.BeelzebubServiceConfiguration", err.Error())
|
||||||
|
}
|
229
plugins/llm-integration.go
Normal file
229
plugins/llm-integration.go
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
systemPromptVirtualizeLinuxTerminal = "You will act as an Ubuntu Linux terminal. The user will type commands, and you are to reply with what the terminal should show. Your responses must be contained within a single code block. Do not provide explanations or type commands unless explicitly instructed by the user. Your entire response/output is going to consist of a simple text with \n for new line, and you will NOT wrap it within string md markers"
|
||||||
|
systemPromptVirtualizeHTTPServer = "You will act as an unsecure HTTP Server with multiple vulnerability like aws and git credentials stored into root http directory. The user will send HTTP requests, and you are to reply with what the server should show. Do not provide explanations or type commands unless explicitly instructed by the user."
|
||||||
|
LLMPluginName = "LLMHoneypot"
|
||||||
|
openAIGPTEndpoint = "https://api.openai.com/v1/chat/completions"
|
||||||
|
ollamaEndpoint = "http://localhost:11434/api/chat"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LLMHoneypot struct {
|
||||||
|
Histories []Message
|
||||||
|
OpenAIKey string
|
||||||
|
client *resty.Client
|
||||||
|
Protocol tracer.Protocol
|
||||||
|
Model LLMModel
|
||||||
|
Host string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Choice struct {
|
||||||
|
Message Message `json:"message"`
|
||||||
|
Index int `json:"index"`
|
||||||
|
FinishReason string `json:"finish_reason"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Object string `json:"object"`
|
||||||
|
Created int `json:"created"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
Choices []Choice `json:"choices"`
|
||||||
|
Message Message `json:"message"`
|
||||||
|
Usage struct {
|
||||||
|
PromptTokens int `json:"prompt_tokens"`
|
||||||
|
CompletionTokens int `json:"completion_tokens"`
|
||||||
|
TotalTokens int `json:"total_tokens"`
|
||||||
|
} `json:"usage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
Messages []Message `json:"messages"`
|
||||||
|
Stream bool `json:"stream"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Role string `json:"role"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Role int
|
||||||
|
|
||||||
|
const (
|
||||||
|
SYSTEM Role = iota
|
||||||
|
USER
|
||||||
|
ASSISTANT
|
||||||
|
)
|
||||||
|
|
||||||
|
func (role Role) String() string {
|
||||||
|
return [...]string{"system", "user", "assistant"}[role]
|
||||||
|
}
|
||||||
|
|
||||||
|
type LLMModel int
|
||||||
|
|
||||||
|
const (
|
||||||
|
LLAMA3 LLMModel = iota
|
||||||
|
GPT4O
|
||||||
|
)
|
||||||
|
|
||||||
|
func FromStringToLLMModel(llmModel string) (LLMModel, error) {
|
||||||
|
switch llmModel {
|
||||||
|
case "llama3":
|
||||||
|
return LLAMA3, nil
|
||||||
|
case "gpt4-o":
|
||||||
|
return GPT4O, nil
|
||||||
|
default:
|
||||||
|
return -1, fmt.Errorf("model %s not found", llmModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitLLMHoneypot(config LLMHoneypot) *LLMHoneypot {
|
||||||
|
// Inject the dependencies
|
||||||
|
config.client = resty.New()
|
||||||
|
|
||||||
|
return &config
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildPrompt(histories []Message, protocol tracer.Protocol, command string) ([]Message, error) {
|
||||||
|
var messages []Message
|
||||||
|
|
||||||
|
switch protocol {
|
||||||
|
case tracer.SSH:
|
||||||
|
messages = append(messages, Message{
|
||||||
|
Role: SYSTEM.String(),
|
||||||
|
Content: systemPromptVirtualizeLinuxTerminal,
|
||||||
|
})
|
||||||
|
messages = append(messages, Message{
|
||||||
|
Role: USER.String(),
|
||||||
|
Content: "pwd",
|
||||||
|
})
|
||||||
|
messages = append(messages, Message{
|
||||||
|
Role: ASSISTANT.String(),
|
||||||
|
Content: "/home/user",
|
||||||
|
})
|
||||||
|
for _, history := range histories {
|
||||||
|
messages = append(messages, history)
|
||||||
|
}
|
||||||
|
case tracer.HTTP:
|
||||||
|
messages = append(messages, Message{
|
||||||
|
Role: SYSTEM.String(),
|
||||||
|
Content: systemPromptVirtualizeHTTPServer,
|
||||||
|
})
|
||||||
|
messages = append(messages, Message{
|
||||||
|
Role: USER.String(),
|
||||||
|
Content: "GET /index.html",
|
||||||
|
})
|
||||||
|
messages = append(messages, Message{
|
||||||
|
Role: ASSISTANT.String(),
|
||||||
|
Content: "<html><body>Hello, World!</body></html>",
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return nil, errors.New("no prompt for protocol selected")
|
||||||
|
}
|
||||||
|
messages = append(messages, Message{
|
||||||
|
Role: USER.String(),
|
||||||
|
Content: command,
|
||||||
|
})
|
||||||
|
|
||||||
|
return messages, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (llmHoneypot *LLMHoneypot) openAICaller(messages []Message) (string, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
requestJson, err := json.Marshal(Request{
|
||||||
|
Model: "gpt-4o",
|
||||||
|
Messages: messages,
|
||||||
|
Stream: false,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if llmHoneypot.OpenAIKey == "" {
|
||||||
|
return "", errors.New("openAIKey is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if llmHoneypot.Host == "" {
|
||||||
|
llmHoneypot.Host = openAIGPTEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug(string(requestJson))
|
||||||
|
response, err := llmHoneypot.client.R().
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetBody(requestJson).
|
||||||
|
SetAuthToken(llmHoneypot.OpenAIKey).
|
||||||
|
SetResult(&Response{}).
|
||||||
|
Post(llmHoneypot.Host)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
log.Debug(response)
|
||||||
|
if len(response.Result().(*Response).Choices) == 0 {
|
||||||
|
return "", errors.New("no choices")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Result().(*Response).Choices[0].Message.Content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (llmHoneypot *LLMHoneypot) ollamaCaller(messages []Message) (string, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
requestJson, err := json.Marshal(Request{
|
||||||
|
Model: "llama3",
|
||||||
|
Messages: messages,
|
||||||
|
Stream: false,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if llmHoneypot.Host == "" {
|
||||||
|
llmHoneypot.Host = ollamaEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug(string(requestJson))
|
||||||
|
response, err := llmHoneypot.client.R().
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetBody(requestJson).
|
||||||
|
SetResult(&Response{}).
|
||||||
|
Post(llmHoneypot.Host)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
log.Debug(response)
|
||||||
|
|
||||||
|
return response.Result().(*Response).Message.Content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (llmHoneypot *LLMHoneypot) ExecuteModel(command string) (string, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
prompt, err := buildPrompt(llmHoneypot.Histories, llmHoneypot.Protocol, command)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch llmHoneypot.Model {
|
||||||
|
case LLAMA3:
|
||||||
|
return llmHoneypot.ollamaCaller(prompt)
|
||||||
|
case GPT4O:
|
||||||
|
return llmHoneypot.openAICaller(prompt)
|
||||||
|
default:
|
||||||
|
return "", errors.New("no model selected")
|
||||||
|
}
|
||||||
|
}
|
300
plugins/llm-integration_test.go
Normal file
300
plugins/llm-integration_test.go
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/jarcoal/httpmock"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const SystemPromptLen = 4
|
||||||
|
|
||||||
|
func TestBuildPromptEmptyHistory(t *testing.T) {
|
||||||
|
//Given
|
||||||
|
var histories []Message
|
||||||
|
command := "pwd"
|
||||||
|
|
||||||
|
//When
|
||||||
|
prompt, err := buildPrompt(histories, tracer.SSH, command)
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, SystemPromptLen, len(prompt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildPromptWithHistory(t *testing.T) {
|
||||||
|
//Given
|
||||||
|
var histories = []Message{
|
||||||
|
{
|
||||||
|
Role: "cat hello.txt",
|
||||||
|
Content: "world",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
command := "pwd"
|
||||||
|
|
||||||
|
//When
|
||||||
|
prompt, err := buildPrompt(histories, tracer.SSH, command)
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, SystemPromptLen+1, len(prompt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildExecuteModelFailValidation(t *testing.T) {
|
||||||
|
|
||||||
|
llmHoneypot := LLMHoneypot{
|
||||||
|
Histories: make([]Message, 0),
|
||||||
|
OpenAIKey: "",
|
||||||
|
Protocol: tracer.SSH,
|
||||||
|
Model: GPT4O,
|
||||||
|
}
|
||||||
|
|
||||||
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
|
|
||||||
|
_, err := openAIGPTVirtualTerminal.ExecuteModel("test")
|
||||||
|
|
||||||
|
assert.Equal(t, "openAIKey is empty", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildExecuteModelFailValidationStrategyType(t *testing.T) {
|
||||||
|
|
||||||
|
llmHoneypot := LLMHoneypot{
|
||||||
|
Histories: make([]Message, 0),
|
||||||
|
OpenAIKey: "",
|
||||||
|
Protocol: tracer.TCP,
|
||||||
|
Model: GPT4O,
|
||||||
|
}
|
||||||
|
|
||||||
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
|
|
||||||
|
_, err := openAIGPTVirtualTerminal.ExecuteModel("test")
|
||||||
|
|
||||||
|
assert.Equal(t, "no prompt for protocol selected", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildExecuteModelFailValidationModelType(t *testing.T) {
|
||||||
|
// Given
|
||||||
|
llmHoneypot := LLMHoneypot{
|
||||||
|
Histories: make([]Message, 0),
|
||||||
|
Protocol: tracer.SSH,
|
||||||
|
Model: 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
|
|
||||||
|
//When
|
||||||
|
_, err := openAIGPTVirtualTerminal.ExecuteModel("ls")
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Errorf(t, err, "no model selected")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildExecuteModelSSHWithResultsOpenAI(t *testing.T) {
|
||||||
|
client := resty.New()
|
||||||
|
httpmock.ActivateNonDefault(client.GetClient())
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
|
// Given
|
||||||
|
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
resp, err := httpmock.NewJsonResponse(200, &Response{
|
||||||
|
Choices: []Choice{
|
||||||
|
{
|
||||||
|
Message: Message{
|
||||||
|
Role: SYSTEM.String(),
|
||||||
|
Content: "prova.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
llmHoneypot := LLMHoneypot{
|
||||||
|
Histories: make([]Message, 0),
|
||||||
|
OpenAIKey: "sdjdnklfjndslkjanfk",
|
||||||
|
Protocol: tracer.SSH,
|
||||||
|
Model: GPT4O,
|
||||||
|
}
|
||||||
|
|
||||||
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
|
openAIGPTVirtualTerminal.client = client
|
||||||
|
|
||||||
|
//When
|
||||||
|
str, err := openAIGPTVirtualTerminal.ExecuteModel("ls")
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "prova.txt", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildExecuteModelSSHWithResultsLLama(t *testing.T) {
|
||||||
|
client := resty.New()
|
||||||
|
httpmock.ActivateNonDefault(client.GetClient())
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
|
// Given
|
||||||
|
httpmock.RegisterResponder("POST", ollamaEndpoint,
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
resp, err := httpmock.NewJsonResponse(200, &Response{
|
||||||
|
Message: Message{
|
||||||
|
Role: SYSTEM.String(),
|
||||||
|
Content: "prova.txt",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
llmHoneypot := LLMHoneypot{
|
||||||
|
Histories: make([]Message, 0),
|
||||||
|
Protocol: tracer.SSH,
|
||||||
|
Model: LLAMA3,
|
||||||
|
}
|
||||||
|
|
||||||
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
|
openAIGPTVirtualTerminal.client = client
|
||||||
|
|
||||||
|
//When
|
||||||
|
str, err := openAIGPTVirtualTerminal.ExecuteModel("ls")
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "prova.txt", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildExecuteModelSSHWithoutResults(t *testing.T) {
|
||||||
|
client := resty.New()
|
||||||
|
httpmock.ActivateNonDefault(client.GetClient())
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
|
// Given
|
||||||
|
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
resp, err := httpmock.NewJsonResponse(200, &Response{
|
||||||
|
Choices: []Choice{},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
llmHoneypot := LLMHoneypot{
|
||||||
|
Histories: make([]Message, 0),
|
||||||
|
OpenAIKey: "sdjdnklfjndslkjanfk",
|
||||||
|
Protocol: tracer.SSH,
|
||||||
|
Model: GPT4O,
|
||||||
|
}
|
||||||
|
|
||||||
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
|
openAIGPTVirtualTerminal.client = client
|
||||||
|
|
||||||
|
//When
|
||||||
|
_, err := openAIGPTVirtualTerminal.ExecuteModel("ls")
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Equal(t, "no choices", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildExecuteModelHTTPWithResults(t *testing.T) {
|
||||||
|
client := resty.New()
|
||||||
|
httpmock.ActivateNonDefault(client.GetClient())
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
|
// Given
|
||||||
|
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
resp, err := httpmock.NewJsonResponse(200, &Response{
|
||||||
|
Choices: []Choice{
|
||||||
|
{
|
||||||
|
Message: Message{
|
||||||
|
Role: SYSTEM.String(),
|
||||||
|
Content: "[default]\nregion = us-west-2\noutput = json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
llmHoneypot := LLMHoneypot{
|
||||||
|
Histories: make([]Message, 0),
|
||||||
|
OpenAIKey: "sdjdnklfjndslkjanfk",
|
||||||
|
Protocol: tracer.HTTP,
|
||||||
|
Model: GPT4O,
|
||||||
|
}
|
||||||
|
|
||||||
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
|
openAIGPTVirtualTerminal.client = client
|
||||||
|
|
||||||
|
//When
|
||||||
|
str, err := openAIGPTVirtualTerminal.ExecuteModel("GET /.aws/credentials")
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "[default]\nregion = us-west-2\noutput = json", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildExecuteModelHTTPWithoutResults(t *testing.T) {
|
||||||
|
client := resty.New()
|
||||||
|
httpmock.ActivateNonDefault(client.GetClient())
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
|
// Given
|
||||||
|
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
|
||||||
|
func(req *http.Request) (*http.Response, error) {
|
||||||
|
resp, err := httpmock.NewJsonResponse(200, &Response{
|
||||||
|
Choices: []Choice{},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
llmHoneypot := LLMHoneypot{
|
||||||
|
Histories: make([]Message, 0),
|
||||||
|
OpenAIKey: "sdjdnklfjndslkjanfk",
|
||||||
|
Protocol: tracer.HTTP,
|
||||||
|
Model: GPT4O,
|
||||||
|
}
|
||||||
|
|
||||||
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
|
openAIGPTVirtualTerminal.client = client
|
||||||
|
|
||||||
|
//When
|
||||||
|
_, err := openAIGPTVirtualTerminal.ExecuteModel("GET /.aws/credentials")
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Equal(t, "no choices", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromString(t *testing.T) {
|
||||||
|
model, err := FromStringToLLMModel("llama3")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, LLAMA3, model)
|
||||||
|
|
||||||
|
model, err = FromStringToLLMModel("gpt4-o")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, GPT4O, model)
|
||||||
|
|
||||||
|
model, err = FromStringToLLMModel("beelzebub-model")
|
||||||
|
assert.Errorf(t, err, "model beelzebub-model not found")
|
||||||
|
}
|
@ -1,117 +0,0 @@
|
|||||||
package plugins
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/go-resty/resty/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Reference: https://www.engraved.blog/building-a-virtual-machine-inside/
|
|
||||||
promptVirtualizeLinuxTerminal = "I want you to act as a Linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. Do no write explanations. Do not type commands unless I instruct you to do so.\n\nA:pwd\n\nQ:/home/user\n\n"
|
|
||||||
ChatGPTPluginName = "OpenAIGPTLinuxTerminal"
|
|
||||||
openAIGPTEndpoint = "https://api.openai.com/v1/completions"
|
|
||||||
)
|
|
||||||
|
|
||||||
type History struct {
|
|
||||||
Input, Output string
|
|
||||||
}
|
|
||||||
|
|
||||||
type OpenAIGPTVirtualTerminal struct {
|
|
||||||
Histories []History
|
|
||||||
OpenAPIChatGPTSecretKey string
|
|
||||||
client *resty.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func (openAIGPTVirtualTerminal *OpenAIGPTVirtualTerminal) InjectDependency() {
|
|
||||||
if openAIGPTVirtualTerminal.client == nil {
|
|
||||||
openAIGPTVirtualTerminal.client = resty.New()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Choice struct {
|
|
||||||
Text string `json:"text"`
|
|
||||||
Index int `json:"index"`
|
|
||||||
Logprobs interface{} `json:"logprobs"`
|
|
||||||
FinishReason string `json:"finish_reason"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type gptResponse struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Object string `json:"object"`
|
|
||||||
Created int `json:"created"`
|
|
||||||
Model string `json:"model"`
|
|
||||||
Choices []Choice `json:"choices"`
|
|
||||||
Usage struct {
|
|
||||||
PromptTokens int `json:"prompt_tokens"`
|
|
||||||
CompletionTokens int `json:"completion_tokens"`
|
|
||||||
TotalTokens int `json:"total_tokens"`
|
|
||||||
} `json:"usage"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type gptRequest struct {
|
|
||||||
Model string `json:"model"`
|
|
||||||
Prompt string `json:"prompt"`
|
|
||||||
Temperature int `json:"temperature"`
|
|
||||||
MaxTokens int `json:"max_tokens"`
|
|
||||||
TopP int `json:"top_p"`
|
|
||||||
FrequencyPenalty int `json:"frequency_penalty"`
|
|
||||||
PresencePenalty int `json:"presence_penalty"`
|
|
||||||
Stop []string `json:"stop"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildPrompt(histories []History, command string) string {
|
|
||||||
var sb strings.Builder
|
|
||||||
|
|
||||||
sb.WriteString(promptVirtualizeLinuxTerminal)
|
|
||||||
|
|
||||||
for _, history := range histories {
|
|
||||||
sb.WriteString(fmt.Sprintf("A:%s\n\nQ:%s\n\n", history.Input, history.Output))
|
|
||||||
}
|
|
||||||
// Append command to evaluate
|
|
||||||
sb.WriteString(fmt.Sprintf("A:%s\n\nQ:", command))
|
|
||||||
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (openAIGPTVirtualTerminal *OpenAIGPTVirtualTerminal) GetCompletions(command string) (string, error) {
|
|
||||||
requestJson, err := json.Marshal(gptRequest{
|
|
||||||
Model: "text-davinci-003",
|
|
||||||
Prompt: buildPrompt(openAIGPTVirtualTerminal.Histories, command),
|
|
||||||
Temperature: 0,
|
|
||||||
MaxTokens: 100,
|
|
||||||
TopP: 1,
|
|
||||||
FrequencyPenalty: 0,
|
|
||||||
PresencePenalty: 0,
|
|
||||||
Stop: []string{"\n"},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if openAIGPTVirtualTerminal.OpenAPIChatGPTSecretKey == "" {
|
|
||||||
return "", errors.New("OpenAPIChatGPTSecretKey is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := openAIGPTVirtualTerminal.client.R().
|
|
||||||
SetHeader("Content-Type", "application/json").
|
|
||||||
SetBody(requestJson).
|
|
||||||
SetAuthToken(openAIGPTVirtualTerminal.OpenAPIChatGPTSecretKey).
|
|
||||||
SetResult(&gptResponse{}).
|
|
||||||
Post(openAIGPTEndpoint)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
log.Debug(response)
|
|
||||||
if len(response.Result().(*gptResponse).Choices) == 0 {
|
|
||||||
return "", errors.New("no choices")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Result().(*gptResponse).Choices[0].Text, nil
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
package plugins
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-resty/resty/v2"
|
|
||||||
"github.com/jarcoal/httpmock"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBuildPromptEmptyHistory(t *testing.T) {
|
|
||||||
//Given
|
|
||||||
var histories []History
|
|
||||||
command := "pwd"
|
|
||||||
|
|
||||||
//When
|
|
||||||
prompt := buildPrompt(histories, command)
|
|
||||||
|
|
||||||
//Then
|
|
||||||
assert.Equal(t,
|
|
||||||
"I want you to act as a Linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. Do no write explanations. Do not type commands unless I instruct you to do so.\n\nA:pwd\n\nQ:/home/user\n\nA:pwd\n\nQ:",
|
|
||||||
prompt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuildPromptWithHistory(t *testing.T) {
|
|
||||||
//Given
|
|
||||||
var histories = []History{
|
|
||||||
{
|
|
||||||
Input: "cat hello.txt",
|
|
||||||
Output: "world",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Input: "echo 1234",
|
|
||||||
Output: "1234",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
command := "pwd"
|
|
||||||
|
|
||||||
//When
|
|
||||||
prompt := buildPrompt(histories, command)
|
|
||||||
|
|
||||||
//Then
|
|
||||||
assert.Equal(t,
|
|
||||||
"I want you to act as a Linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. Do no write explanations. Do not type commands unless I instruct you to do so.\n\nA:pwd\n\nQ:/home/user\n\nA:cat hello.txt\n\nQ:world\n\nA:echo 1234\n\nQ:1234\n\nA:pwd\n\nQ:",
|
|
||||||
prompt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuildGetCompletions(t *testing.T) {
|
|
||||||
client := resty.New()
|
|
||||||
httpmock.ActivateNonDefault(client.GetClient())
|
|
||||||
defer httpmock.DeactivateAndReset()
|
|
||||||
|
|
||||||
// Given
|
|
||||||
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
|
|
||||||
func(req *http.Request) (*http.Response, error) {
|
|
||||||
resp, err := httpmock.NewJsonResponse(200, &gptResponse{
|
|
||||||
Choices: []Choice{
|
|
||||||
{
|
|
||||||
Text: "prova.txt",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return httpmock.NewStringResponse(500, ""), nil
|
|
||||||
}
|
|
||||||
return resp, nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
openAIGPTVirtualTerminal := OpenAIGPTVirtualTerminal{
|
|
||||||
OpenAPIChatGPTSecretKey: "sdjdnklfjndslkjanfk",
|
|
||||||
client: client,
|
|
||||||
}
|
|
||||||
|
|
||||||
//When
|
|
||||||
str, err := openAIGPTVirtualTerminal.GetCompletions("ls")
|
|
||||||
|
|
||||||
//Then
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, "prova.txt", str)
|
|
||||||
}
|
|
@ -1,10 +1,12 @@
|
|||||||
|
// Package protocols is responsible for managing the different protocols
|
||||||
package protocols
|
package protocols
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"beelzebub/parser"
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
"beelzebub/tracer"
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServiceStrategy is the common interface that each protocol honeypot implements
|
||||||
type ServiceStrategy interface {
|
type ServiceStrategy interface {
|
||||||
Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tracer tracer.Tracer) error
|
Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tracer tracer.Tracer) error
|
||||||
}
|
}
|
||||||
@ -14,10 +16,11 @@ type ProtocolManager struct {
|
|||||||
tracer tracer.Tracer
|
tracer tracer.Tracer
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitProtocolManager(tracerStrategy tracer.Strategy, strategy ServiceStrategy) *ProtocolManager {
|
// InitProtocolManager is the method that initializes the protocol manager, receving the concrete tracer and the concrete service
|
||||||
|
func InitProtocolManager(tracerStrategy tracer.Strategy, serviceStrategy ServiceStrategy) *ProtocolManager {
|
||||||
return &ProtocolManager{
|
return &ProtocolManager{
|
||||||
tracer: tracer.Init(tracerStrategy),
|
tracer: tracer.GetInstance(tracerStrategy),
|
||||||
strategy: strategy,
|
strategy: serviceStrategy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,6 +28,7 @@ func (pm *ProtocolManager) SetProtocolStrategy(strategy ServiceStrategy) {
|
|||||||
pm.strategy = strategy
|
pm.strategy = strategy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitService is the method that initializes the honeypot
|
||||||
func (pm *ProtocolManager) InitService(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration) error {
|
func (pm *ProtocolManager) InitService(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration) error {
|
||||||
return pm.strategy.Init(beelzebubServiceConfiguration, pm.tracer)
|
return pm.strategy.Init(beelzebubServiceConfiguration, pm.tracer)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package protocols
|
package protocols
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"beelzebub/parser"
|
|
||||||
"beelzebub/tracer"
|
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package strategies
|
package strategies
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"beelzebub/parser"
|
|
||||||
"beelzebub/tracer"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/plugins"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@ -31,8 +33,40 @@ func (httpStrategy HTTPStrategy) Init(beelzebubServiceConfiguration parser.Beelz
|
|||||||
}
|
}
|
||||||
|
|
||||||
if matched {
|
if matched {
|
||||||
|
responseHTTPBody := command.Handler
|
||||||
|
|
||||||
|
if command.Plugin == plugins.LLMPluginName {
|
||||||
|
|
||||||
|
llmModel, err := plugins.FromStringToLLMModel(beelzebubServiceConfiguration.Plugin.LLMModel)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error fromString: %s", err.Error())
|
||||||
|
responseHTTPBody = "404 Not Found!"
|
||||||
|
}
|
||||||
|
|
||||||
|
llmHoneypot := plugins.LLMHoneypot{
|
||||||
|
Histories: make([]plugins.Message, 0),
|
||||||
|
OpenAIKey: beelzebubServiceConfiguration.Plugin.OpenAISecretKey,
|
||||||
|
Protocol: tracer.HTTP,
|
||||||
|
Host: beelzebubServiceConfiguration.Plugin.Host,
|
||||||
|
Model: llmModel,
|
||||||
|
}
|
||||||
|
|
||||||
|
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
|
||||||
|
|
||||||
|
command := fmt.Sprintf("%s %s", request.Method, request.RequestURI)
|
||||||
|
|
||||||
|
if completions, err := llmHoneypotInstance.ExecuteModel(command); err != nil {
|
||||||
|
log.Errorf("Error ExecuteModel: %s, %s", command, err.Error())
|
||||||
|
responseHTTPBody = "404 Not Found!"
|
||||||
|
} else {
|
||||||
|
responseHTTPBody = completions
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
setResponseHeaders(responseWriter, command.Headers, command.StatusCode)
|
setResponseHeaders(responseWriter, command.Headers, command.StatusCode)
|
||||||
fmt.Fprintf(responseWriter, command.Handler)
|
fmt.Fprintf(responseWriter, responseHTTPBody)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,6 +92,8 @@ func traceRequest(request *http.Request, tr tracer.Tracer, HoneypotDescription s
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
body = string(bodyBytes)
|
body = string(bodyBytes)
|
||||||
}
|
}
|
||||||
|
host, port, _ := net.SplitHostPort(request.RemoteAddr)
|
||||||
|
|
||||||
tr.TraceEvent(tracer.Event{
|
tr.TraceEvent(tracer.Event{
|
||||||
Msg: "HTTP New request",
|
Msg: "HTTP New request",
|
||||||
RequestURI: request.RequestURI,
|
RequestURI: request.RequestURI,
|
||||||
@ -70,6 +106,8 @@ func traceRequest(request *http.Request, tr tracer.Tracer, HoneypotDescription s
|
|||||||
Headers: mapHeaderToString(request.Header),
|
Headers: mapHeaderToString(request.Header),
|
||||||
Status: tracer.Stateless.String(),
|
Status: tracer.Stateless.String(),
|
||||||
RemoteAddr: request.RemoteAddr,
|
RemoteAddr: request.RemoteAddr,
|
||||||
|
SourceIp: host,
|
||||||
|
SourcePort: port,
|
||||||
ID: uuid.New().String(),
|
ID: uuid.New().String(),
|
||||||
Description: HoneypotDescription,
|
Description: HoneypotDescription,
|
||||||
})
|
})
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package strategies
|
package strategies
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"beelzebub/parser"
|
|
||||||
"beelzebub/plugins"
|
|
||||||
"beelzebub/tracer"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/plugins"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
|
"net"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -28,20 +29,85 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
Handler: func(sess ssh.Session) {
|
Handler: func(sess ssh.Session) {
|
||||||
uuidSession := uuid.New()
|
uuidSession := uuid.New()
|
||||||
|
|
||||||
|
host, port, _ := net.SplitHostPort(sess.RemoteAddr().String())
|
||||||
|
|
||||||
|
if sess.RawCommand() != "" {
|
||||||
|
for _, command := range beelzebubServiceConfiguration.Commands {
|
||||||
|
matched, err := regexp.MatchString(command.Regex, sess.RawCommand())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error regex: %s, %s", command.Regex, err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if matched {
|
||||||
|
commandOutput := command.Handler
|
||||||
|
|
||||||
|
if command.Plugin == plugins.LLMPluginName {
|
||||||
|
|
||||||
|
llmModel, err := plugins.FromStringToLLMModel(beelzebubServiceConfiguration.Plugin.LLMModel)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error fromString: %s", err.Error())
|
||||||
|
commandOutput = "command not found"
|
||||||
|
}
|
||||||
|
|
||||||
|
llmHoneypot := plugins.LLMHoneypot{
|
||||||
|
Histories: make([]plugins.Message, 0),
|
||||||
|
OpenAIKey: beelzebubServiceConfiguration.Plugin.OpenAISecretKey,
|
||||||
|
Protocol: tracer.SSH,
|
||||||
|
Host: beelzebubServiceConfiguration.Plugin.Host,
|
||||||
|
Model: llmModel,
|
||||||
|
}
|
||||||
|
|
||||||
|
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
|
||||||
|
|
||||||
|
if commandOutput, err = llmHoneypotInstance.ExecuteModel(sess.RawCommand()); err != nil {
|
||||||
|
log.Errorf("Error ExecuteModel: %s, %s", sess.RawCommand(), err.Error())
|
||||||
|
commandOutput = "command not found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sess.Write(append([]byte(commandOutput), '\n'))
|
||||||
|
|
||||||
tr.TraceEvent(tracer.Event{
|
tr.TraceEvent(tracer.Event{
|
||||||
Msg: "New SSH Session",
|
Msg: "New SSH Session",
|
||||||
Protocol: tracer.SSH.String(),
|
Protocol: tracer.SSH.String(),
|
||||||
RemoteAddr: sess.RemoteAddr().String(),
|
RemoteAddr: sess.RemoteAddr().String(),
|
||||||
|
SourceIp: host,
|
||||||
|
SourcePort: port,
|
||||||
Status: tracer.Start.String(),
|
Status: tracer.Start.String(),
|
||||||
ID: uuidSession.String(),
|
ID: uuidSession.String(),
|
||||||
Environ: strings.Join(sess.Environ(), ","),
|
Environ: strings.Join(sess.Environ(), ","),
|
||||||
User: sess.User(),
|
User: sess.User(),
|
||||||
Description: beelzebubServiceConfiguration.Description,
|
Description: beelzebubServiceConfiguration.Description,
|
||||||
Command: sess.RawCommand(),
|
Command: sess.RawCommand(),
|
||||||
|
CommandOutput: commandOutput,
|
||||||
|
})
|
||||||
|
tr.TraceEvent(tracer.Event{
|
||||||
|
Msg: "End SSH Session",
|
||||||
|
Status: tracer.End.String(),
|
||||||
|
ID: uuidSession.String(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.TraceEvent(tracer.Event{
|
||||||
|
Msg: "New SSH Session",
|
||||||
|
Protocol: tracer.SSH.String(),
|
||||||
|
RemoteAddr: sess.RemoteAddr().String(),
|
||||||
|
SourceIp: host,
|
||||||
|
SourcePort: port,
|
||||||
|
Status: tracer.Start.String(),
|
||||||
|
ID: uuidSession.String(),
|
||||||
|
Environ: strings.Join(sess.Environ(), ","),
|
||||||
|
User: sess.User(),
|
||||||
|
Description: beelzebubServiceConfiguration.Description,
|
||||||
})
|
})
|
||||||
|
|
||||||
term := terminal.NewTerminal(sess, buildPrompt(sess.User(), beelzebubServiceConfiguration.ServerName))
|
term := terminal.NewTerminal(sess, buildPrompt(sess.User(), beelzebubServiceConfiguration.ServerName))
|
||||||
var histories []plugins.History
|
var histories []plugins.Message
|
||||||
for {
|
for {
|
||||||
commandInput, err := term.ReadLine()
|
commandInput, err := term.ReadLine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -61,23 +127,41 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
if matched {
|
if matched {
|
||||||
commandOutput := command.Handler
|
commandOutput := command.Handler
|
||||||
|
|
||||||
if command.Plugin == plugins.ChatGPTPluginName {
|
if command.Plugin == plugins.LLMPluginName {
|
||||||
openAIGPTVirtualTerminal := plugins.OpenAIGPTVirtualTerminal{Histories: histories, OpenAPIChatGPTSecretKey: beelzebubServiceConfiguration.Plugin.OpenAPIChatGPTSecretKey}
|
|
||||||
openAIGPTVirtualTerminal.InjectDependency()
|
|
||||||
|
|
||||||
if commandOutput, err = openAIGPTVirtualTerminal.GetCompletions(commandInput); err != nil {
|
llmModel, err := plugins.FromStringToLLMModel(beelzebubServiceConfiguration.Plugin.LLMModel)
|
||||||
log.Errorf("Error GetCompletions: %s, %s", commandInput, err.Error())
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error fromString: %s", err.Error())
|
||||||
|
commandOutput = "command not found"
|
||||||
|
}
|
||||||
|
|
||||||
|
llmHoneypot := plugins.LLMHoneypot{
|
||||||
|
Histories: histories,
|
||||||
|
OpenAIKey: beelzebubServiceConfiguration.Plugin.OpenAISecretKey,
|
||||||
|
Protocol: tracer.SSH,
|
||||||
|
Host: beelzebubServiceConfiguration.Plugin.Host,
|
||||||
|
Model: llmModel,
|
||||||
|
}
|
||||||
|
|
||||||
|
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
|
||||||
|
|
||||||
|
if commandOutput, err = llmHoneypotInstance.ExecuteModel(commandInput); err != nil {
|
||||||
|
log.Errorf("Error ExecuteModel: %s, %s", commandInput, err.Error())
|
||||||
commandOutput = "command not found"
|
commandOutput = "command not found"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
histories = append(histories, plugins.History{Input: commandInput, Output: commandOutput})
|
histories = append(histories, plugins.Message{Role: plugins.USER.String(), Content: commandInput})
|
||||||
|
histories = append(histories, plugins.Message{Role: plugins.ASSISTANT.String(), Content: commandOutput})
|
||||||
|
|
||||||
term.Write(append([]byte(commandOutput), '\n'))
|
term.Write(append([]byte(commandOutput), '\n'))
|
||||||
|
|
||||||
tr.TraceEvent(tracer.Event{
|
tr.TraceEvent(tracer.Event{
|
||||||
Msg: "New SSH Terminal Session",
|
Msg: "New SSH Terminal Session",
|
||||||
RemoteAddr: sess.RemoteAddr().String(),
|
RemoteAddr: sess.RemoteAddr().String(),
|
||||||
|
SourceIp: host,
|
||||||
|
SourcePort: port,
|
||||||
Status: tracer.Interaction.String(),
|
Status: tracer.Interaction.String(),
|
||||||
Command: commandInput,
|
Command: commandInput,
|
||||||
CommandOutput: commandOutput,
|
CommandOutput: commandOutput,
|
||||||
@ -96,6 +180,8 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
PasswordHandler: func(ctx ssh.Context, password string) bool {
|
PasswordHandler: func(ctx ssh.Context, password string) bool {
|
||||||
|
host, port, _ := net.SplitHostPort(ctx.RemoteAddr().String())
|
||||||
|
|
||||||
tr.TraceEvent(tracer.Event{
|
tr.TraceEvent(tracer.Event{
|
||||||
Msg: "New SSH attempt",
|
Msg: "New SSH attempt",
|
||||||
Protocol: tracer.SSH.String(),
|
Protocol: tracer.SSH.String(),
|
||||||
@ -104,6 +190,8 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
Password: password,
|
Password: password,
|
||||||
Client: ctx.ClientVersion(),
|
Client: ctx.ClientVersion(),
|
||||||
RemoteAddr: ctx.RemoteAddr().String(),
|
RemoteAddr: ctx.RemoteAddr().String(),
|
||||||
|
SourceIp: host,
|
||||||
|
SourcePort: port,
|
||||||
ID: uuid.New().String(),
|
ID: uuid.New().String(),
|
||||||
Description: beelzebubServiceConfiguration.Description,
|
Description: beelzebubServiceConfiguration.Description,
|
||||||
})
|
})
|
||||||
@ -124,7 +212,7 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"port": beelzebubServiceConfiguration.Address,
|
"port": beelzebubServiceConfiguration.Address,
|
||||||
"commands": len(beelzebubServiceConfiguration.Commands),
|
"commands": len(beelzebubServiceConfiguration.Commands),
|
||||||
}).Infof("Init service %s", beelzebubServiceConfiguration.Protocol)
|
}).Infof("GetInstance service %s", beelzebubServiceConfiguration.Protocol)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package strategies
|
package strategies
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"beelzebub/parser"
|
|
||||||
"beelzebub/tracer"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -35,12 +35,16 @@ func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
command = string(buffer[:n])
|
command = string(buffer[:n])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
host, port, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
||||||
|
|
||||||
tr.TraceEvent(tracer.Event{
|
tr.TraceEvent(tracer.Event{
|
||||||
Msg: "New TCP attempt",
|
Msg: "New TCP attempt",
|
||||||
Protocol: tracer.TCP.String(),
|
Protocol: tracer.TCP.String(),
|
||||||
Command: command,
|
Command: command,
|
||||||
Status: tracer.Stateless.String(),
|
Status: tracer.Stateless.String(),
|
||||||
RemoteAddr: conn.RemoteAddr().String(),
|
RemoteAddr: conn.RemoteAddr().String(),
|
||||||
|
SourceIp: host,
|
||||||
|
SourcePort: port,
|
||||||
ID: uuid.New().String(),
|
ID: uuid.New().String(),
|
||||||
Description: beelzebubServiceConfiguration.Description,
|
Description: beelzebubServiceConfiguration.Description,
|
||||||
})
|
})
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
|
// Package tracer is responsible for tracing the events that occur in the honeypots
|
||||||
package tracer
|
package tracer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Workers is the number of workers that will
|
||||||
const Workers = 5
|
const Workers = 5
|
||||||
|
|
||||||
type Event struct {
|
type Event struct {
|
||||||
@ -31,6 +34,8 @@ type Event struct {
|
|||||||
HTTPMethod string
|
HTTPMethod string
|
||||||
RequestURI string
|
RequestURI string
|
||||||
Description string
|
Description string
|
||||||
|
SourceIp string
|
||||||
|
SourcePort string
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@ -44,8 +49,8 @@ const (
|
|||||||
TCP
|
TCP
|
||||||
)
|
)
|
||||||
|
|
||||||
func (status Protocol) String() string {
|
func (protocol Protocol) String() string {
|
||||||
return [...]string{"HTTP", "SSH", "TCP"}[status]
|
return [...]string{"HTTP", "SSH", "TCP"}[protocol]
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -68,47 +73,58 @@ type Tracer interface {
|
|||||||
type tracer struct {
|
type tracer struct {
|
||||||
strategy Strategy
|
strategy Strategy
|
||||||
eventsChan chan Event
|
eventsChan chan Event
|
||||||
|
eventsTotal prometheus.Counter
|
||||||
|
eventsSSHTotal prometheus.Counter
|
||||||
|
eventsTCPTotal prometheus.Counter
|
||||||
|
eventsHTTPTotal prometheus.Counter
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var lock = &sync.Mutex{}
|
||||||
eventsTotal = promauto.NewCounter(prometheus.CounterOpts{
|
var singleton *tracer
|
||||||
|
|
||||||
|
func GetInstance(defaultStrategy Strategy) *tracer {
|
||||||
|
if singleton == nil {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
// This is to prevent expensive lock operations every time the GetInstance method is called
|
||||||
|
if singleton == nil {
|
||||||
|
singleton = &tracer{
|
||||||
|
strategy: defaultStrategy,
|
||||||
|
eventsChan: make(chan Event, Workers),
|
||||||
|
eventsTotal: promauto.NewCounter(prometheus.CounterOpts{
|
||||||
Namespace: "beelzebub",
|
Namespace: "beelzebub",
|
||||||
Name: "events_total",
|
Name: "events_total",
|
||||||
Help: "The total number of events",
|
Help: "The total number of events",
|
||||||
})
|
}),
|
||||||
eventsSSHTotal = promauto.NewCounter(prometheus.CounterOpts{
|
eventsSSHTotal: promauto.NewCounter(prometheus.CounterOpts{
|
||||||
Namespace: "beelzebub",
|
Namespace: "beelzebub",
|
||||||
Name: "ssh_events_total",
|
Name: "ssh_events_total",
|
||||||
Help: "The total number of SSH events",
|
Help: "The total number of SSH events",
|
||||||
})
|
}),
|
||||||
eventsTCPTotal = promauto.NewCounter(prometheus.CounterOpts{
|
eventsTCPTotal: promauto.NewCounter(prometheus.CounterOpts{
|
||||||
Namespace: "beelzebub",
|
Namespace: "beelzebub",
|
||||||
Name: "tcp_events_total",
|
Name: "tcp_events_total",
|
||||||
Help: "The total number of TCP events",
|
Help: "The total number of TCP events",
|
||||||
})
|
}),
|
||||||
eventsHTTPTotal = promauto.NewCounter(prometheus.CounterOpts{
|
eventsHTTPTotal: promauto.NewCounter(prometheus.CounterOpts{
|
||||||
Namespace: "beelzebub",
|
Namespace: "beelzebub",
|
||||||
Name: "http_events_total",
|
Name: "http_events_total",
|
||||||
Help: "The total number of HTTP events",
|
Help: "The total number of HTTP events",
|
||||||
})
|
}),
|
||||||
)
|
|
||||||
|
|
||||||
func Init(strategy Strategy) *tracer {
|
|
||||||
tracer := &tracer{
|
|
||||||
strategy: strategy,
|
|
||||||
eventsChan: make(chan Event, Workers),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < Workers; i++ {
|
for i := 0; i < Workers; i++ {
|
||||||
go func(i int) {
|
go func(i int) {
|
||||||
log.Debug("Init trace worker: ", i)
|
log.Debug("Trace worker: ", i)
|
||||||
for event := range tracer.eventsChan {
|
for event := range singleton.eventsChan {
|
||||||
tracer.strategy(event)
|
singleton.strategy(event)
|
||||||
}
|
}
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return tracer
|
return singleton
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tracer *tracer) setStrategy(strategy Strategy) {
|
func (tracer *tracer) setStrategy(strategy Strategy) {
|
||||||
@ -120,14 +136,17 @@ func (tracer *tracer) TraceEvent(event Event) {
|
|||||||
|
|
||||||
tracer.eventsChan <- event
|
tracer.eventsChan <- event
|
||||||
|
|
||||||
eventsTotal.Inc()
|
tracer.updatePrometheusCounters(event.Protocol)
|
||||||
|
}
|
||||||
switch event.Protocol {
|
|
||||||
case HTTP.String():
|
func (tracer *tracer) updatePrometheusCounters(protocol string) {
|
||||||
eventsHTTPTotal.Inc()
|
switch protocol {
|
||||||
case SSH.String():
|
case HTTP.String():
|
||||||
eventsSSHTotal.Inc()
|
tracer.eventsHTTPTotal.Inc()
|
||||||
case TCP.String():
|
case SSH.String():
|
||||||
eventsTCPTotal.Inc()
|
tracer.eventsSSHTotal.Inc()
|
||||||
}
|
case TCP.String():
|
||||||
|
tracer.eventsTCPTotal.Inc()
|
||||||
|
}
|
||||||
|
tracer.eventsTotal.Inc()
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package tracer
|
package tracer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -10,7 +11,7 @@ import (
|
|||||||
func TestInit(t *testing.T) {
|
func TestInit(t *testing.T) {
|
||||||
mockStrategy := func(event Event) {}
|
mockStrategy := func(event Event) {}
|
||||||
|
|
||||||
tracer := Init(mockStrategy)
|
tracer := GetInstance(mockStrategy)
|
||||||
|
|
||||||
assert.NotNil(t, tracer.strategy)
|
assert.NotNil(t, tracer.strategy)
|
||||||
}
|
}
|
||||||
@ -25,7 +26,9 @@ func TestTraceEvent(t *testing.T) {
|
|||||||
eventCalled = event
|
eventCalled = event
|
||||||
}
|
}
|
||||||
|
|
||||||
tracer := Init(mockStrategy)
|
tracer := GetInstance(mockStrategy)
|
||||||
|
|
||||||
|
tracer.strategy = mockStrategy
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
tracer.TraceEvent(Event{
|
tracer.TraceEvent(Event{
|
||||||
@ -51,7 +54,7 @@ func TestSetStrategy(t *testing.T) {
|
|||||||
eventCalled = event
|
eventCalled = event
|
||||||
}
|
}
|
||||||
|
|
||||||
tracer := Init(mockStrategy)
|
tracer := GetInstance(mockStrategy)
|
||||||
|
|
||||||
tracer.setStrategy(mockStrategy)
|
tracer.setStrategy(mockStrategy)
|
||||||
|
|
||||||
@ -75,3 +78,42 @@ func TestStringStatus(t *testing.T) {
|
|||||||
assert.Equal(t, Stateless.String(), "Stateless")
|
assert.Equal(t, Stateless.String(), "Stateless")
|
||||||
assert.Equal(t, Interaction.String(), "Interaction")
|
assert.Equal(t, Interaction.String(), "Interaction")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockCounter struct {
|
||||||
|
prometheus.Metric
|
||||||
|
prometheus.Collector
|
||||||
|
inc func()
|
||||||
|
add func(float64)
|
||||||
|
}
|
||||||
|
|
||||||
|
var counter = 0
|
||||||
|
|
||||||
|
func (m mockCounter) Inc() {
|
||||||
|
counter += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockCounter) Add(f float64) {
|
||||||
|
counter = int(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdatePrometheusCounters(t *testing.T) {
|
||||||
|
mockStrategy := func(event Event) {}
|
||||||
|
|
||||||
|
tracer := &tracer{
|
||||||
|
strategy: mockStrategy,
|
||||||
|
eventsChan: make(chan Event, Workers),
|
||||||
|
eventsTotal: mockCounter{},
|
||||||
|
eventsSSHTotal: mockCounter{},
|
||||||
|
eventsTCPTotal: mockCounter{},
|
||||||
|
eventsHTTPTotal: mockCounter{},
|
||||||
|
}
|
||||||
|
|
||||||
|
tracer.updatePrometheusCounters(SSH.String())
|
||||||
|
assert.Equal(t, 2, counter)
|
||||||
|
|
||||||
|
tracer.updatePrometheusCounters(HTTP.String())
|
||||||
|
assert.Equal(t, 4, counter)
|
||||||
|
|
||||||
|
tracer.updatePrometheusCounters(TCP.String())
|
||||||
|
assert.Equal(t, 6, counter)
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user