mirror of
https://github.com/mariocandela/beelzebub.git
synced 2025-07-01 18:47:26 -04:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
a79937c5ae | |||
bdabbe9adc | |||
db0da03baa | |||
b062416c00 | |||
933f02911b | |||
ef07ca1203 | |||
1f59685530 | |||
f658a26b32 | |||
3fb8a667b3 | |||
8963bbc86d | |||
44ec44ea5c | |||
38297faed2 | |||
8703d1afda | |||
db804474d3 | |||
48dd70d523 | |||
4813685834 | |||
6f6acb212b | |||
99c7287c02 | |||
c3d2ff885d | |||
f1b35e9e43 | |||
d74499bb37 | |||
5bba406e6b | |||
ec931bd6f9 | |||
b7f7aa0170 | |||
79f9162f24 | |||
24828e503b | |||
acd5aa0e9c | |||
480b734834 | |||
7556c76652 | |||
11421665db | |||
cce0dcfa30 | |||
4740b2b6f5 | |||
27eb88e050 | |||
9eaa503def | |||
f07ce7d3be | |||
a924b2cb8b |
9
.github/workflows/codeql.yml
vendored
9
.github/workflows/codeql.yml
vendored
@ -26,8 +26,13 @@ jobs:
|
|||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.20.0
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
|
|
||||||
@ -35,6 +40,6 @@ jobs:
|
|||||||
run: go build ./...
|
run: go build ./...
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
with:
|
with:
|
||||||
category: "/language:${{matrix.language}}"
|
category: "/language:${{matrix.language}}"
|
||||||
|
21
.github/workflows/docker-image.yml
vendored
21
.github/workflows/docker-image.yml
vendored
@ -1,31 +1,32 @@
|
|||||||
|
---
|
||||||
name: Docker Hub Image
|
name: Docker Hub Image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- 'v*.*.*'
|
- 'v*.*.*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
CD:
|
CD:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Checkout
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
-
|
- name: Login to Docker Hub
|
||||||
name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
-
|
- name: Set up QEMU
|
||||||
name: Set up Docker Buildx
|
uses: docker/setup-qemu-action@v3
|
||||||
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v2
|
||||||
-
|
- name: Build and push
|
||||||
name: Build and push
|
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
push: true
|
push: true
|
||||||
tags: m4r10/beelzebub:${{ github.ref_name }}
|
tags: |
|
||||||
|
m4r10/beelzebub:${{ github.ref_name }}
|
||||||
|
m4r10/beelzebub:latest
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
32
README.md
32
README.md
@ -211,9 +211,9 @@ commands:
|
|||||||
|
|
||||||
#### Example SSH Honeypot
|
#### Example SSH Honeypot
|
||||||
|
|
||||||
###### Honeypot LLM Honeypots
|
###### LLM Honeypots
|
||||||
|
|
||||||
Example with OpenAI GPT-4:
|
Follow a SSH LLM Honeypot using OpenAI as provider LLM:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: "v1"
|
apiVersion: "v1"
|
||||||
@ -228,11 +228,12 @@ 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:
|
||||||
llmModel: "gpt4-o"
|
llmProvider: "openai"
|
||||||
|
llmModel: "gpt-4o" #Models https://platform.openai.com/docs/models
|
||||||
openAISecretKey: "sk-proj-123456"
|
openAISecretKey: "sk-proj-123456"
|
||||||
```
|
```
|
||||||
|
|
||||||
Example with Ollama Llama3:
|
Examples with local Ollama instance using model codellama:7b:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: "v1"
|
apiVersion: "v1"
|
||||||
@ -247,9 +248,30 @@ 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:
|
||||||
llmModel: "llama3"
|
llmProvider: "ollama"
|
||||||
|
llmModel: "codellama:7b" #Models https://ollama.com/search
|
||||||
host: "http://example.com/api/chat" #default http://localhost:11434/api/chat
|
host: "http://example.com/api/chat" #default http://localhost:11434/api/chat
|
||||||
```
|
```
|
||||||
|
Example with custom prompt:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: "v1"
|
||||||
|
protocol: "ssh"
|
||||||
|
address: ":2222"
|
||||||
|
description: "SSH interactive OpenAI GPT-4"
|
||||||
|
commands:
|
||||||
|
- regex: "^(.+)$"
|
||||||
|
plugin: "LLMHoneypot"
|
||||||
|
serverVersion: "OpenSSH"
|
||||||
|
serverName: "ubuntu"
|
||||||
|
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
|
||||||
|
deadlineTimeoutSeconds: 60
|
||||||
|
plugin:
|
||||||
|
llmProvider: "openai"
|
||||||
|
llmModel: "gpt-4o"
|
||||||
|
openAISecretKey: "sk-proj-123456"
|
||||||
|
prompt: "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."
|
||||||
|
```
|
||||||
|
|
||||||
###### SSH Honeypot on Port 22
|
###### SSH Honeypot on Port 22
|
||||||
|
|
||||||
|
@ -3,15 +3,18 @@ package builder
|
|||||||
import (
|
import (
|
||||||
"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"
|
||||||
|
|
||||||
|
"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/HTTP"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/protocols/strategies/SSH"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/protocols/strategies/TCP"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
amqp "github.com/rabbitmq/amqp091-go"
|
amqp "github.com/rabbitmq/amqp091-go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -106,9 +109,9 @@ Honeypot Framework, happy hacking!`)
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// Init Protocol strategies
|
// Init Protocol strategies
|
||||||
secureShellStrategy := &strategies.SSHStrategy{}
|
secureShellStrategy := &SSH.SSHStrategy{}
|
||||||
hypertextTransferProtocolStrategy := &strategies.HTTPStrategy{}
|
hypertextTransferProtocolStrategy := &HTTP.HTTPStrategy{}
|
||||||
transmissionControlProtocolStrategy := &strategies.TCPStrategy{}
|
transmissionControlProtocolStrategy := &TCP.TCPStrategy{}
|
||||||
|
|
||||||
// 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)
|
||||||
@ -122,7 +125,7 @@ Honeypot Framework, happy hacking!`)
|
|||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
if len(honeypotsConfiguration) == 0 {
|
if len(honeypotsConfiguration) == 0 {
|
||||||
return errors.New("No honeypots configuration found")
|
return errors.New("no honeypots configuration found")
|
||||||
}
|
}
|
||||||
b.beelzebubServicesConfiguration = honeypotsConfiguration
|
b.beelzebubServicesConfiguration = honeypotsConfiguration
|
||||||
}
|
}
|
||||||
@ -132,20 +135,16 @@ Honeypot Framework, happy hacking!`)
|
|||||||
switch beelzebubServiceConfiguration.Protocol {
|
switch beelzebubServiceConfiguration.Protocol {
|
||||||
case "http":
|
case "http":
|
||||||
protocolManager.SetProtocolStrategy(hypertextTransferProtocolStrategy)
|
protocolManager.SetProtocolStrategy(hypertextTransferProtocolStrategy)
|
||||||
break
|
|
||||||
case "ssh":
|
case "ssh":
|
||||||
protocolManager.SetProtocolStrategy(secureShellStrategy)
|
protocolManager.SetProtocolStrategy(secureShellStrategy)
|
||||||
break
|
|
||||||
case "tcp":
|
case "tcp":
|
||||||
protocolManager.SetProtocolStrategy(transmissionControlProtocolStrategy)
|
protocolManager.SetProtocolStrategy(transmissionControlProtocolStrategy)
|
||||||
break
|
|
||||||
default:
|
default:
|
||||||
log.Fatalf("Protocol %s not managed", beelzebubServiceConfiguration.Protocol)
|
log.Fatalf("protocol %s not managed", beelzebubServiceConfiguration.Protocol)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := protocolManager.InitService(beelzebubServiceConfiguration); err != nil {
|
if err := protocolManager.InitService(beelzebubServiceConfiguration); err != nil {
|
||||||
return errors.New(fmt.Sprintf("Error during init protocol: %s, %s", beelzebubServiceConfiguration.Protocol, err.Error()))
|
return fmt.Errorf("error during init protocol: %s, %s", beelzebubServiceConfiguration.Protocol, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,5 +22,6 @@ commands:
|
|||||||
plugin: "LLMHoneypot"
|
plugin: "LLMHoneypot"
|
||||||
statusCode: 200
|
statusCode: 200
|
||||||
plugin:
|
plugin:
|
||||||
llmModel: "gpt4-o"
|
llmProvider: "openai"
|
||||||
openAISecretKey: "sk-proj-123456"
|
llmModel: "gpt-4o"
|
||||||
|
openAISecretKey: "sk-proj-123456"
|
||||||
|
@ -7,7 +7,9 @@ commands:
|
|||||||
plugin: "LLMHoneypot"
|
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|1234)$"
|
||||||
deadlineTimeoutSeconds: 6000
|
deadlineTimeoutSeconds: 6000
|
||||||
plugin:
|
plugin:
|
||||||
llmModel: "llama3"
|
llmProvider: "openai"
|
||||||
|
llmModel: "gpt-4o"
|
||||||
|
openAISecretKey: "sk-proj-12345"
|
@ -3,18 +3,18 @@ version: "3.9"
|
|||||||
services:
|
services:
|
||||||
beelzebub:
|
beelzebub:
|
||||||
build: .
|
build: .
|
||||||
#network_mode: host # Not work on Mac OS
|
|
||||||
container_name: beelzebub
|
container_name: beelzebub
|
||||||
restart: always
|
restart: always
|
||||||
ports: # Remove me, if you use configuration network_mode: host
|
ports:
|
||||||
- "22:22"
|
- "22:22"
|
||||||
- "2222:2222"
|
- "2222:2222"
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
- "8081:8081"
|
- "8081:8081"
|
||||||
- "80:80"
|
- "80:80"
|
||||||
- "3306:3306"
|
- "3306:3306"
|
||||||
- "2112:2112" # Prometheus openmetrics
|
- "2112:2112" #Prometheus Open Metrics
|
||||||
environment:
|
environment:
|
||||||
RABBITMQ_URI: ${RABBITMQ_URI}
|
RABBITMQ_URI: ${RABBITMQ_URI}
|
||||||
|
OPEN_AI_SECRET_KEY: ${OPEN_AI_SECRET_KEY}
|
||||||
volumes:
|
volumes:
|
||||||
- "./configurations:/configurations"
|
- "./configurations:/configurations"
|
16
go.mod
16
go.mod
@ -3,16 +3,17 @@ module github.com/mariocandela/beelzebub/v3
|
|||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gliderlabs/ssh v0.3.7
|
github.com/gliderlabs/ssh v0.3.8
|
||||||
github.com/go-resty/resty/v2 v2.14.0
|
github.com/go-resty/resty/v2 v2.16.5
|
||||||
github.com/google/uuid v1.6.0
|
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.20.3
|
github.com/prometheus/client_golang v1.20.5
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0
|
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.9.0
|
github.com/stretchr/testify v1.10.0
|
||||||
golang.org/x/crypto v0.27.0
|
golang.org/x/crypto v0.35.0
|
||||||
|
golang.org/x/term v0.30.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,8 +32,7 @@ require (
|
|||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.55.0 // indirect
|
github.com/prometheus/common v0.55.0 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
golang.org/x/net v0.27.0 // indirect
|
golang.org/x/net v0.36.0 // indirect
|
||||||
golang.org/x/sys v0.25.0 // indirect
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
golang.org/x/term v0.24.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
)
|
)
|
||||||
|
69
go.sum
69
go.sum
@ -8,12 +8,11 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
|||||||
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.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||||
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||||
github.com/go-resty/resty/v2 v2.14.0 h1:/rhkzsAqGQkozwfKS5aFAbb6TyKd3zyFRWcdRXLPCAU=
|
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
||||||
github.com/go-resty/resty/v2 v2.14.0/go.mod h1:IW6mekUOsElt9C7oWr0XRt9BNSD6D5rr9mhk6NjmNHg=
|
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/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=
|
||||||
@ -37,8 +36,8 @@ 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.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4=
|
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||||
github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||||
@ -52,42 +51,26 @@ 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/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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
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.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
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.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/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
|
||||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
|
||||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
|
||||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
|
||||||
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/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
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.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.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
|
||||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
|
||||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
|
||||||
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/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
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=
|
||||||
@ -97,42 +80,22 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
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.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.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
|
||||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
|
||||||
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.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.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
|
||||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
|
||||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
|
||||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
|
||||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
|
||||||
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/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
|
||||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
|
||||||
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/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
|
||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
|
||||||
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=
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
|
47
historystore/history_store.go
Normal file
47
historystore/history_store.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package historystore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/mariocandela/beelzebub/v3/plugins"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HistoryStore is a thread-safe structure for storing Messages used to build LLM Context.
|
||||||
|
type HistoryStore struct {
|
||||||
|
sync.RWMutex
|
||||||
|
sessions map[string][]plugins.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHistoryStore returns a prepared HistoryStore
|
||||||
|
func NewHistoryStore() *HistoryStore {
|
||||||
|
return &HistoryStore{
|
||||||
|
sessions: make(map[string][]plugins.Message),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasKey returns true if the supplied key exists in the map.
|
||||||
|
func (hs *HistoryStore) HasKey(key string) bool {
|
||||||
|
hs.RLock()
|
||||||
|
defer hs.RUnlock()
|
||||||
|
_, ok := hs.sessions[key]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns the value stored at the map
|
||||||
|
func (hs *HistoryStore) Query(key string) []plugins.Message {
|
||||||
|
hs.RLock()
|
||||||
|
defer hs.RUnlock()
|
||||||
|
return hs.sessions[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append will add the slice of Mesages to the entry for the key.
|
||||||
|
// If the map has not yet been initalised, then a new map is created.
|
||||||
|
func (hs *HistoryStore) Append(key string, message ...plugins.Message) {
|
||||||
|
hs.Lock()
|
||||||
|
defer hs.Unlock()
|
||||||
|
// In the unexpected case that the map has not yet been initalised, create it.
|
||||||
|
if hs.sessions == nil {
|
||||||
|
hs.sessions = make(map[string][]plugins.Message)
|
||||||
|
}
|
||||||
|
hs.sessions[key] = append(hs.sessions[key], message...)
|
||||||
|
}
|
47
historystore/history_store_test.go
Normal file
47
historystore/history_store_test.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package historystore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mariocandela/beelzebub/v3/plugins"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewHistoryStore(t *testing.T) {
|
||||||
|
hs := NewHistoryStore()
|
||||||
|
assert.NotNil(t, hs)
|
||||||
|
assert.NotNil(t, hs.sessions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasKey(t *testing.T) {
|
||||||
|
hs := NewHistoryStore()
|
||||||
|
hs.sessions["testKey"] = []plugins.Message{}
|
||||||
|
assert.True(t, hs.HasKey("testKey"))
|
||||||
|
assert.False(t, hs.HasKey("nonExistentKey"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQuery(t *testing.T) {
|
||||||
|
hs := NewHistoryStore()
|
||||||
|
expectedMessages := []plugins.Message{{Role: "user", Content: "Hello"}}
|
||||||
|
hs.sessions["testKey"] = expectedMessages
|
||||||
|
actualMessages := hs.Query("testKey")
|
||||||
|
assert.Equal(t, expectedMessages, actualMessages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppend(t *testing.T) {
|
||||||
|
hs := NewHistoryStore()
|
||||||
|
message1 := plugins.Message{Role: "user", Content: "Hello"}
|
||||||
|
message2 := plugins.Message{Role: "assistant", Content: "Hi"}
|
||||||
|
hs.Append("testKey", message1)
|
||||||
|
assert.Equal(t, []plugins.Message{message1}, hs.sessions["testKey"])
|
||||||
|
hs.Append("testKey", message2)
|
||||||
|
assert.Equal(t, []plugins.Message{message1, message2}, hs.sessions["testKey"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendNilSessions(t *testing.T) {
|
||||||
|
hs := &HistoryStore{}
|
||||||
|
message1 := plugins.Message{Role: "user", Content: "Hello"}
|
||||||
|
hs.Append("testKey", message1)
|
||||||
|
assert.NotNil(t, hs.sessions)
|
||||||
|
assert.Equal(t, []plugins.Message{message1}, hs.sessions["testKey"])
|
||||||
|
}
|
@ -67,8 +67,11 @@ func (suite *IntegrationTestSuite) TestInvokeHTTPHoneypot() {
|
|||||||
response, err := resty.New().R().
|
response, err := resty.New().R().
|
||||||
Get(suite.httpHoneypotHost + "/index.php")
|
Get(suite.httpHoneypotHost + "/index.php")
|
||||||
|
|
||||||
|
response.Header().Del("Date")
|
||||||
|
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
suite.Equal(http.StatusOK, response.StatusCode())
|
suite.Equal(http.StatusOK, response.StatusCode())
|
||||||
|
suite.Equal(http.Header{"Content-Length": []string{"15"}, "Content-Type": []string{"text/html"}, "Server": []string{"Apache/2.4.53 (Debian)"}, "X-Powered-By": []string{"PHP/7.4.29"}}, response.Header())
|
||||||
suite.Equal("mocked response", string(response.Body()))
|
suite.Equal("mocked response", string(response.Body()))
|
||||||
|
|
||||||
response, err = resty.New().R().
|
response, err = resty.New().R().
|
||||||
|
@ -52,6 +52,8 @@ type Plugin struct {
|
|||||||
OpenAISecretKey string `yaml:"openAISecretKey"`
|
OpenAISecretKey string `yaml:"openAISecretKey"`
|
||||||
Host string `yaml:"host"`
|
Host string `yaml:"host"`
|
||||||
LLMModel string `yaml:"llmModel"`
|
LLMModel string `yaml:"llmModel"`
|
||||||
|
LLMProvider string `yaml:"llmProvider"`
|
||||||
|
Prompt string `yaml:"prompt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeelzebubServiceConfiguration is the struct that contains the configurations of the honeypot service
|
// BeelzebubServiceConfiguration is the struct that contains the configurations of the honeypot service
|
||||||
@ -60,6 +62,7 @@ type BeelzebubServiceConfiguration struct {
|
|||||||
Protocol string `yaml:"protocol"`
|
Protocol string `yaml:"protocol"`
|
||||||
Address string `yaml:"address"`
|
Address string `yaml:"address"`
|
||||||
Commands []Command `yaml:"commands"`
|
Commands []Command `yaml:"commands"`
|
||||||
|
FallbackCommand Command `yaml:"fallbackCommand"`
|
||||||
ServerVersion string `yaml:"serverVersion"`
|
ServerVersion string `yaml:"serverVersion"`
|
||||||
ServerName string `yaml:"serverName"`
|
ServerName string `yaml:"serverName"`
|
||||||
DeadlineTimeoutSeconds int `yaml:"deadlineTimeoutSeconds"`
|
DeadlineTimeoutSeconds int `yaml:"deadlineTimeoutSeconds"`
|
||||||
@ -67,6 +70,8 @@ type BeelzebubServiceConfiguration struct {
|
|||||||
Description string `yaml:"description"`
|
Description string `yaml:"description"`
|
||||||
Banner string `yaml:"banner"`
|
Banner string `yaml:"banner"`
|
||||||
Plugin Plugin `yaml:"plugin"`
|
Plugin Plugin `yaml:"plugin"`
|
||||||
|
TLSCertPath string `yaml:"tlsCertPath"`
|
||||||
|
TLSKeyPath string `yaml:"tlsKeyPath"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command is the struct that contains the configurations of the commands
|
// Command is the struct that contains the configurations of the commands
|
||||||
|
@ -49,15 +49,22 @@ func mockReadfilebytesBeelzebubServiceConfiguration(filePath string) ([]byte, er
|
|||||||
apiVersion: "v1"
|
apiVersion: "v1"
|
||||||
protocol: "http"
|
protocol: "http"
|
||||||
address: ":8080"
|
address: ":8080"
|
||||||
|
tlsCertPath: "/tmp/cert.crt"
|
||||||
|
tlsKeyPath: "/tmp/cert.key"
|
||||||
commands:
|
commands:
|
||||||
- regex: "wp-admin"
|
- regex: "wp-admin"
|
||||||
handler: "login"
|
handler: "login"
|
||||||
headers:
|
headers:
|
||||||
- "Content-Type: text/html"
|
- "Content-Type: text/html"
|
||||||
|
fallbackCommand:
|
||||||
|
handler: "404 Not Found!"
|
||||||
|
statusCode: 404
|
||||||
plugin:
|
plugin:
|
||||||
openAISecretKey: "qwerty"
|
openAISecretKey: "qwerty"
|
||||||
llmModel: "llama3"
|
llmModel: "llama3"
|
||||||
|
llmProvider: "ollama"
|
||||||
host: "localhost:1563"
|
host: "localhost:1563"
|
||||||
|
prompt: "hello world"
|
||||||
`)
|
`)
|
||||||
return beelzebubServiceConfiguration, nil
|
return beelzebubServiceConfiguration, nil
|
||||||
}
|
}
|
||||||
@ -130,9 +137,15 @@ 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.FallbackCommand.Handler, "404 Not Found!")
|
||||||
|
assert.Equal(t, firstBeelzebubServiceConfiguration.FallbackCommand.StatusCode, 404)
|
||||||
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.OpenAISecretKey, "qwerty")
|
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.OpenAISecretKey, "qwerty")
|
||||||
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.LLMModel, "llama3")
|
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.LLMModel, "llama3")
|
||||||
|
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.LLMProvider, "ollama")
|
||||||
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.Host, "localhost:1563")
|
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.Host, "localhost:1563")
|
||||||
|
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.Prompt, "hello world")
|
||||||
|
assert.Equal(t, firstBeelzebubServiceConfiguration.TLSCertPath, "/tmp/cert.crt")
|
||||||
|
assert.Equal(t, firstBeelzebubServiceConfiguration.TLSKeyPath, "/tmp/cert.key")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGelAllFilesNameByDirName(t *testing.T) {
|
func TestGelAllFilesNameByDirName(t *testing.T) {
|
||||||
@ -168,7 +181,8 @@ func TestGelAllFilesNameByDirNameError(t *testing.T) {
|
|||||||
files, err := gelAllFilesNameByDirName("nosuchfile")
|
files, err := gelAllFilesNameByDirName("nosuchfile")
|
||||||
|
|
||||||
assert.Nil(t, files)
|
assert.Nil(t, files)
|
||||||
assert.Equal(t, "open nosuchfile: no such file or directory", err.Error())
|
// Windows and Linux return slightly different error strings, but share a common prefix, so check for that.
|
||||||
|
assert.Contains(t, err.Error(), "open nosuchfile: ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadFileBytesByFilePath(t *testing.T) {
|
func TestReadFileBytesByFilePath(t *testing.T) {
|
||||||
|
@ -85,7 +85,7 @@ func TestGetHoneypotsConfigurationsWithResults(t *testing.T) {
|
|||||||
resp, err := httpmock.NewJsonResponse(200, &[]HoneypotConfigResponseDTO{
|
resp, err := httpmock.NewJsonResponse(200, &[]HoneypotConfigResponseDTO{
|
||||||
{
|
{
|
||||||
ID: "123456",
|
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",
|
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: \"gpt-4o\"\n openAISecretKey: \"1234\"\n",
|
||||||
TokenID: "1234567",
|
TokenID: "1234567",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -120,7 +120,7 @@ func TestGetHoneypotsConfigurationsWithResults(t *testing.T) {
|
|||||||
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: parser.Plugin{
|
Plugin: parser.Plugin{
|
||||||
LLMModel: "gpt4-o",
|
LLMModel: "gpt-4o",
|
||||||
OpenAISecretKey: "1234",
|
OpenAISecretKey: "1234",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -6,25 +6,29 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/mariocandela/beelzebub/v3/tracer"
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
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"
|
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 note. 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."
|
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"
|
LLMPluginName = "LLMHoneypot"
|
||||||
openAIGPTEndpoint = "https://api.openai.com/v1/chat/completions"
|
openAIEndpoint = "https://api.openai.com/v1/chat/completions"
|
||||||
ollamaEndpoint = "http://localhost:11434/api/chat"
|
ollamaEndpoint = "http://localhost:11434/api/chat"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LLMHoneypot struct {
|
type LLMHoneypot struct {
|
||||||
Histories []Message
|
Histories []Message
|
||||||
OpenAIKey string
|
OpenAIKey string
|
||||||
client *resty.Client
|
client *resty.Client
|
||||||
Protocol tracer.Protocol
|
Protocol tracer.Protocol
|
||||||
Model LLMModel
|
Provider LLMProvider
|
||||||
Host string
|
Model string
|
||||||
|
Host string
|
||||||
|
CustomPrompt string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Choice struct {
|
type Choice struct {
|
||||||
@ -70,21 +74,21 @@ func (role Role) String() string {
|
|||||||
return [...]string{"system", "user", "assistant"}[role]
|
return [...]string{"system", "user", "assistant"}[role]
|
||||||
}
|
}
|
||||||
|
|
||||||
type LLMModel int
|
type LLMProvider int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LLAMA3 LLMModel = iota
|
Ollama LLMProvider = iota
|
||||||
GPT4O
|
OpenAI
|
||||||
)
|
)
|
||||||
|
|
||||||
func FromStringToLLMModel(llmModel string) (LLMModel, error) {
|
func FromStringToLLMProvider(llmProvider string) (LLMProvider, error) {
|
||||||
switch llmModel {
|
switch strings.ToLower(llmProvider) {
|
||||||
case "llama3":
|
case "ollama":
|
||||||
return LLAMA3, nil
|
return Ollama, nil
|
||||||
case "gpt4-o":
|
case "openai":
|
||||||
return GPT4O, nil
|
return OpenAI, nil
|
||||||
default:
|
default:
|
||||||
return -1, fmt.Errorf("model %s not found", llmModel)
|
return -1, fmt.Errorf("provider %s not found, valid providers: ollama, openai", llmProvider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,17 +96,26 @@ func InitLLMHoneypot(config LLMHoneypot) *LLMHoneypot {
|
|||||||
// Inject the dependencies
|
// Inject the dependencies
|
||||||
config.client = resty.New()
|
config.client = resty.New()
|
||||||
|
|
||||||
|
if os.Getenv("OPEN_AI_SECRET_KEY") != "" {
|
||||||
|
config.OpenAIKey = os.Getenv("OPEN_AI_SECRET_KEY")
|
||||||
|
}
|
||||||
|
|
||||||
return &config
|
return &config
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildPrompt(histories []Message, protocol tracer.Protocol, command string) ([]Message, error) {
|
func (llmHoneypot *LLMHoneypot) buildPrompt(command string) ([]Message, error) {
|
||||||
var messages []Message
|
var messages []Message
|
||||||
|
var prompt string
|
||||||
|
|
||||||
switch protocol {
|
switch llmHoneypot.Protocol {
|
||||||
case tracer.SSH:
|
case tracer.SSH:
|
||||||
|
prompt = systemPromptVirtualizeLinuxTerminal
|
||||||
|
if llmHoneypot.CustomPrompt != "" {
|
||||||
|
prompt = llmHoneypot.CustomPrompt
|
||||||
|
}
|
||||||
messages = append(messages, Message{
|
messages = append(messages, Message{
|
||||||
Role: SYSTEM.String(),
|
Role: SYSTEM.String(),
|
||||||
Content: systemPromptVirtualizeLinuxTerminal,
|
Content: prompt,
|
||||||
})
|
})
|
||||||
messages = append(messages, Message{
|
messages = append(messages, Message{
|
||||||
Role: USER.String(),
|
Role: USER.String(),
|
||||||
@ -112,13 +125,17 @@ func buildPrompt(histories []Message, protocol tracer.Protocol, command string)
|
|||||||
Role: ASSISTANT.String(),
|
Role: ASSISTANT.String(),
|
||||||
Content: "/home/user",
|
Content: "/home/user",
|
||||||
})
|
})
|
||||||
for _, history := range histories {
|
for _, history := range llmHoneypot.Histories {
|
||||||
messages = append(messages, history)
|
messages = append(messages, history)
|
||||||
}
|
}
|
||||||
case tracer.HTTP:
|
case tracer.HTTP:
|
||||||
|
prompt = systemPromptVirtualizeHTTPServer
|
||||||
|
if llmHoneypot.CustomPrompt != "" {
|
||||||
|
prompt = llmHoneypot.CustomPrompt
|
||||||
|
}
|
||||||
messages = append(messages, Message{
|
messages = append(messages, Message{
|
||||||
Role: SYSTEM.String(),
|
Role: SYSTEM.String(),
|
||||||
Content: systemPromptVirtualizeHTTPServer,
|
Content: prompt,
|
||||||
})
|
})
|
||||||
messages = append(messages, Message{
|
messages = append(messages, Message{
|
||||||
Role: USER.String(),
|
Role: USER.String(),
|
||||||
@ -143,7 +160,7 @@ func (llmHoneypot *LLMHoneypot) openAICaller(messages []Message) (string, error)
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
requestJson, err := json.Marshal(Request{
|
requestJson, err := json.Marshal(Request{
|
||||||
Model: "gpt-4o",
|
Model: llmHoneypot.Model,
|
||||||
Messages: messages,
|
Messages: messages,
|
||||||
Stream: false,
|
Stream: false,
|
||||||
})
|
})
|
||||||
@ -156,7 +173,7 @@ func (llmHoneypot *LLMHoneypot) openAICaller(messages []Message) (string, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if llmHoneypot.Host == "" {
|
if llmHoneypot.Host == "" {
|
||||||
llmHoneypot.Host = openAIGPTEndpoint
|
llmHoneypot.Host = openAIEndpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug(string(requestJson))
|
log.Debug(string(requestJson))
|
||||||
@ -175,14 +192,14 @@ func (llmHoneypot *LLMHoneypot) openAICaller(messages []Message) (string, error)
|
|||||||
return "", errors.New("no choices")
|
return "", errors.New("no choices")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Result().(*Response).Choices[0].Message.Content, nil
|
return removeQuotes(response.Result().(*Response).Choices[0].Message.Content), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (llmHoneypot *LLMHoneypot) ollamaCaller(messages []Message) (string, error) {
|
func (llmHoneypot *LLMHoneypot) ollamaCaller(messages []Message) (string, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
requestJson, err := json.Marshal(Request{
|
requestJson, err := json.Marshal(Request{
|
||||||
Model: "llama3",
|
Model: llmHoneypot.Model,
|
||||||
Messages: messages,
|
Messages: messages,
|
||||||
Stream: false,
|
Stream: false,
|
||||||
})
|
})
|
||||||
@ -206,24 +223,30 @@ func (llmHoneypot *LLMHoneypot) ollamaCaller(messages []Message) (string, error)
|
|||||||
}
|
}
|
||||||
log.Debug(response)
|
log.Debug(response)
|
||||||
|
|
||||||
return response.Result().(*Response).Message.Content, nil
|
return removeQuotes(response.Result().(*Response).Message.Content), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (llmHoneypot *LLMHoneypot) ExecuteModel(command string) (string, error) {
|
func (llmHoneypot *LLMHoneypot) ExecuteModel(command string) (string, error) {
|
||||||
var err error
|
var err error
|
||||||
|
var prompt []Message
|
||||||
|
|
||||||
prompt, err := buildPrompt(llmHoneypot.Histories, llmHoneypot.Protocol, command)
|
prompt, err = llmHoneypot.buildPrompt(command)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch llmHoneypot.Model {
|
switch llmHoneypot.Provider {
|
||||||
case LLAMA3:
|
case Ollama:
|
||||||
return llmHoneypot.ollamaCaller(prompt)
|
return llmHoneypot.ollamaCaller(prompt)
|
||||||
case GPT4O:
|
case OpenAI:
|
||||||
return llmHoneypot.openAICaller(prompt)
|
return llmHoneypot.openAICaller(prompt)
|
||||||
default:
|
default:
|
||||||
return "", errors.New("no model selected")
|
return "", fmt.Errorf("provider %d not found, valid providers: ollama, openai", llmHoneypot.Provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeQuotes(content string) string {
|
||||||
|
regex := regexp.MustCompile("(```( *)?([a-z]*)?(\\n)?)")
|
||||||
|
return regex.ReplaceAllString(content, "")
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/mariocandela/beelzebub/v3/tracer"
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,8 +17,13 @@ func TestBuildPromptEmptyHistory(t *testing.T) {
|
|||||||
var histories []Message
|
var histories []Message
|
||||||
command := "pwd"
|
command := "pwd"
|
||||||
|
|
||||||
|
honeypot := LLMHoneypot{
|
||||||
|
Histories: histories,
|
||||||
|
Protocol: tracer.SSH,
|
||||||
|
}
|
||||||
|
|
||||||
//When
|
//When
|
||||||
prompt, err := buildPrompt(histories, tracer.SSH, command)
|
prompt, err := honeypot.buildPrompt(command)
|
||||||
|
|
||||||
//Then
|
//Then
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
@ -35,21 +41,53 @@ func TestBuildPromptWithHistory(t *testing.T) {
|
|||||||
|
|
||||||
command := "pwd"
|
command := "pwd"
|
||||||
|
|
||||||
|
honeypot := LLMHoneypot{
|
||||||
|
Histories: histories,
|
||||||
|
Protocol: tracer.SSH,
|
||||||
|
}
|
||||||
|
|
||||||
//When
|
//When
|
||||||
prompt, err := buildPrompt(histories, tracer.SSH, command)
|
prompt, err := honeypot.buildPrompt(command)
|
||||||
|
|
||||||
//Then
|
//Then
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, SystemPromptLen+1, len(prompt))
|
assert.Equal(t, SystemPromptLen+1, len(prompt))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildPromptWithCustomPrompt(t *testing.T) {
|
||||||
|
//Given
|
||||||
|
var histories = []Message{
|
||||||
|
{
|
||||||
|
Role: "cat hello.txt",
|
||||||
|
Content: "world",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
command := "pwd"
|
||||||
|
|
||||||
|
honeypot := LLMHoneypot{
|
||||||
|
Histories: histories,
|
||||||
|
Protocol: tracer.SSH,
|
||||||
|
CustomPrompt: "act as calculator",
|
||||||
|
}
|
||||||
|
|
||||||
|
//When
|
||||||
|
prompt, err := honeypot.buildPrompt(command)
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, prompt[0].Content, "act as calculator")
|
||||||
|
assert.Equal(t, prompt[0].Role, SYSTEM.String())
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildExecuteModelFailValidation(t *testing.T) {
|
func TestBuildExecuteModelFailValidation(t *testing.T) {
|
||||||
|
|
||||||
llmHoneypot := LLMHoneypot{
|
llmHoneypot := LLMHoneypot{
|
||||||
Histories: make([]Message, 0),
|
Histories: make([]Message, 0),
|
||||||
OpenAIKey: "",
|
OpenAIKey: "",
|
||||||
Protocol: tracer.SSH,
|
Protocol: tracer.SSH,
|
||||||
Model: GPT4O,
|
Model: "gpt-4o",
|
||||||
|
Provider: OpenAI,
|
||||||
}
|
}
|
||||||
|
|
||||||
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
@ -59,13 +97,78 @@ func TestBuildExecuteModelFailValidation(t *testing.T) {
|
|||||||
assert.Equal(t, "openAIKey is empty", err.Error())
|
assert.Equal(t, "openAIKey is empty", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildExecuteModelOpenAISecretKeyFromEnv(t *testing.T) {
|
||||||
|
|
||||||
|
llmHoneypot := LLMHoneypot{
|
||||||
|
Histories: make([]Message, 0),
|
||||||
|
OpenAIKey: "",
|
||||||
|
Protocol: tracer.SSH,
|
||||||
|
Model: "gpt-4o",
|
||||||
|
Provider: OpenAI,
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("OPEN_AI_SECRET_KEY", "sdjdnklfjndslkjanfk")
|
||||||
|
|
||||||
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
|
|
||||||
|
assert.Equal(t, "sdjdnklfjndslkjanfk", openAIGPTVirtualTerminal.OpenAIKey)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildExecuteModelWithCustomPrompt(t *testing.T) {
|
||||||
|
client := resty.New()
|
||||||
|
httpmock.ActivateNonDefault(client.GetClient())
|
||||||
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
|
// Given
|
||||||
|
httpmock.RegisterMatcherResponder("POST", openAIEndpoint,
|
||||||
|
httpmock.BodyContainsString("hello world"),
|
||||||
|
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: "gpt-4o",
|
||||||
|
Provider: OpenAI,
|
||||||
|
CustomPrompt: "hello world",
|
||||||
|
}
|
||||||
|
|
||||||
|
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 TestBuildExecuteModelFailValidationStrategyType(t *testing.T) {
|
func TestBuildExecuteModelFailValidationStrategyType(t *testing.T) {
|
||||||
|
|
||||||
llmHoneypot := LLMHoneypot{
|
llmHoneypot := LLMHoneypot{
|
||||||
Histories: make([]Message, 0),
|
Histories: make([]Message, 0),
|
||||||
OpenAIKey: "",
|
OpenAIKey: "",
|
||||||
Protocol: tracer.TCP,
|
Protocol: tracer.TCP,
|
||||||
Model: GPT4O,
|
Model: "gpt-4o",
|
||||||
|
Provider: OpenAI,
|
||||||
}
|
}
|
||||||
|
|
||||||
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
@ -80,7 +183,8 @@ func TestBuildExecuteModelFailValidationModelType(t *testing.T) {
|
|||||||
llmHoneypot := LLMHoneypot{
|
llmHoneypot := LLMHoneypot{
|
||||||
Histories: make([]Message, 0),
|
Histories: make([]Message, 0),
|
||||||
Protocol: tracer.SSH,
|
Protocol: tracer.SSH,
|
||||||
Model: 5,
|
Model: "llama3",
|
||||||
|
Provider: 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
@ -98,7 +202,7 @@ func TestBuildExecuteModelSSHWithResultsOpenAI(t *testing.T) {
|
|||||||
defer httpmock.DeactivateAndReset()
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
// Given
|
// Given
|
||||||
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
|
httpmock.RegisterResponder("POST", openAIEndpoint,
|
||||||
func(req *http.Request) (*http.Response, error) {
|
func(req *http.Request) (*http.Response, error) {
|
||||||
resp, err := httpmock.NewJsonResponse(200, &Response{
|
resp, err := httpmock.NewJsonResponse(200, &Response{
|
||||||
Choices: []Choice{
|
Choices: []Choice{
|
||||||
@ -121,7 +225,8 @@ func TestBuildExecuteModelSSHWithResultsOpenAI(t *testing.T) {
|
|||||||
Histories: make([]Message, 0),
|
Histories: make([]Message, 0),
|
||||||
OpenAIKey: "sdjdnklfjndslkjanfk",
|
OpenAIKey: "sdjdnklfjndslkjanfk",
|
||||||
Protocol: tracer.SSH,
|
Protocol: tracer.SSH,
|
||||||
Model: GPT4O,
|
Model: "gpt-4o",
|
||||||
|
Provider: OpenAI,
|
||||||
}
|
}
|
||||||
|
|
||||||
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
@ -159,7 +264,8 @@ func TestBuildExecuteModelSSHWithResultsLLama(t *testing.T) {
|
|||||||
llmHoneypot := LLMHoneypot{
|
llmHoneypot := LLMHoneypot{
|
||||||
Histories: make([]Message, 0),
|
Histories: make([]Message, 0),
|
||||||
Protocol: tracer.SSH,
|
Protocol: tracer.SSH,
|
||||||
Model: LLAMA3,
|
Model: "llama3",
|
||||||
|
Provider: Ollama,
|
||||||
}
|
}
|
||||||
|
|
||||||
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
@ -179,7 +285,7 @@ func TestBuildExecuteModelSSHWithoutResults(t *testing.T) {
|
|||||||
defer httpmock.DeactivateAndReset()
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
// Given
|
// Given
|
||||||
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
|
httpmock.RegisterResponder("POST", openAIEndpoint,
|
||||||
func(req *http.Request) (*http.Response, error) {
|
func(req *http.Request) (*http.Response, error) {
|
||||||
resp, err := httpmock.NewJsonResponse(200, &Response{
|
resp, err := httpmock.NewJsonResponse(200, &Response{
|
||||||
Choices: []Choice{},
|
Choices: []Choice{},
|
||||||
@ -195,7 +301,8 @@ func TestBuildExecuteModelSSHWithoutResults(t *testing.T) {
|
|||||||
Histories: make([]Message, 0),
|
Histories: make([]Message, 0),
|
||||||
OpenAIKey: "sdjdnklfjndslkjanfk",
|
OpenAIKey: "sdjdnklfjndslkjanfk",
|
||||||
Protocol: tracer.SSH,
|
Protocol: tracer.SSH,
|
||||||
Model: GPT4O,
|
Model: "gpt-4o",
|
||||||
|
Provider: OpenAI,
|
||||||
}
|
}
|
||||||
|
|
||||||
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
@ -214,7 +321,7 @@ func TestBuildExecuteModelHTTPWithResults(t *testing.T) {
|
|||||||
defer httpmock.DeactivateAndReset()
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
// Given
|
// Given
|
||||||
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
|
httpmock.RegisterResponder("POST", openAIEndpoint,
|
||||||
func(req *http.Request) (*http.Response, error) {
|
func(req *http.Request) (*http.Response, error) {
|
||||||
resp, err := httpmock.NewJsonResponse(200, &Response{
|
resp, err := httpmock.NewJsonResponse(200, &Response{
|
||||||
Choices: []Choice{
|
Choices: []Choice{
|
||||||
@ -237,7 +344,8 @@ func TestBuildExecuteModelHTTPWithResults(t *testing.T) {
|
|||||||
Histories: make([]Message, 0),
|
Histories: make([]Message, 0),
|
||||||
OpenAIKey: "sdjdnklfjndslkjanfk",
|
OpenAIKey: "sdjdnklfjndslkjanfk",
|
||||||
Protocol: tracer.HTTP,
|
Protocol: tracer.HTTP,
|
||||||
Model: GPT4O,
|
Model: "gpt-4o",
|
||||||
|
Provider: OpenAI,
|
||||||
}
|
}
|
||||||
|
|
||||||
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
@ -257,7 +365,7 @@ func TestBuildExecuteModelHTTPWithoutResults(t *testing.T) {
|
|||||||
defer httpmock.DeactivateAndReset()
|
defer httpmock.DeactivateAndReset()
|
||||||
|
|
||||||
// Given
|
// Given
|
||||||
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
|
httpmock.RegisterResponder("POST", openAIEndpoint,
|
||||||
func(req *http.Request) (*http.Response, error) {
|
func(req *http.Request) (*http.Response, error) {
|
||||||
resp, err := httpmock.NewJsonResponse(200, &Response{
|
resp, err := httpmock.NewJsonResponse(200, &Response{
|
||||||
Choices: []Choice{},
|
Choices: []Choice{},
|
||||||
@ -273,7 +381,8 @@ func TestBuildExecuteModelHTTPWithoutResults(t *testing.T) {
|
|||||||
Histories: make([]Message, 0),
|
Histories: make([]Message, 0),
|
||||||
OpenAIKey: "sdjdnklfjndslkjanfk",
|
OpenAIKey: "sdjdnklfjndslkjanfk",
|
||||||
Protocol: tracer.HTTP,
|
Protocol: tracer.HTTP,
|
||||||
Model: GPT4O,
|
Model: "gpt-4o",
|
||||||
|
Provider: OpenAI,
|
||||||
}
|
}
|
||||||
|
|
||||||
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
@ -287,14 +396,105 @@ func TestBuildExecuteModelHTTPWithoutResults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFromString(t *testing.T) {
|
func TestFromString(t *testing.T) {
|
||||||
model, err := FromStringToLLMModel("llama3")
|
model, err := FromStringToLLMProvider("openai")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, LLAMA3, model)
|
assert.Equal(t, OpenAI, model)
|
||||||
|
|
||||||
model, err = FromStringToLLMModel("gpt4-o")
|
model, err = FromStringToLLMProvider("ollama")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, GPT4O, model)
|
assert.Equal(t, Ollama, model)
|
||||||
|
|
||||||
model, err = FromStringToLLMModel("beelzebub-model")
|
model, err = FromStringToLLMProvider("beelzebub-model")
|
||||||
assert.Errorf(t, err, "model beelzebub-model not found")
|
assert.Errorf(t, err, "provider beelzebub-model not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildExecuteModelSSHWithoutPlaintextSection(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: "```plaintext\n```\n",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
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, "", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildExecuteModelSSHWithoutQuotesSection(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: "```\n```\n",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return httpmock.NewStringResponse(500, ""), nil
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
llmHoneypot := LLMHoneypot{
|
||||||
|
Histories: make([]Message, 0),
|
||||||
|
Protocol: tracer.SSH,
|
||||||
|
Model: "llama3",
|
||||||
|
Provider: Ollama,
|
||||||
|
}
|
||||||
|
|
||||||
|
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
|
||||||
|
openAIGPTVirtualTerminal.client = client
|
||||||
|
|
||||||
|
//When
|
||||||
|
str, err := openAIGPTVirtualTerminal.ExecuteModel("ls")
|
||||||
|
|
||||||
|
//Then
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveQuotes(t *testing.T) {
|
||||||
|
plaintext := "```plaintext\n```"
|
||||||
|
bash := "```bash\n```"
|
||||||
|
onlyQuotes := "```\n```"
|
||||||
|
complexText := "```plaintext\ntop - 10:30:48 up 1 day, 4:30, 2 users, load average: 0.15, 0.10, 0.08\nTasks: 198 total, 1 running, 197 sleeping, 0 stopped, 0 zombie\n```"
|
||||||
|
complexText2 := "```\ntop - 15:06:59 up 10 days, 3:17, 1 user, load average: 0.10, 0.09, 0.08\nTasks: 285 total\n```"
|
||||||
|
|
||||||
|
assert.Equal(t, "", removeQuotes(plaintext))
|
||||||
|
assert.Equal(t, "", removeQuotes(bash))
|
||||||
|
assert.Equal(t, "", removeQuotes(onlyQuotes))
|
||||||
|
assert.Equal(t, "top - 10:30:48 up 1 day, 4:30, 2 users, load average: 0.15, 0.10, 0.08\nTasks: 198 total, 1 running, 197 sleeping, 0 stopped, 0 zombie\n", removeQuotes(complexText))
|
||||||
|
assert.Equal(t, "top - 15:06:59 up 10 days, 3:17, 1 user, load average: 0.10, 0.09, 0.08\nTasks: 285 total\n", removeQuotes(complexText2))
|
||||||
}
|
}
|
||||||
|
186
protocols/strategies/HTTP/http.go
Normal file
186
protocols/strategies/HTTP/http.go
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
package HTTP
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/plugins"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HTTPStrategy struct{}
|
||||||
|
|
||||||
|
type httpResponse struct {
|
||||||
|
StatusCode int
|
||||||
|
Headers []string
|
||||||
|
Body string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (httpStrategy HTTPStrategy) Init(servConf parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
|
||||||
|
serverMux := http.NewServeMux()
|
||||||
|
|
||||||
|
serverMux.HandleFunc("/", func(responseWriter http.ResponseWriter, request *http.Request) {
|
||||||
|
traceRequest(request, tr, servConf.Description)
|
||||||
|
var matched bool
|
||||||
|
var resp httpResponse
|
||||||
|
var err error
|
||||||
|
for _, command := range servConf.Commands {
|
||||||
|
var err error
|
||||||
|
matched, err = regexp.MatchString(command.Regex, request.RequestURI)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error parsing regex: %s, %s", command.Regex, err.Error())
|
||||||
|
resp.StatusCode = 500
|
||||||
|
resp.Body = "500 Internal Server Error"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if matched {
|
||||||
|
resp, err = buildHTTPResponse(servConf, command, request)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error building http response: %s: %v", request.RequestURI, err)
|
||||||
|
resp.StatusCode = 500
|
||||||
|
resp.Body = "500 Internal Server Error"
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If none of the main commands matched, and we have a fallback command configured, process it here.
|
||||||
|
// The regexp is ignored for fallback commands, as they are catch-all for any request.
|
||||||
|
if !matched {
|
||||||
|
command := servConf.FallbackCommand
|
||||||
|
if command.Handler != "" || command.Plugin != "" {
|
||||||
|
resp, err = buildHTTPResponse(servConf, command, request)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error building http response: %s: %v", request.RequestURI, err)
|
||||||
|
resp.StatusCode = 500
|
||||||
|
resp.Body = "500 Internal Server Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setResponseHeaders(responseWriter, resp.Headers, resp.StatusCode)
|
||||||
|
fmt.Fprint(responseWriter, resp.Body)
|
||||||
|
|
||||||
|
})
|
||||||
|
go func() {
|
||||||
|
var err error
|
||||||
|
// Launch a TLS supporting server if we are supplied a TLS Key and Certificate.
|
||||||
|
// If relative paths are supplied, they are relative to the CWD of the binary.
|
||||||
|
// The can be self-signed, only the client will validate this (or not).
|
||||||
|
if servConf.TLSKeyPath != "" && servConf.TLSCertPath != "" {
|
||||||
|
err = http.ListenAndServeTLS(servConf.Address, servConf.TLSCertPath, servConf.TLSKeyPath, serverMux)
|
||||||
|
} else {
|
||||||
|
err = http.ListenAndServe(servConf.Address, serverMux)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error during init HTTP Protocol: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"port": servConf.Address,
|
||||||
|
"commands": len(servConf.Commands),
|
||||||
|
}).Infof("Init service: %s", servConf.Description)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildHTTPResponse(servConf parser.BeelzebubServiceConfiguration, command parser.Command, request *http.Request) (httpResponse, error) {
|
||||||
|
resp := httpResponse{
|
||||||
|
Body: command.Handler,
|
||||||
|
Headers: command.Headers,
|
||||||
|
StatusCode: command.StatusCode,
|
||||||
|
}
|
||||||
|
|
||||||
|
if command.Plugin == plugins.LLMPluginName {
|
||||||
|
llmProvider, err := plugins.FromStringToLLMProvider(servConf.Plugin.LLMProvider)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error: %v", err)
|
||||||
|
resp.Body = "404 Not Found!"
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
llmHoneypot := plugins.LLMHoneypot{
|
||||||
|
Histories: make([]plugins.Message, 0),
|
||||||
|
OpenAIKey: servConf.Plugin.OpenAISecretKey,
|
||||||
|
Protocol: tracer.HTTP,
|
||||||
|
Host: servConf.Plugin.Host,
|
||||||
|
Model: servConf.Plugin.LLMModel,
|
||||||
|
Provider: llmProvider,
|
||||||
|
CustomPrompt: servConf.Plugin.Prompt,
|
||||||
|
}
|
||||||
|
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
|
||||||
|
command := fmt.Sprintf("%s %s", request.Method, request.RequestURI)
|
||||||
|
|
||||||
|
completions, err := llmHoneypotInstance.ExecuteModel(command)
|
||||||
|
if err != nil {
|
||||||
|
resp.Body = "404 Not Found!"
|
||||||
|
return resp, fmt.Errorf("ExecuteModel error: %s, %v", command, err)
|
||||||
|
}
|
||||||
|
resp.Body = completions
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func traceRequest(request *http.Request, tr tracer.Tracer, HoneypotDescription string) {
|
||||||
|
bodyBytes, err := io.ReadAll(request.Body)
|
||||||
|
body := ""
|
||||||
|
if err == nil {
|
||||||
|
body = string(bodyBytes)
|
||||||
|
}
|
||||||
|
host, port, _ := net.SplitHostPort(request.RemoteAddr)
|
||||||
|
|
||||||
|
event := tracer.Event{
|
||||||
|
Msg: "HTTP New request",
|
||||||
|
RequestURI: request.RequestURI,
|
||||||
|
Protocol: tracer.HTTP.String(),
|
||||||
|
HTTPMethod: request.Method,
|
||||||
|
Body: body,
|
||||||
|
HostHTTPRequest: request.Host,
|
||||||
|
UserAgent: request.UserAgent(),
|
||||||
|
Cookies: mapCookiesToString(request.Cookies()),
|
||||||
|
Headers: request.Header,
|
||||||
|
Status: tracer.Stateless.String(),
|
||||||
|
RemoteAddr: request.RemoteAddr,
|
||||||
|
SourceIp: host,
|
||||||
|
SourcePort: port,
|
||||||
|
ID: uuid.New().String(),
|
||||||
|
Description: HoneypotDescription,
|
||||||
|
}
|
||||||
|
// Capture the TLS details from the request, if provided.
|
||||||
|
if request.TLS != nil {
|
||||||
|
event.Msg = "HTTPS New Request"
|
||||||
|
event.TLSServerName = request.TLS.ServerName
|
||||||
|
}
|
||||||
|
tr.TraceEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapCookiesToString(cookies []*http.Cookie) string {
|
||||||
|
cookiesString := ""
|
||||||
|
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
cookiesString += cookie.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return cookiesString
|
||||||
|
}
|
||||||
|
|
||||||
|
func setResponseHeaders(responseWriter http.ResponseWriter, headers []string, statusCode int) {
|
||||||
|
for _, headerStr := range headers {
|
||||||
|
keyValue := strings.Split(headerStr, ":")
|
||||||
|
if len(keyValue) > 1 {
|
||||||
|
responseWriter.Header().Add(keyValue[0], keyValue[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// http.StatusText(statusCode): empty string if the code is unknown.
|
||||||
|
if len(http.StatusText(statusCode)) > 0 {
|
||||||
|
responseWriter.WriteHeader(statusCode)
|
||||||
|
}
|
||||||
|
}
|
@ -1,68 +1,78 @@
|
|||||||
package strategies
|
package SSH
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mariocandela/beelzebub/v3/parser"
|
|
||||||
"github.com/mariocandela/beelzebub/v3/plugins"
|
|
||||||
"github.com/mariocandela/beelzebub/v3/tracer"
|
|
||||||
"net"
|
"net"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mariocandela/beelzebub/v3/historystore"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/plugins"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
|
|
||||||
"github.com/gliderlabs/ssh"
|
"github.com/gliderlabs/ssh"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SSHStrategy struct {
|
type SSHStrategy struct {
|
||||||
|
Sessions *historystore.HistoryStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
|
func (sshStrategy *SSHStrategy) Init(servConf parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
|
||||||
|
if sshStrategy.Sessions == nil {
|
||||||
|
sshStrategy.Sessions = historystore.NewHistoryStore()
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
server := &ssh.Server{
|
server := &ssh.Server{
|
||||||
Addr: beelzebubServiceConfiguration.Address,
|
Addr: servConf.Address,
|
||||||
MaxTimeout: time.Duration(beelzebubServiceConfiguration.DeadlineTimeoutSeconds) * time.Second,
|
MaxTimeout: time.Duration(servConf.DeadlineTimeoutSeconds) * time.Second,
|
||||||
IdleTimeout: time.Duration(beelzebubServiceConfiguration.DeadlineTimeoutSeconds) * time.Second,
|
IdleTimeout: time.Duration(servConf.DeadlineTimeoutSeconds) * time.Second,
|
||||||
Version: beelzebubServiceConfiguration.ServerVersion,
|
Version: servConf.ServerVersion,
|
||||||
Handler: func(sess ssh.Session) {
|
Handler: func(sess ssh.Session) {
|
||||||
uuidSession := uuid.New()
|
uuidSession := uuid.New()
|
||||||
|
|
||||||
host, port, _ := net.SplitHostPort(sess.RemoteAddr().String())
|
host, port, _ := net.SplitHostPort(sess.RemoteAddr().String())
|
||||||
|
sessionKey := "SSH" + host + sess.User()
|
||||||
|
|
||||||
|
// Inline SSH command
|
||||||
if sess.RawCommand() != "" {
|
if sess.RawCommand() != "" {
|
||||||
for _, command := range beelzebubServiceConfiguration.Commands {
|
for _, command := range servConf.Commands {
|
||||||
matched, err := regexp.MatchString(command.Regex, sess.RawCommand())
|
matched, err := regexp.MatchString(command.Regex, sess.RawCommand())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error regex: %s, %s", command.Regex, err.Error())
|
log.Errorf("error regex: %s, %s", command.Regex, err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if matched {
|
if matched {
|
||||||
commandOutput := command.Handler
|
commandOutput := command.Handler
|
||||||
|
|
||||||
if command.Plugin == plugins.LLMPluginName {
|
if command.Plugin == plugins.LLMPluginName {
|
||||||
|
llmProvider, err := plugins.FromStringToLLMProvider(servConf.Plugin.LLMProvider)
|
||||||
llmModel, err := plugins.FromStringToLLMModel(beelzebubServiceConfiguration.Plugin.LLMModel)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error fromString: %s", err.Error())
|
log.Errorf("error: %s", err.Error())
|
||||||
commandOutput = "command not found"
|
commandOutput = "command not found"
|
||||||
|
llmProvider = plugins.OpenAI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var histories []plugins.Message
|
||||||
|
if sshStrategy.Sessions.HasKey(sessionKey) {
|
||||||
|
histories = sshStrategy.Sessions.Query(sessionKey)
|
||||||
|
}
|
||||||
llmHoneypot := plugins.LLMHoneypot{
|
llmHoneypot := plugins.LLMHoneypot{
|
||||||
Histories: make([]plugins.Message, 0),
|
Histories: histories,
|
||||||
OpenAIKey: beelzebubServiceConfiguration.Plugin.OpenAISecretKey,
|
OpenAIKey: servConf.Plugin.OpenAISecretKey,
|
||||||
Protocol: tracer.SSH,
|
Protocol: tracer.SSH,
|
||||||
Host: beelzebubServiceConfiguration.Plugin.Host,
|
Host: servConf.Plugin.Host,
|
||||||
Model: llmModel,
|
Model: servConf.Plugin.LLMModel,
|
||||||
|
Provider: llmProvider,
|
||||||
|
CustomPrompt: servConf.Plugin.Prompt,
|
||||||
}
|
}
|
||||||
|
|
||||||
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
|
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
|
||||||
|
|
||||||
if commandOutput, err = llmHoneypotInstance.ExecuteModel(sess.RawCommand()); err != nil {
|
if commandOutput, err = llmHoneypotInstance.ExecuteModel(sess.RawCommand()); err != nil {
|
||||||
log.Errorf("Error ExecuteModel: %s, %s", sess.RawCommand(), err.Error())
|
log.Errorf("error ExecuteModel: %s, %s", sess.RawCommand(), err.Error())
|
||||||
commandOutput = "command not found"
|
commandOutput = "command not found"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,7 +80,7 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
sess.Write(append([]byte(commandOutput), '\n'))
|
sess.Write(append([]byte(commandOutput), '\n'))
|
||||||
|
|
||||||
tr.TraceEvent(tracer.Event{
|
tr.TraceEvent(tracer.Event{
|
||||||
Msg: "New SSH Session",
|
Msg: "New SSH Raw Command Session",
|
||||||
Protocol: tracer.SSH.String(),
|
Protocol: tracer.SSH.String(),
|
||||||
RemoteAddr: sess.RemoteAddr().String(),
|
RemoteAddr: sess.RemoteAddr().String(),
|
||||||
SourceIp: host,
|
SourceIp: host,
|
||||||
@ -79,12 +89,20 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
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: servConf.Description,
|
||||||
Command: sess.RawCommand(),
|
Command: sess.RawCommand(),
|
||||||
CommandOutput: commandOutput,
|
CommandOutput: commandOutput,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var histories []plugins.Message
|
||||||
|
if sshStrategy.Sessions.HasKey(sessionKey) {
|
||||||
|
histories = sshStrategy.Sessions.Query(sessionKey)
|
||||||
|
}
|
||||||
|
histories = append(histories, plugins.Message{Role: plugins.USER.String(), Content: sess.RawCommand()})
|
||||||
|
histories = append(histories, plugins.Message{Role: plugins.ASSISTANT.String(), Content: commandOutput})
|
||||||
|
sshStrategy.Sessions.Append(sessionKey, histories...)
|
||||||
tr.TraceEvent(tracer.Event{
|
tr.TraceEvent(tracer.Event{
|
||||||
Msg: "End SSH Session",
|
Msg: "End SSH Raw Command Session",
|
||||||
Status: tracer.End.String(),
|
Status: tracer.End.String(),
|
||||||
ID: uuidSession.String(),
|
ID: uuidSession.String(),
|
||||||
})
|
})
|
||||||
@ -94,7 +112,7 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
}
|
}
|
||||||
|
|
||||||
tr.TraceEvent(tracer.Event{
|
tr.TraceEvent(tracer.Event{
|
||||||
Msg: "New SSH Session",
|
Msg: "New SSH Terminal Session",
|
||||||
Protocol: tracer.SSH.String(),
|
Protocol: tracer.SSH.String(),
|
||||||
RemoteAddr: sess.RemoteAddr().String(),
|
RemoteAddr: sess.RemoteAddr().String(),
|
||||||
SourceIp: host,
|
SourceIp: host,
|
||||||
@ -103,51 +121,50 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
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: servConf.Description,
|
||||||
})
|
})
|
||||||
|
|
||||||
term := terminal.NewTerminal(sess, buildPrompt(sess.User(), beelzebubServiceConfiguration.ServerName))
|
terminal := term.NewTerminal(sess, buildPrompt(sess.User(), servConf.ServerName))
|
||||||
var histories []plugins.Message
|
var histories []plugins.Message
|
||||||
|
if sshStrategy.Sessions.HasKey(sessionKey) {
|
||||||
|
histories = sshStrategy.Sessions.Query(sessionKey)
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
commandInput, err := term.ReadLine()
|
commandInput, err := terminal.ReadLine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if commandInput == "exit" {
|
if commandInput == "exit" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
for _, command := range beelzebubServiceConfiguration.Commands {
|
for _, command := range servConf.Commands {
|
||||||
matched, err := regexp.MatchString(command.Regex, commandInput)
|
matched, err := regexp.MatchString(command.Regex, commandInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error regex: %s, %s", command.Regex, err.Error())
|
log.Errorf("error regex: %s, %s", command.Regex, err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if matched {
|
if matched {
|
||||||
commandOutput := command.Handler
|
commandOutput := command.Handler
|
||||||
|
|
||||||
if command.Plugin == plugins.LLMPluginName {
|
if command.Plugin == plugins.LLMPluginName {
|
||||||
|
llmProvider, err := plugins.FromStringToLLMProvider(servConf.Plugin.LLMProvider)
|
||||||
llmModel, err := plugins.FromStringToLLMModel(beelzebubServiceConfiguration.Plugin.LLMModel)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error fromString: %s", err.Error())
|
log.Errorf("error: %s, fallback OpenAI", err.Error())
|
||||||
commandOutput = "command not found"
|
llmProvider = plugins.OpenAI
|
||||||
}
|
}
|
||||||
|
|
||||||
llmHoneypot := plugins.LLMHoneypot{
|
llmHoneypot := plugins.LLMHoneypot{
|
||||||
Histories: histories,
|
Histories: histories,
|
||||||
OpenAIKey: beelzebubServiceConfiguration.Plugin.OpenAISecretKey,
|
OpenAIKey: servConf.Plugin.OpenAISecretKey,
|
||||||
Protocol: tracer.SSH,
|
Protocol: tracer.SSH,
|
||||||
Host: beelzebubServiceConfiguration.Plugin.Host,
|
Host: servConf.Plugin.Host,
|
||||||
Model: llmModel,
|
Model: servConf.Plugin.LLMModel,
|
||||||
|
Provider: llmProvider,
|
||||||
|
CustomPrompt: servConf.Plugin.Prompt,
|
||||||
}
|
}
|
||||||
|
|
||||||
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
|
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
|
||||||
|
|
||||||
if commandOutput, err = llmHoneypotInstance.ExecuteModel(commandInput); err != nil {
|
if commandOutput, err = llmHoneypotInstance.ExecuteModel(commandInput); err != nil {
|
||||||
log.Errorf("Error ExecuteModel: %s, %s", commandInput, err.Error())
|
log.Errorf("error ExecuteModel: %s, %s", commandInput, err.Error())
|
||||||
commandOutput = "command not found"
|
commandOutput = "command not found"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,7 +172,7 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
histories = append(histories, plugins.Message{Role: plugins.USER.String(), Content: commandInput})
|
histories = append(histories, plugins.Message{Role: plugins.USER.String(), Content: commandInput})
|
||||||
histories = append(histories, plugins.Message{Role: plugins.ASSISTANT.String(), Content: commandOutput})
|
histories = append(histories, plugins.Message{Role: plugins.ASSISTANT.String(), Content: commandOutput})
|
||||||
|
|
||||||
term.Write(append([]byte(commandOutput), '\n'))
|
terminal.Write(append([]byte(commandOutput), '\n'))
|
||||||
|
|
||||||
tr.TraceEvent(tracer.Event{
|
tr.TraceEvent(tracer.Event{
|
||||||
Msg: "New SSH Terminal Session",
|
Msg: "New SSH Terminal Session",
|
||||||
@ -167,12 +184,16 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
CommandOutput: commandOutput,
|
CommandOutput: commandOutput,
|
||||||
ID: uuidSession.String(),
|
ID: uuidSession.String(),
|
||||||
Protocol: tracer.SSH.String(),
|
Protocol: tracer.SSH.String(),
|
||||||
Description: beelzebubServiceConfiguration.Description,
|
Description: servConf.Description,
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add all history events for the terminal session to the store.
|
||||||
|
// This is done at the end of the session to avoid excess lock operations.
|
||||||
|
sshStrategy.Sessions.Append(sessionKey, histories...)
|
||||||
tr.TraceEvent(tracer.Event{
|
tr.TraceEvent(tracer.Event{
|
||||||
Msg: "End SSH Session",
|
Msg: "End SSH Session",
|
||||||
Status: tracer.End.String(),
|
Status: tracer.End.String(),
|
||||||
@ -183,7 +204,7 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
host, port, _ := net.SplitHostPort(ctx.RemoteAddr().String())
|
host, port, _ := net.SplitHostPort(ctx.RemoteAddr().String())
|
||||||
|
|
||||||
tr.TraceEvent(tracer.Event{
|
tr.TraceEvent(tracer.Event{
|
||||||
Msg: "New SSH attempt",
|
Msg: "New SSH Login Attempt",
|
||||||
Protocol: tracer.SSH.String(),
|
Protocol: tracer.SSH.String(),
|
||||||
Status: tracer.Stateless.String(),
|
Status: tracer.Stateless.String(),
|
||||||
User: ctx.User(),
|
User: ctx.User(),
|
||||||
@ -193,11 +214,11 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
SourceIp: host,
|
SourceIp: host,
|
||||||
SourcePort: port,
|
SourcePort: port,
|
||||||
ID: uuid.New().String(),
|
ID: uuid.New().String(),
|
||||||
Description: beelzebubServiceConfiguration.Description,
|
Description: servConf.Description,
|
||||||
})
|
})
|
||||||
matched, err := regexp.MatchString(beelzebubServiceConfiguration.PasswordRegex, password)
|
matched, err := regexp.MatchString(servConf.PasswordRegex, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error regex: %s, %s", beelzebubServiceConfiguration.PasswordRegex, err.Error())
|
log.Errorf("error regex: %s, %s", servConf.PasswordRegex, err.Error())
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return matched
|
return matched
|
||||||
@ -205,14 +226,14 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
}
|
}
|
||||||
err := server.ListenAndServe()
|
err := server.ListenAndServe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error during init SSH Protocol: %s", err.Error())
|
log.Errorf("error during init SSH Protocol: %s", err.Error())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"port": beelzebubServiceConfiguration.Address,
|
"port": servConf.Address,
|
||||||
"commands": len(beelzebubServiceConfiguration.Commands),
|
"commands": len(servConf.Commands),
|
||||||
}).Infof("GetInstance service %s", beelzebubServiceConfiguration.Protocol)
|
}).Infof("GetInstance service %s", servConf.Protocol)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,13 @@
|
|||||||
package strategies
|
package TCP
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mariocandela/beelzebub/v3/parser"
|
|
||||||
"github.com/mariocandela/beelzebub/v3/tracer"
|
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mariocandela/beelzebub/v3/parser"
|
||||||
|
"github.com/mariocandela/beelzebub/v3/tracer"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -14,8 +15,8 @@ import (
|
|||||||
type TCPStrategy struct {
|
type TCPStrategy struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
|
func (tcpStrategy *TCPStrategy) Init(servConf parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
|
||||||
listen, err := net.Listen("tcp", beelzebubServiceConfiguration.Address)
|
listen, err := net.Listen("tcp", servConf.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error during init TCP Protocol: %s", err.Error())
|
log.Errorf("Error during init TCP Protocol: %s", err.Error())
|
||||||
return err
|
return err
|
||||||
@ -25,8 +26,8 @@ func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
for {
|
for {
|
||||||
if conn, err := listen.Accept(); err == nil {
|
if conn, err := listen.Accept(); err == nil {
|
||||||
go func() {
|
go func() {
|
||||||
conn.SetDeadline(time.Now().Add(time.Duration(beelzebubServiceConfiguration.DeadlineTimeoutSeconds) * time.Second))
|
conn.SetDeadline(time.Now().Add(time.Duration(servConf.DeadlineTimeoutSeconds) * time.Second))
|
||||||
conn.Write([]byte(fmt.Sprintf("%s\n", beelzebubServiceConfiguration.Banner)))
|
conn.Write(fmt.Appendf([]byte{}, "%s\n", servConf.Banner))
|
||||||
|
|
||||||
buffer := make([]byte, 1024)
|
buffer := make([]byte, 1024)
|
||||||
command := ""
|
command := ""
|
||||||
@ -46,7 +47,7 @@ func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
SourceIp: host,
|
SourceIp: host,
|
||||||
SourcePort: port,
|
SourcePort: port,
|
||||||
ID: uuid.New().String(),
|
ID: uuid.New().String(),
|
||||||
Description: beelzebubServiceConfiguration.Description,
|
Description: servConf.Description,
|
||||||
})
|
})
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}()
|
}()
|
||||||
@ -55,8 +56,8 @@ func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.Beelze
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"port": beelzebubServiceConfiguration.Address,
|
"port": servConf.Address,
|
||||||
"banner": beelzebubServiceConfiguration.Banner,
|
"banner": servConf.Banner,
|
||||||
}).Infof("Init service %s", beelzebubServiceConfiguration.Protocol)
|
}).Infof("Init service %s", servConf.Protocol)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
@ -1,149 +0,0 @@
|
|||||||
package strategies
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/mariocandela/beelzebub/v3/parser"
|
|
||||||
"github.com/mariocandela/beelzebub/v3/plugins"
|
|
||||||
"github.com/mariocandela/beelzebub/v3/tracer"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HTTPStrategy struct {
|
|
||||||
beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (httpStrategy HTTPStrategy) Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
|
|
||||||
httpStrategy.beelzebubServiceConfiguration = beelzebubServiceConfiguration
|
|
||||||
serverMux := http.NewServeMux()
|
|
||||||
|
|
||||||
serverMux.HandleFunc("/", func(responseWriter http.ResponseWriter, request *http.Request) {
|
|
||||||
traceRequest(request, tr, beelzebubServiceConfiguration.Description)
|
|
||||||
for _, command := range httpStrategy.beelzebubServiceConfiguration.Commands {
|
|
||||||
matched, err := regexp.MatchString(command.Regex, request.RequestURI)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Error regex: %s, %s", command.Regex, err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
fmt.Fprintf(responseWriter, responseHTTPBody)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
go func() {
|
|
||||||
err := http.ListenAndServe(httpStrategy.beelzebubServiceConfiguration.Address, serverMux)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Error during init HTTP Protocol: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"port": beelzebubServiceConfiguration.Address,
|
|
||||||
"commands": len(beelzebubServiceConfiguration.Commands),
|
|
||||||
}).Infof("Init service: %s", beelzebubServiceConfiguration.Description)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func traceRequest(request *http.Request, tr tracer.Tracer, HoneypotDescription string) {
|
|
||||||
bodyBytes, err := io.ReadAll(request.Body)
|
|
||||||
body := ""
|
|
||||||
if err == nil {
|
|
||||||
body = string(bodyBytes)
|
|
||||||
}
|
|
||||||
host, port, _ := net.SplitHostPort(request.RemoteAddr)
|
|
||||||
|
|
||||||
tr.TraceEvent(tracer.Event{
|
|
||||||
Msg: "HTTP New request",
|
|
||||||
RequestURI: request.RequestURI,
|
|
||||||
Protocol: tracer.HTTP.String(),
|
|
||||||
HTTPMethod: request.Method,
|
|
||||||
Body: body,
|
|
||||||
HostHTTPRequest: request.Host,
|
|
||||||
UserAgent: request.UserAgent(),
|
|
||||||
Cookies: mapCookiesToString(request.Cookies()),
|
|
||||||
Headers: mapHeaderToString(request.Header),
|
|
||||||
Status: tracer.Stateless.String(),
|
|
||||||
RemoteAddr: request.RemoteAddr,
|
|
||||||
SourceIp: host,
|
|
||||||
SourcePort: port,
|
|
||||||
ID: uuid.New().String(),
|
|
||||||
Description: HoneypotDescription,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapHeaderToString(headers http.Header) string {
|
|
||||||
headersString := ""
|
|
||||||
|
|
||||||
for key := range headers {
|
|
||||||
for _, values := range headers[key] {
|
|
||||||
headersString += fmt.Sprintf("[Key: %s, values: %s],", key, values)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return headersString
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapCookiesToString(cookies []*http.Cookie) string {
|
|
||||||
cookiesString := ""
|
|
||||||
|
|
||||||
for _, cookie := range cookies {
|
|
||||||
cookiesString += cookie.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return cookiesString
|
|
||||||
}
|
|
||||||
|
|
||||||
func setResponseHeaders(responseWriter http.ResponseWriter, headers []string, statusCode int) {
|
|
||||||
for _, headerStr := range headers {
|
|
||||||
keyValue := strings.Split(headerStr, ":")
|
|
||||||
if len(keyValue) > 1 {
|
|
||||||
responseWriter.Header().Add(keyValue[0], keyValue[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// http.StatusText(statusCode): empty string if the code is unknown.
|
|
||||||
if len(http.StatusText(statusCode)) > 0 {
|
|
||||||
responseWriter.WriteHeader(statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,10 +2,11 @@
|
|||||||
package tracer
|
package tracer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
@ -26,7 +27,7 @@ type Event struct {
|
|||||||
User string
|
User string
|
||||||
Password string
|
Password string
|
||||||
Client string
|
Client string
|
||||||
Headers string
|
Headers map[string][]string
|
||||||
Cookies string
|
Cookies string
|
||||||
UserAgent string
|
UserAgent string
|
||||||
HostHTTPRequest string
|
HostHTTPRequest string
|
||||||
@ -36,6 +37,7 @@ type Event struct {
|
|||||||
Description string
|
Description string
|
||||||
SourceIp string
|
SourceIp string
|
||||||
SourcePort string
|
SourcePort string
|
||||||
|
TLSServerName string
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
Reference in New Issue
Block a user