Compare commits

...

123 Commits
v2.1.3 ... main

Author SHA1 Message Date
c032489522 Feat: add MCP honeypot support (#199)
* Add MCP honeypot

* Add http headers in plain text

* Improve code coverage

* Refactor README.md
2025-07-01 23:07:49 +02:00
e1e80120df Update FUNDING.yml
Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
2025-06-18 07:27:13 +02:00
9c4c1e016f Build(deps): Bump golang.org/x/term from 0.31.0 to 0.32.0 (#197)
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.31.0 to 0.32.0.
- [Commits](https://github.com/golang/term/compare/v0.31.0...v0.32.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-version: 0.32.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-06 07:19:07 +02:00
35cf61623c Build(deps): Bump golang.org/x/net from 0.36.0 to 0.38.0 (#194)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.36.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-17 09:47:33 +02:00
8952164cce Build(deps): Bump github.com/prometheus/client_golang from 1.20.5 to 1.22.0 (#193)
Build(deps): Bump github.com/prometheus/client_golang

Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.20.5 to 1.22.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.20.5...v1.22.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-version: 1.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-09 08:02:34 +02:00
b2ad584107 Build(deps): Bump github.com/jarcoal/httpmock from 1.3.1 to 1.4.0 (#191)
Bumps [github.com/jarcoal/httpmock](https://github.com/jarcoal/httpmock) from 1.3.1 to 1.4.0.
- [Release notes](https://github.com/jarcoal/httpmock/releases)
- [Commits](https://github.com/jarcoal/httpmock/compare/v1.3.1...v1.4.0)

---
updated-dependencies:
- dependency-name: github.com/jarcoal/httpmock
  dependency-version: 1.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 09:33:47 +02:00
ad646538c3 Build(deps): Bump golang.org/x/term from 0.30.0 to 0.31.0 (#192) 2025-04-07 06:30:10 +02:00
39c26c90c2 feat: add "Supported By" section to README.md 2025-04-05 08:52:13 +02:00
8f1075b33f Feat: Change licenze from MIT to GNU GPL v3 (#190)
Update LICENSE: Change from MIT to GNU GPL V3

Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
2025-03-31 21:44:21 +02:00
74cf64f628 Feat: Update README.md, replace last jetbrains logo
Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
2025-03-23 19:24:12 +01:00
d677cd20b9 Feature: Enhance Performance, Logging & Stability: Precompile Regex, Command Matching, Golang 1.24, History Cleanup & memLimitMiB Flag. (#182)
* Feat: Add support for logging which "command" was matched for SSH and HTTP strategies.

* Feat: Convert to precompiling regexp at config load time. This allows for errors to be presented to the user during startup, and provides better performance for complex regexp.

* Feat:Bump Golang version to latest stable 1.24

* Feat: Add a cleanup routine for HistoryStore, default TTL for events is 1 hour since last interaction.

* Feat: Add new command line flag "memLimitMiB" with a default value of 100.

---------

Signed-off-by: Bryan Nolen <bryan@arc.net.au>
Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
Co-authored-by: Mario Candela <mario.candela.personal@gmail.com>
2025-03-23 19:16:34 +01:00
16b012784c fix: test on minimal and stable golang versions (#183)
fix: test on minimal and stable golang versions
---------

Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
Signed-off-by: James Hodgkinson <james@terminaloutcomes.com>
Co-authored-by: Mario Candela <mario.candela.personal@gmail.com>
2025-03-19 10:18:44 +01:00
a79937c5ae Build(deps): Bump golang.org/x/net from 0.33.0 to 0.36.0 (#180)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.33.0 to 0.36.0.
- [Commits](https://github.com/golang/net/compare/v0.33.0...v0.36.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-14 07:35:41 +01:00
bdabbe9adc Build(deps): Bump golang.org/x/term from 0.29.0 to 0.30.0 (#181)
* Build(deps): Bump golang.org/x/term from 0.29.0 to 0.30.0

Bumps [golang.org/x/term](https://github.com/golang/term) from 0.29.0 to 0.30.0.
- [Commits](https://github.com/golang/term/compare/v0.29.0...v0.30.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update codeql.yml

fixed go version on CodeQL pipeline

Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mario Candela <mario.candela.personal@gmail.com>
2025-03-14 07:27:14 +01:00
db0da03baa Fix: Update http-80.yaml, improve examples HTTP LLM Honeypot
Add LLM Provider name

Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
2025-03-13 08:15:51 +01:00
b062416c00 Feat: Add FallbackCommand for HTTP Strategy, refactor packages strategies (#175)
Add FallbackCommand for HTTP Strategy, refactor packages strategies, improve histories implementations.
2025-03-13 08:06:46 +01:00
933f02911b feat: Improve SSH LLM honeypot, preserve session after attacker logout (#179)
* Migrate from deprecated library "golang.org/x/crypto/ssh/terminal" to "golang.org/x/term"

* Feat: Inject OpenAI secret key from environment variable

* Feat: Add test for OpenAI secret key injection from environment variable

* Fix: Correct llmModel value in http-80.yaml configuration

* Feat: Add OPEN_AI_SECRET_KEY environment variable to docker-compose.yml

* Feat: Implement session management for SSHStrategy with command history
2025-03-09 13:17:04 +01:00
ef07ca1203 Feat: continuous delivery pipeline add latest tag (#174)
Feat: continuous delivery pipeline add latest tag

Signed-off-by: James Hodgkinson <james@terminaloutcomes.com>
2025-03-02 05:30:36 +01:00
1f59685530 Feat: Improve HTTP Headers serializer json log #172 (#173)
* Changed Event struct, field headers from string to map[string][]string

* Add integration test for http Headers
2025-03-01 12:31:34 +01:00
f658a26b32 Feat: Update docker-image.yml to add multi-platform support (#171)
* Update docker-image.yml

Adds multi-arch support

Signed-off-by: James Hodgkinson <james@terminaloutcomes.com>
Co-authored-by: Mario Candela <mario.candela.personal@gmail.com>
2025-02-28 11:36:15 +01:00
3fb8a667b3 Update codeql.yml
Upgrade codeQL from v2 to v3

Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
2025-02-24 08:16:34 +01:00
8963bbc86d Fix: mapping LLMModel for SSH inline, removed old comments on docker-c… (#168)
Fix mapping LLMModel for SSH inline, removed old comments on docker-compose.yml
2025-02-20 22:41:28 +01:00
44ec44ea5c Fix LLM model name typo 2025-02-20 18:20:17 +01:00
38297faed2 Feat: Refactoring LLM Plugin, update docs. (#165)
Refactoring LLM Plugin, update docs.
2025-02-16 22:48:59 +01:00
8703d1afda Fix: llm plugin OpenAI generates random plaintext (hallucinations) (#163)
* Add tests to adopt TDD.

* Fix bug, LLM hallucinations
2025-02-16 16:27:10 +01:00
db804474d3 Add support for TLS based HTTP connections. (#158)
* Add support for TLS based HTTP connections, With Unit Tests.
2025-02-13 20:54:22 +01:00
48dd70d523 Build(deps): Bump golang.org/x/crypto from 0.32.0 to 0.33.0 (#157) 2025-02-10 07:07:41 +01:00
4813685834 Bump github.com/go-resty/resty/v2 from 2.16.4 to 2.16.5 (#156)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.16.4 to 2.16.5.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.16.4...v2.16.5)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-24 08:32:45 +01:00
6f6acb212b Bump github.com/go-resty/resty/v2 from 2.16.3 to 2.16.4 (#155)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.16.3 to 2.16.4.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.16.3...v2.16.4)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-21 08:16:54 +01:00
99c7287c02 Feat: Refactoring plugin:LLM honeypot custom prompt (#154)
refactoring LLM honeypot custom prompt
2025-01-16 08:46:13 +01:00
c3d2ff885d Feat: LLM Honeypot allow specifying the custom prompt #152 (#153)
* implement new feature, custom prompt

* Add doc for custom prompt
2025-01-14 08:45:30 +01:00
f1b35e9e43 Bump github.com/go-resty/resty/v2 from 2.16.2 to 2.16.3 (#151)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.16.2 to 2.16.3.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.16.2...v2.16.3)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-10 09:09:15 +01:00
d74499bb37 Bump golang.org/x/crypto from 0.31.0 to 0.32.0 (#149)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.31.0 to 0.32.0.
- [Commits](https://github.com/golang/crypto/compare/v0.31.0...v0.32.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-07 16:18:27 +01:00
5bba406e6b Bump github.com/gliderlabs/ssh from 0.3.7 to 0.3.8 (#148)
Bumps [github.com/gliderlabs/ssh](https://github.com/gliderlabs/ssh) from 0.3.7 to 0.3.8.
- [Release notes](https://github.com/gliderlabs/ssh/releases)
- [Commits](https://github.com/gliderlabs/ssh/compare/v0.3.7...v0.3.8)

---
updated-dependencies:
- dependency-name: github.com/gliderlabs/ssh
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-13 08:02:07 +01:00
ec931bd6f9 Bump golang.org/x/crypto from 0.30.0 to 0.31.0 (#147)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.30.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.30.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-12 07:58:47 +01:00
b7f7aa0170 Bump golang.org/x/crypto from 0.29.0 to 0.30.0 (#146)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.29.0 to 0.30.0.
- [Commits](https://github.com/golang/crypto/compare/v0.29.0...v0.30.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-05 09:07:15 +01:00
79f9162f24 Bump github.com/stretchr/testify from 1.9.0 to 1.10.0 (#145)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.9.0...v1.10.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-25 08:39:56 +01:00
24828e503b Bump github.com/go-resty/resty/v2 from 2.16.1 to 2.16.2 (#144)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.16.1 to 2.16.2.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.16.1...v2.16.2)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-25 08:39:40 +01:00
acd5aa0e9c Bump github.com/go-resty/resty/v2 from 2.16.0 to 2.16.1 (#143)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.16.0 to 2.16.1.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.16.0...v2.16.1)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 08:29:07 +01:00
480b734834 Bump github.com/go-resty/resty/v2 from 2.15.3 to 2.16.0 (#142)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.15.3 to 2.16.0.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.15.3...v2.16.0)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-11 15:26:54 +01:00
7556c76652 Bump golang.org/x/crypto from 0.28.0 to 0.29.0 (#141) 2024-11-09 23:40:16 +01:00
11421665db Bump github.com/prometheus/client_golang from 1.20.4 to 1.20.5 (#140) 2024-10-16 07:38:30 +02:00
cce0dcfa30 Bump golang.org/x/crypto from 0.27.0 to 0.28.0 (#137)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.27.0 to 0.28.0.
- [Commits](https://github.com/golang/crypto/compare/v0.27.0...v0.28.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 11:45:02 +02:00
4740b2b6f5 Bump github.com/go-resty/resty/v2 from 2.15.2 to 2.15.3 (#136)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.15.2 to 2.15.3.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.15.2...v2.15.3)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-30 14:14:45 +02:00
27eb88e050 Bump github.com/go-resty/resty/v2 from 2.15.1 to 2.15.2 (#135)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.15.1 to 2.15.2.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.15.1...v2.15.2)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 11:42:54 +02:00
9eaa503def Bump github.com/go-resty/resty/v2 from 2.15.0 to 2.15.1 (#134)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.15.0 to 2.15.1.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.15.0...v2.15.1)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 11:29:37 +02:00
f07ce7d3be Bump github.com/prometheus/client_golang from 1.20.3 to 1.20.4 (#133)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.20.3 to 1.20.4.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.20.3...v1.20.4)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 08:48:32 +02:00
a924b2cb8b Bump github.com/go-resty/resty/v2 from 2.14.0 to 2.15.0 (#132)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.14.0 to 2.15.0.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.14.0...v2.15.0)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 09:40:34 +02:00
8e81a8721e Feat: manage SSH inline command (#130)
manage SSH inline command
2024-09-07 12:54:37 +02:00
f40ce9215e Bump github.com/prometheus/client_golang from 1.20.2 to 1.20.3 (#128)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.20.2 to 1.20.3.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/v1.20.3/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.20.2...v1.20.3)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-06 08:47:31 +02:00
0fc9ebb05e Bump golang.org/x/crypto from 0.26.0 to 0.27.0 (#127)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.26.0 to 0.27.0.
- [Commits](https://github.com/golang/crypto/compare/v0.26.0...v0.27.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-06 08:46:43 +02:00
0b5486964b feat: add source ip and source port (#126)
add source ip and source port
2024-08-30 08:28:56 +02:00
fa472effb9 Allow for MultiArch builds (#125)
Remove `GOARCH=amd64` to allow for MultiArch builds.

Signed-off-by: Marco Ochse <t3chn0m4g3@users.noreply.github.com>
2024-08-30 08:07:50 +02:00
628e20e01f Bump github.com/prometheus/client_golang from 1.20.1 to 1.20.2 (#124) 2024-08-26 07:49:28 +02:00
0d4aa5f52e Bump github.com/prometheus/client_golang from 1.20.0 to 1.20.1 (#123)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.20.0 to 1.20.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/v1.20.1/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.20.0...v1.20.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-25 17:12:04 +02:00
58f27ab076 Bump github.com/prometheus/client_golang from 1.19.1 to 1.20.0 (#122) 2024-08-15 08:48:06 +02:00
c4132f2d75 Bump golang.org/x/crypto from 0.25.0 to 0.26.0 (#120)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.25.0 to 0.26.0.
- [Commits](https://github.com/golang/crypto/compare/v0.25.0...v0.26.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-07 07:53:36 +02:00
f4ec6dcefb Bump github.com/go-resty/resty/v2 from 2.13.1 to 2.14.0 (#119)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.13.1 to 2.14.0.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.13.1...v2.14.0)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-06 09:07:03 +02:00
a1e96738fb Feat: beelzebub cloud integrations (#117)
* improve beelzebub cloud integration

* refactoring cloud integration, fix unit test

* add unit test get honeypots

* improve code coverage
2024-08-01 20:05:05 +02:00
cd284877cf fix typo README.md 2024-07-21 20:15:09 +02:00
2088163b54 Feature: add support for llama, refactor yaml service interface (#115)
* refactor and add llama LMM support

* update readme

* improve code coverage
2024-07-21 20:11:18 +02:00
0af1a05ae9 Bump golang.org/x/crypto from 0.24.0 to 0.25.0 (#113)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.24.0 to 0.25.0.
- [Commits](https://github.com/golang/crypto/compare/v0.24.0...v0.25.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 09:01:31 +02:00
5086f5ba08 Update README.md
Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
2024-06-26 23:22:56 +02:00
e1f82db2d0 Update README.md
Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
2024-06-23 20:04:43 +02:00
59f40a166b Feat: Improve LMM SSH Honeypot (#112)
* add LMM Honeypot HTTP Server

* improve unit test code coverage

* integrate LLM plugin into http honeypot strategy

* improve code coverage

* fix typos

* improve OpenAI plugin with gpt-4, adpt new API amd map new object

* improve LLM SSH honeypot, fix updated README.md
2024-06-23 16:00:31 +02:00
93d7804ba3 Feat: add LMM Honeypot HTTP Server (#110)
* add LMM Honeypot HTTP Server

* improve unit test code coverage

* integrate LLM plugin into http honeypot strategy

* improve code coverage

* fix typos

* improve OpenAI plugin with gpt-4, adpt new API amd map new object
2024-06-23 10:55:06 +02:00
24b4153e77 Bump golang.org/x/crypto from 0.23.0 to 0.24.0 (#109)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.23.0 to 0.24.0.
- [Commits](https://github.com/golang/crypto/compare/v0.23.0...v0.24.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-05 09:08:16 +02:00
1d90c83678 Bump github.com/go-resty/resty/v2 from 2.13.0 to 2.13.1 (#108) 2024-05-13 07:46:43 +02:00
67829655f4 Bump github.com/go-resty/resty/v2 from 2.12.0 to 2.13.0 (#106)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.12.0 to 2.13.0.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.12.0...v2.13.0)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-10 09:03:59 +02:00
9ad21e138b Bump github.com/prometheus/client_golang from 1.19.0 to 1.19.1 (#107)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.19.0 to 1.19.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.19.0...v1.19.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-10 09:03:36 +02:00
8ab11e6ac2 Bump github.com/rabbitmq/amqp091-go from 1.9.0 to 1.10.0 (#105)
Bumps [github.com/rabbitmq/amqp091-go](https://github.com/rabbitmq/amqp091-go) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/rabbitmq/amqp091-go/releases)
- [Changelog](https://github.com/rabbitmq/amqp091-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rabbitmq/amqp091-go/compare/v1.9.0...v1.10.0)

---
updated-dependencies:
- dependency-name: github.com/rabbitmq/amqp091-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-09 17:05:51 +02:00
965942609d Bump golang.org/x/crypto from 0.22.0 to 0.23.0 (#104)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.22.0 to 0.23.0.
- [Commits](https://github.com/golang/crypto/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-07 09:10:44 +02:00
b8d77983ee Bump golang.org/x/net from 0.22.0 to 0.23.0 (#102)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.22.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 15:46:12 +02:00
b332f85230 Bump golang.org/x/crypto from 0.21.0 to 0.22.0 (#101)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.21.0 to 0.22.0.
- [Commits](https://github.com/golang/crypto/compare/v0.21.0...v0.22.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-05 07:46:16 +02:00
b1de020de8 Feat: Improve OpenAI plugin: model and prompt (#100)
* Change OpenAI model and prompt.

---------

Co-authored-by: mariocandela <mario.candela.personal@gmail.com>
2024-04-03 08:20:52 +02:00
05b49051db Bump github.com/gliderlabs/ssh from 0.3.6 to 0.3.7 (#99)
Bumps [github.com/gliderlabs/ssh](https://github.com/gliderlabs/ssh) from 0.3.6 to 0.3.7.
- [Release notes](https://github.com/gliderlabs/ssh/releases)
- [Commits](https://github.com/gliderlabs/ssh/compare/v0.3.6...v0.3.7)

---
updated-dependencies:
- dependency-name: github.com/gliderlabs/ssh
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-19 09:19:22 +01:00
3555ea9d3b Bump github.com/go-resty/resty/v2 from 2.11.0 to 2.12.0 (#98)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.11.0 to 2.12.0.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.11.0...v2.12.0)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-18 09:07:45 +01:00
d4fe0f96bd Bump google.golang.org/protobuf from 1.32.0 to 1.33.0 (#96)
Bumps google.golang.org/protobuf from 1.32.0 to 1.33.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-14 15:20:03 +01:00
6e26f76c51 Bump golang.org/x/crypto from 0.20.0 to 0.21.0 (#94)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.20.0 to 0.21.0.
- [Commits](https://github.com/golang/crypto/compare/v0.20.0...v0.21.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-05 08:14:43 +01:00
b2a7a527ff Bump github.com/stretchr/testify from 1.8.4 to 1.9.0 (#93) 2024-03-04 08:20:38 +01:00
1c650882b6 feat: Beelzebub cloud tracer plugin (#92) 2024-03-02 15:29:43 +01:00
ada0a9b8f0 Bump github.com/prometheus/client_golang from 1.18.0 to 1.19.0 (#91)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.18.0 to 1.19.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/v1.19.0/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.18.0...v1.19.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-28 12:05:28 +01:00
ccd160f7b0 Bump golang.org/x/crypto from 0.19.0 to 0.20.0 (#90)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.19.0 to 0.20.0.
- [Commits](https://github.com/golang/crypto/compare/v0.19.0...v0.20.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-27 08:18:46 +01:00
67d9ea7168 Bump golang.org/x/crypto from 0.18.0 to 0.19.0 (#88) 2024-02-08 07:57:30 +01:00
b441f8f9ab Bump github.com/google/uuid from 1.5.0 to 1.6.0 (#87) 2024-01-24 07:56:26 +01:00
0b9aa8b965 Bump golang.org/x/crypto from 0.17.0 to 0.18.0 (#86)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.17.0 to 0.18.0.
- [Commits](https://github.com/golang/crypto/compare/v0.17.0...v0.18.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-09 15:02:28 +01:00
c7bd863b36 Bump github.com/prometheus/client_golang from 1.17.0 to 1.18.0 (#85)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.17.0 to 1.18.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/v1.18.0/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.17.0...v1.18.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-03 15:54:39 +01:00
e2d1cc6087 Bump github.com/go-resty/resty/v2 from 2.10.0 to 2.11.0 (#84) 2023-12-29 10:56:21 +01:00
5bb7a96b39 Bump github.com/gliderlabs/ssh from 0.3.5 to 0.3.6 (#83)
Bumps [github.com/gliderlabs/ssh](https://github.com/gliderlabs/ssh) from 0.3.5 to 0.3.6.
- [Release notes](https://github.com/gliderlabs/ssh/releases)
- [Commits](https://github.com/gliderlabs/ssh/compare/v0.3.5...v0.3.6)

---
updated-dependencies:
- dependency-name: github.com/gliderlabs/ssh
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-21 09:04:53 +01:00
ab7cefdb18 Bump golang.org/x/crypto from 0.16.0 to 0.17.0 (#81)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 08:45:36 +01:00
34159ca06f Bump github.com/google/uuid from 1.4.0 to 1.5.0 (#80) 2023-12-13 08:02:10 +01:00
fd2bbe6be6 Bump golang.org/x/crypto from 0.15.0 to 0.16.0 (#78) 2023-11-28 07:06:57 +01:00
9076dfa47c Bump golang.org/x/crypto from 0.14.0 to 0.15.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.14.0 to 0.15.0.
- [Commits](https://github.com/golang/crypto/compare/v0.14.0...v0.15.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-09 08:42:35 +01:00
2b90c4a1f5 Update README.md
Add: Mentioned in Awesome Go

Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
2023-10-30 12:29:34 +01:00
90004e7d84 Bump github.com/google/uuid from 1.3.1 to 1.4.0 (#76) 2023-10-27 08:44:12 +02:00
9e042e33f5 Bump github.com/go-resty/resty/v2 from 2.9.1 to 2.10.0 (#75) 2023-10-16 07:57:05 +02:00
5e5d0494a9 refactor: Improve Go docs (#74)
* add go docs, package: parser
* add go docs, package: protocols
* add go docs, package: tracer
2023-10-15 20:54:53 +02:00
d77aa0c8a0 Bump golang.org/x/net from 0.15.0 to 0.17.0 (#73) 2023-10-12 08:35:19 +02:00
3733c902b9 add Go Reference 2023-10-11 23:35:58 +02:00
07ffdd839f Refactoring, improve code coverage (#72)
* Refactoring, improve code coverage

* Add unit test for gelAllFilesNameByDirName

* Add codecov coverage into README.md

* Improve coverage readFileBytesByFilePath
2023-10-09 01:16:53 +02:00
1f48f4dff5 Feature: CI, Add codecov 2023-10-08 19:26:41 +02:00
88a96a7efd Update README.md
Add goreportcard

Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-10-08 17:47:33 +02:00
34a80b06f3 Feature: Refactor import for release v3 (#71)
Refactor import for release v3
2023-10-08 17:45:31 +02:00
a9584f0ac6 Bump golang.org/x/crypto from 0.13.0 to 0.14.0 (#70)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.13.0 to 0.14.0.
- [Commits](https://github.com/golang/crypto/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-06 08:14:08 +02:00
fd57d6ed92 fix unit test 2023-10-05 21:57:30 +02:00
7badd5b655 Merge remote-tracking branch 'origin/main' 2023-10-05 21:52:50 +02:00
9b4b399df0 Refactor import, edit go module with github.com 2023-10-05 21:40:34 +02:00
abb956ec7a Bump github.com/rabbitmq/amqp091-go from 1.8.1 to 1.9.0 (#69)
Bumps [github.com/rabbitmq/amqp091-go](https://github.com/rabbitmq/amqp091-go) from 1.8.1 to 1.9.0.
- [Release notes](https://github.com/rabbitmq/amqp091-go/releases)
- [Changelog](https://github.com/rabbitmq/amqp091-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rabbitmq/amqp091-go/compare/v1.8.1...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/rabbitmq/amqp091-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 07:43:06 +02:00
dbcff8d002 Bump github.com/melbahja/goph from 1.3.1 to 1.4.0 (#66)
Bumps [github.com/melbahja/goph](https://github.com/melbahja/goph) from 1.3.1 to 1.4.0.
- [Commits](https://github.com/melbahja/goph/compare/v1.3.1...v1.4.0)

---
updated-dependencies:
- dependency-name: github.com/melbahja/goph
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mario Candela <m4r10.php@gmail.com>
2023-10-02 08:31:59 +02:00
26fa5f5ec9 Bump github.com/go-resty/resty/v2 from 2.8.0 to 2.9.1 (#67)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.8.0 to 2.9.1.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.8.0...v2.9.1)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-02 08:27:39 +02:00
2bf3dccce0 Bump github.com/prometheus/client_golang from 1.16.0 to 1.17.0 (#65) 2023-09-28 08:32:21 +02:00
3504995658 Add JetBrains support badge to README.md, Add CONTRIBUTING.md 2023-09-27 23:05:09 +02:00
ffd64b63e3 feature: helm chart configurations to deploy beelzebub on kubernetes (#63)
* Initial helm chart configuration

* configure config map with beelzebub configuration

* edit README.md add helm docs

* configured config maps

*parametric configmaps using values.yaml

* addd services confgiuration by chart values

* update readme.md
2023-09-21 23:19:56 +02:00
2e4f383bc0 Bump github.com/go-resty/resty/v2 from 2.7.0 to 2.8.0 (#64)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.7.0...v2.8.0)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-18 09:03:15 +02:00
7bc065100f Bump golang.org/x/crypto from 0.12.0 to 0.13.0 (#62) 2023-09-07 10:48:39 +02:00
b763ea5490 Update README.md
add new beelzebub icon

Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-08-31 16:38:05 +02:00
0794736bc5 Feature: non-blocking tracer, changed license (#57)
* refactor tracer, introduce worker to avoid un-blocker trace strategy

* fixed unit test and refactor dockerFile

* buffered events chan

* refactor yaml API core, initial web hook tracing implementations

* added banner

* changed license from GPL 3 to MIT

* Edit readme
2023-08-30 23:04:35 +02:00
345e9ef9fd Bump github.com/google/uuid from 1.3.0 to 1.3.1 (#58) 2023-08-22 08:24:12 +02:00
6ee83f102e Bump github.com/jarcoal/httpmock from 1.3.0 to 1.3.1 (#56) 2023-08-17 13:40:39 +02:00
42390faf4d Bump golang.org/x/crypto from 0.11.0 to 0.12.0 (#55)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.11.0 to 0.12.0.
- [Commits](https://github.com/golang/crypto/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-07 09:47:45 +02:00
951bb3bd0a Update openai-gpt.go
Removed duplicate comment 

Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-07-09 13:16:59 +02:00
f2d66d0f8c Bump golang.org/x/crypto from 0.10.0 to 0.11.0 (#54)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.10.0 to 0.11.0.
- [Commits](https://github.com/golang/crypto/compare/v0.10.0...v0.11.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-06 08:57:31 +02:00
9670cf00c8 Small refactor to reduce LOC and keep code DRY. (#53) 2023-06-26 18:55:49 +02:00
54 changed files with 3800 additions and 1254 deletions

2
.github/FUNDING.yml vendored
View File

@ -1 +1 @@
custom: ["https://www.paypal.com/donate/?business=P75FH5LXKQTAC&no_recurring=0&currency_code=EUR"]
github: mariocandela

View File

@ -2,13 +2,18 @@ name: CI
on:
push:
branches: [ main ]
branches: [ "main" ]
pull_request:
branches: [ main ]
branches: [ "main" ]
jobs:
CI:
strategy:
fail-fast: false
matrix:
go-version:
- "1.24.1"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@ -16,7 +21,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.20.0
go-version: ${{ matrix.go-version }}
- name: Dependencies
run: go mod download
@ -35,12 +40,12 @@ jobs:
- name: Quality Gate - Test coverage shall be above threshold
env:
TESTCOVERAGE_THRESHOLD: 65
TESTCOVERAGE_THRESHOLD: 80
run: |
echo "Quality Gate: checking test coverage is above threshold ..."
echo "Threshold : $TESTCOVERAGE_THRESHOLD %"
# Excluded the concrete strategy from the unit test coverage, because covered by integration tests
cat coverage.tmp.out | grep -v "ssh.go" | grep -v "http.go" | grep -v "tcp.go" > coverage.out
cat coverage.tmp.out | grep -v "mcp.go" | grep -v "ssh.go" | grep -v "http.go" | grep -v "tcp.go" | grep -v "builder.go" | grep -v "director.go" > coverage.out
totalCoverage=`go tool cover -func=coverage.out | grep total | grep -Eo '[0-9]+\.[0-9]+'`
echo "Current test coverage : $totalCoverage %"
if (( $(echo "$totalCoverage $TESTCOVERAGE_THRESHOLD" | awk '{print ($1 > $2)}') )); then
@ -51,6 +56,13 @@ jobs:
exit 1
fi
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
with:
files: ./coverage.out
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Start integration test dependencies
run: |
make test.dependencies.start

View File

@ -26,8 +26,13 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.24.1
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
@ -35,6 +40,6 @@ jobs:
run: go build ./...
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View File

@ -1,31 +1,32 @@
---
name: Docker Hub Image
on:
push:
tags:
- 'v*.*.*'
jobs:
CD:
runs-on: ubuntu-latest
steps:
-
name: Checkout
- name: Checkout
uses: actions/checkout@v3
-
name: Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
-
name: Set up Docker Buildx
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build and push
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
push: true
tags: m4r10/beelzebub:${{ github.ref_name }}
tags: |
m4r10/beelzebub:${{ github.ref_name }}
m4r10/beelzebub:latest
platforms: linux/amd64,linux/arm64

3
.gitignore vendored
View File

@ -2,4 +2,5 @@
.idea
logs
.vscode
.history
.history
coverage*.out

102
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,102 @@
# Contributing to Beelzebub
First off, thanks for taking the time to contribute! ❤️
All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉
> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about:
> - Star the project
> - Tweet about it
> - Refer this project in your project's readme
> - Mention the project at local meetups and tell your friends/colleagues
## Table of Contents
- [Code of Conduct](#code-of-conduct)
- [I Have a Question](#i-have-a-question)
- [I Want To Contribute](#i-want-to-contribute)
- [Reporting Bugs](#reporting-bugs)
- [Suggesting Enhancements](#suggesting-enhancements)
## Code of Conduct
This project and everyone participating in it is governed by the
[Beelzebub Code of Conduct](https://github.com/mariocandela/beelzebubblob/master/CODE_OF_CONDUCT.md).
By participating, you are expected to uphold this code. Please report unacceptable behavior
to <mario.candela.personal@gmail.com>.
## I Have a Question
> If you want to ask a question, we assume that you have read the available [Documentation](https://beelzebub-honeypot.com/docs/).
Before you ask a question, it is best to search for existing [Issues](https://github.com/mariocandela/beelzebub/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first.
If you then still feel the need to ask a question and need clarification, we recommend the following:
- Open an [Issue](https://github.com/mariocandela/beelzebub/issues/new).
- Provide as much context as you can about what you're running into.
- Provide project and platform versions (docker, beelzebub, etc), depending on what seems relevant.
We will then take care of the issue as soon as possible.
## I Want To Contribute
> ### Legal Notice <!-- omit in toc -->
> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license.
### Reporting Bugs
#### Before Submitting a Bug Report
A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible.
- Make sure that you are using the latest version.
- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](https://beelzebub-honeypot.com/docs/). If you are looking for support, you might want to check [this section](#i-have-a-question)).
- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/mariocandela/beelzebubissues?q=label%3Abug).
- Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue.
- Collect information about the bug:
- Stack trace (Traceback)
- OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
- Possibly your input and the output
- Can you reliably reproduce the issue? And can you also reproduce it with older versions?
#### How Do I Submit a Good Bug Report?
> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to <mario.candela.personal@gmail.com>.
We use GitHub issues to track bugs and errors. If you run into an issue with the project:
- Open an [Issue](https://github.com/mariocandela/beelzebub/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.)
- Explain the behavior you would expect and the actual behavior.
- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case.
- Provide the information you collected in the previous section.
Once it's filed:
- The project team will label the issue accordingly.
- A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced.
<!-- You might want to create an issue template for bugs and errors that can be used as a guide and that defines the structure of the information to be included. If you do so, reference it here in the description. -->
### Suggesting Enhancements
This section guides you through submitting an enhancement suggestion for Beelzebub, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions.
<!-- omit in toc -->
#### Before Submitting an Enhancement
- Make sure that you are using the latest version.
- Read the [documentation](https://beelzebub-honeypot.com/docs/) carefully and find out if the functionality is already covered, maybe by an individual configuration.
- Perform a [search](https://github.com/mariocandela/beelzebub/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library.
#### How Do I Submit a Good Enhancement Suggestion?
Enhancement suggestions are tracked as [GitHub issues](https://github.com/mariocandela/beelzebub/issues).
- Use a **clear and descriptive title** for the issue to identify the suggestion.
- Provide a **step-by-step description of the suggested enhancement** in as many details as possible.
- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you.
- **Explain why this enhancement would be useful** to most Beelzebub users. You may also want to point out the other projects that solved it better and which could serve as inspiration.

View File

@ -2,35 +2,28 @@ FROM golang:alpine AS builder
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
GOOS=linux
RUN apk add git
WORKDIR /build
# Copy and download dependency using go mod
COPY go.mod .
COPY go.sum .
# Download dependency
COPY . .
RUN go mod download
# Copy the code into the container
COPY . .
# Build the application
# Build
RUN go build -o main .
# Move to /dist directory as the place for resulting binary folder
WORKDIR /dist
# Copy binary from build to main folder
RUN cp /build/main .
# Build a small image
# Use scratch image as finally tiny container
FROM scratch
# copy the ca-certificate.crt from the builder stage
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /dist/main /
ENTRYPOINT ["/main"]
ENTRYPOINT ["/main"]

1014
LICENSE

File diff suppressed because it is too large Load Diff

239
README.md
View File

@ -1,32 +1,56 @@
# Beelzebub
[![CI](https://github.com/mariocandela/beelzebub/actions/workflows/ci.yml/badge.svg)](https://github.com/mariocandela/beelzebub/actions/workflows/ci.yml) [![Docker](https://github.com/mariocandela/beelzebub/actions/workflows/docker-image.yml/badge.svg)](https://github.com/mariocandela/beelzebub/actions/workflows/docker-image.yml) [![codeql](https://github.com/mariocandela/beelzebub/actions/workflows/codeql.yml/badge.svg)](https://github.com/mariocandela/beelzebub/actions/workflows/codeql.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/mariocandela/beelzebub/v3)](https://goreportcard.com/report/github.com/mariocandela/beelzebub/v3)
[![codecov](https://codecov.io/gh/mariocandela/beelzebub/graph/badge.svg?token=8XTK7D4WHE)](https://codecov.io/gh/mariocandela/beelzebub)
[![Go Reference](https://pkg.go.dev/badge/github.com/mariocandela/beelzebub/v3.svg)](https://pkg.go.dev/github.com/mariocandela/beelzebub/v3)
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)
## Overview
Beelzebub is an advanced honeypot framework designed to provide a highly secure environment for detecting and analyzing cyber attacks. It offers a low code approach for easy implementation and utilizes virtualization techniques powered by GPT-3, the OpenAI language model.
Beelzebub is an advanced honeypot framework designed to provide a highly secure environment for detecting and analyzing cyber attacks. It offers a low code approach for easy implementation and uses AI to mimic the behavior of a high-interaction honeypot.
![Beelzebub Logo](https://i.postimg.cc/KvbsJFp3/logo-1.png)
<img src="https://beelzebub.netlify.app/go-beelzebub.png" alt="Beelzebub Logo" width="200"/>
## OpenAI GPT Integration
## Key Features
Learn how to integrate Beelzebub with OpenAI GPT-3 by referring to our comprehensive guide on Medium: [Medium Article](https://medium.com/@mario.candela.personal/how-to-build-a-highly-effective-honeypot-with-beelzebub-and-chatgpt-a2f0f05b3e1)
Beelzebub offers a wide range of features to enhance your honeypot environment:
For a visual representation of the integration, you can explore the following diagram:
- Low-code configuration: YAML-based, modular service definition
- LLM integration: The LLM convincingly simulates a real system, creating high-interaction honeypot experiences, while actually maintaining low-interaction architecture for enhanced security and easy management.
- Multi-protocol support: SSH, HTTP, TCP, MCP(Detect prompt injection against LLM agents)
- Prometheus metrics & observability
- Docker & Kubernetes ready
- ELK stack ready, docs: [Official ELK integration](https://www.elastic.co/docs/reference/integrations/beelzebub)
[![OpenAI Integration Diagram](https://static.swimlanes.io/24d6634a381aa8eb0decf5bac7ae214d.png)](https://static.swimlanes.io/24d6634a381aa8eb0decf5bac7ae214d.png)
## LLM SSH Honeypot Demo
## Telegram Bot for Real-Time Attacks
[![asciicast](https://asciinema.org/a/665295.svg)](https://asciinema.org/a/665295)
Stay updated on real-time attacks by joining our dedicated Telegram channel: [Telegram Channel](https://t.me/beelzebubhoneypot)
## Code Quality
## Examples
We are strongly committed to maintaining high code quality in the Beelzebub project. Our development workflow includes comprehensive testing, code reviews, static analysis, and continuous integration to ensure the reliability and maintainability of the codebase.
To better understand the capabilities of Beelzebub, you can explore our example repository: [mariocandela/beelzebub-example](https://github.com/mariocandela/beelzebub-example)
### What We Do
* **Automated Testing:**
Both unit and integration tests are run on every pull request to catch regressions and ensure stability.
* **Static Analysis:**
We use tools like Go Report Card and CodeQL to automatically check for code quality, style, and security issues.
* **Code Coverage:**
Our test coverage is monitored with [Codecov](https://codecov.io/gh/mariocandela/beelzebub), and we aim for extensive coverage of all core components.
* **Continuous Integration:**
Every commit triggers automated CI pipelines on GitHub Actions, which run all tests and quality checks.
* **Code Reviews:**
All new contributions undergo peer review to maintain consistency and high standards across the project.
## Quick Start
We provide two quick start options for build and run Beelzebub: using Docker Compose or the Go compiler.
You can run Beelzebub via Docker, Go compiler(cross device), or Helm (Kubernetes).
### Using Docker Compose
@ -42,6 +66,7 @@ We provide two quick start options for build and run Beelzebub: using Docker Com
$ docker-compose up -d
```
### Using Go Compiler
1. Download the necessary Go modules:
@ -62,37 +87,21 @@ We provide two quick start options for build and run Beelzebub: using Docker Com
$ ./beelzebub
```
## Testing
### Deploy on kubernetes cluster using helm
We provide two types of tests: unit tests and integration tests.
1. Install helm
### Unit Tests
2. Deploy beelzebub:
To run unit tests:
```bash
$ helm install beelzebub ./beelzebub-chart
```
```bash
$ make test.unit
```
3. Next release
### Integration Tests
To run integration tests:
```bash
$ make test.integration
```
## Key Features
Beelzebub offers a wide range of features to enhance your honeypot environment:
- OpenAPI ChatBot GPT-3 Linux virtualization
- SSH Honeypot
- HTTP Honeypot
- TCP Honeypot
- Prometheus openmetrics integration
- Docker integration
- RabbitMQ integration
```bash
$ helm upgrade beelzebub ./beelzebub-chart
```
## Example Configuration
@ -106,7 +115,73 @@ $ ./beelzebub --confCore ./configurations/beelzebub.yaml --confServices ./config
Here are some example configurations for different honeypot scenarios:
#### Example HTTP Honeypot on Port 80
### MCP Honeypot
#### Why choose an MCP Honeypot?
An MCP honeypot is a **decoy tool** that the agent should never invoke under normal circumstances. Integrating this strategy into your agent pipeline offers three key benefits:
* **Real-time detection of guardrail bypass attempts.**
Instantly identify when a prompt injection attack successfully convinces the agent to invoke a restricted tool.
* **Automatic collection of real attack prompts for guardrail fine-tuning.**
Every activation logs genuine malicious prompts, enabling continuous improvement of your filtering mechanisms.
* **Continuous monitoring of attack trends through key metrics (HAR, TPR, MTP).**
Track exploit frequency and system resilience using objective, actionable measurements.
##### Example MCP Honeypot Configuration
###### mcp-8000.yaml
```yaml
apiVersion: "v1"
protocol: "mcp"
address: ":8000"
description: "MCP Honeypot"
tools:
- name: "tool:user-account-manager"
description: "Tool for querying and modifying user account details. Requires administrator privileges."
params:
- name: "user_id"
description: "The ID of the user account to manage."
- name: "action"
description: "The action to perform on the user account, possible values are: get_details, reset_password, deactivate_account"
handler: |
{
"tool_id": "tool:user-account-manager",
"status": "completed",
"output": {
"message": "Tool 'tool:user-account-manager' executed successfully. Results are pending internal processing and will be logged.",
"result": {
"operation_status": "success",
"details": "email: kirsten@gmail.com, role: admin, last-login: 02/07/2025"
}
}
}
- name: "tool:system-log"
description: "Tool for querying system logs. Requires administrator privileges."
params:
- name: "filter"
description: "The input used to filter the logs."
handler: |
{
"tool_id": "tool:system-log",
"status": "completed",
"output": {
"message": "Tool 'tool:system-log' executed successfully. Results are pending internal processing and will be logged.",
"result": {
"operation_status": "success",
"details": "Info: email: kirsten@gmail.com, last-login: 02/07/2025"
}
}
}
```
#### Invoke remotely: beelzebub:port/mcp (Streamable HTTPServer).
### HTTP Honeypot
###### http-80.yaml
@ -171,7 +246,7 @@ commands:
statusCode: 404
```
#### Example HTTP Honeypot on Port 8080
### HTTP Honeypot
###### http-8080.yaml
@ -189,27 +264,71 @@ commands:
statusCode: 401
```
#### Example SSH Honeypot
### SSH Honeypot
###### Honeypot with GPT-3 on Port 2222
###### LLM Honeypots
Follow a SSH LLM Honeypot using OpenAI as provider LLM:
```yaml
apiVersion: "v1"
protocol: "ssh"
address: ":2222"
description: "SSH interactive ChatGPT"
description: "SSH interactive OpenAI GPT-4"
commands:
- regex: "^(.+)$"
plugin: "OpenAIGPTLinuxTerminal"
plugin: "LLMHoneypot"
serverVersion: "OpenSSH"
serverName: "ubuntu"
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
deadlineTimeoutSeconds: 60
plugin:
openAPIChatGPTSecretKey: "Your OpenAI Secret Key"
llmProvider: "openai"
llmModel: "gpt-4o" #Models https://platform.openai.com/docs/models
openAISecretKey: "sk-proj-123456"
```
###### SSH Honeypot on Port 22
Examples with local Ollama instance using model codellama:7b:
```yaml
apiVersion: "v1"
protocol: "ssh"
address: ":2222"
description: "SSH Ollama Llama3"
commands:
- regex: "^(.+)$"
plugin: "LLMHoneypot"
serverVersion: "OpenSSH"
serverName: "ubuntu"
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
deadlineTimeoutSeconds: 60
plugin:
llmProvider: "ollama"
llmModel: "codellama:7b" #Models https://ollama.com/search
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
###### ssh-22.yaml
@ -243,7 +362,28 @@ passwordRegex: "^(root|qwerty|Smoker666)$"
deadlineTimeoutSeconds: 60
```
![Screenshot](https://i.postimg.cc/jdpfT0LB/Schermata-2022-06-02-alle-12-46-50.png)
## Testing
Maintaining excellent code quality is essential for security-focused projects like Beelzebub. We welcome all contributors who share our commitment to robust, readable, and reliable code!
### Unit Tests
For contributor, we have a comprehensive suite of unit/integration tests that cover the core functionality of Beelzebub. To run the unit tests, use the following command:
```bash
$ make test.unit
```
### Integration Tests
To run integration tests:
```bash
$ make test.dependencies.start
$ make test.integration
$ make test.dependencies.down
```
## Roadmap
@ -257,6 +397,9 @@ Happy hacking!
## License
Beelzebub is licensed under the [GNU GPL 3 License](LICENSE).
Beelzebub is licensed under the [MIT License](LICENSE).
[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate/?business=P75FH5LXKQTAC&no_recurring=0&currency_code=EUR)
## Supported by
[![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport)
![gitbook logo](https://i.postimg.cc/VNQh5hnk/gitbook.png)

View File

@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@ -0,0 +1,12 @@
apiVersion: v2
name: beelzebub-chart
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: "v3.0.0"
maintainers:
- name: mariocandela
email: mario.candela.personal@gmail.com
url: https://github.com/mariocandela

View File

@ -0,0 +1,28 @@
██████ ███████ ███████ ██ ███████ ███████ ██████ ██ ██ ██████
██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██
██████ █████ █████ ██ ███ █████ ██████ ██ ██ ██████
██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██
██████ ███████ ███████ ███████ ███████ ███████ ██████ ██████ ██████
Honeypot Framework, happy hacking!
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "beelzebub-chart.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "beelzebub-chart.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "beelzebub-chart.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "beelzebub-chart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your honeypot or ssh root@127.0.0.1 -p 8080 for ssh honeypot"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

View File

@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "beelzebub-chart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "beelzebub-chart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "beelzebub-chart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "beelzebub-chart.labels" -}}
helm.sh/chart: {{ include "beelzebub-chart.chart" . }}
{{ include "beelzebub-chart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "beelzebub-chart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "beelzebub-chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "beelzebub-chart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "beelzebub-chart.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,9 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: "beelzebub-configuration"
labels:
{{- include "beelzebub-chart.labels" . | nindent 4 }}
data:
beelzebub.yaml:
{{- toYaml .Values.beelzebubCoreConfigs | nindent 12 }}

View File

@ -0,0 +1,9 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: "beelzebub-services"
labels:
{{- include "beelzebub-chart.labels" . | nindent 4 }}
data:
service.yaml:
{{- toYaml .Values.beelsebubServiceConfigs | nindent 12 }}

View File

@ -0,0 +1,77 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "beelzebub-chart.fullname" . }}
labels:
{{- include "beelzebub-chart.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "beelzebub-chart.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "beelzebub-chart.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "beelzebub-chart.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
volumeMounts:
- name: config-core-volume
mountPath: /configurations/beelzebub.yaml
subPath: beelzebub.yaml
readOnly: true
- name: config-services-volume
mountPath: /configurations/services/service.yaml
subPath: service.yaml
readOnly: true
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
# livenessProbe:
# httpGet:
# path: /
# port: http
# readinessProbe:
# httpGet:
# path: /
# port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumes:
- name: config-core-volume
configMap:
name: beelzebub-configuration
- name: config-services-volume
configMap:
name: beelzebub-services
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@ -0,0 +1,32 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "beelzebub-chart.fullname" . }}
labels:
{{- include "beelzebub-chart.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "beelzebub-chart.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,61 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "beelzebub-chart.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "beelzebub-chart.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "beelzebub-chart.fullname" . }}
labels:
{{- include "beelzebub-chart.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "beelzebub-chart.selectorLabels" . | nindent 4 }}

View File

@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "beelzebub-chart.serviceAccountName" . }}
labels:
{{- include "beelzebub-chart.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "beelzebub-chart.fullname" . }}-test-connection"
labels:
{{- include "beelzebub-chart.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "beelzebub-chart.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View File

@ -0,0 +1,96 @@
# Default values for beelzebub-chart.
replicaCount: 1
image:
repository: m4r10/beelzebub
pullPolicy: IfNotPresent
tag: v3.0.0
beelzebubCoreConfigs: |
core:
logging:
debug: false
debugReportCaller: false
logDisableTimestamp: true
logsPath: ./logs
tracings:
rabbit-mq:
enabled: false
uri: ""
prometheus:
path: "/metrics"
port: ":2112"
beelsebubServiceConfigs: |
apiVersion: "v1"
protocol: "ssh"
address: ":2222"
description: "SSH interactive"
commands:
- regex: "^ls$"
handler: "Documents Images Desktop Downloads .m2 .kube .ssh .docker"
- regex: "^pwd$"
handler: "/home/"
- regex: "^uname -m$"
handler: "x86_64"
- regex: "^docker ps$"
handler: "CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES"
- regex: "^docker .*$"
handler: "Error response from daemon: dial unix docker.raw.sock: connect: connection refused"
- regex: "^uname$"
handler: "Linux"
- regex: "^ps$"
handler: " PID TTY TIME CMD\n21642 ttys000 0:00.07 /bin/dockerd"
- regex: "^(.+)$"
handler: "command not found"
serverVersion: "OpenSSH"
serverName: "ubuntu"
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
deadlineTimeoutSeconds: 60
# Port and address(beelsebubServiceConfigs) must equals.
service:
type: ClusterIP
port: 2222
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: "beelzebub-service-account"
podAnnotations: {}
podSecurityContext: {}
securityContext: {}
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
resources: {}
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}

View File

@ -1,16 +1,21 @@
package builder
import (
"beelzebub/parser"
"beelzebub/protocols"
"beelzebub/protocols/strategies"
"beelzebub/tracer"
"errors"
"fmt"
"github.com/mariocandela/beelzebub/v3/protocols/strategies/MCP"
"io"
"net/http"
"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"
amqp "github.com/rabbitmq/amqp091-go"
log "github.com/sirupsen/logrus"
@ -85,6 +90,14 @@ func (b *Builder) Close() error {
}
func (b *Builder) Run() error {
fmt.Println(
`
██████ ███████ ███████ ██ ███████ ███████ ██████ ██ ██ ██████
██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██
██████ █████ █████ ██ ███ █████ ██████ ██ ██ ██████
██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██
██████ ███████ ███████ ███████ ███████ ███████ ██████ ██████ ██████
Honeypot Framework, happy hacking!`)
// Init Prometheus openmetrics
go func() {
if (b.beelzebubCoreConfigurations.Core.Prometheus != parser.Prometheus{}) {
@ -97,31 +110,45 @@ func (b *Builder) Run() error {
}()
// Init Protocol strategies
secureShellStrategy := &strategies.SSHStrategy{}
hypertextTransferProtocolStrategy := &strategies.HTTPStrategy{}
transmissionControlProtocolStrategy := &strategies.TCPStrategy{}
secureShellStrategy := &SSH.SSHStrategy{}
hypertextTransferProtocolStrategy := &HTTP.HTTPStrategy{}
transmissionControlProtocolStrategy := &TCP.TCPStrategy{}
modelContextProtocolStrategy := &MCP.MCPStrategy{}
// Init Tracer strategies, and set the trace strategy default HTTP
protocolManager := protocols.InitProtocolManager(b.traceStrategy, hypertextTransferProtocolStrategy)
if b.beelzebubCoreConfigurations.Core.BeelzebubCloud.Enabled {
conf := b.beelzebubCoreConfigurations.Core.BeelzebubCloud
beelzebubCloud := plugins.InitBeelzebubCloud(conf.URI, conf.AuthToken)
if honeypotsConfiguration, err := beelzebubCloud.GetHoneypotsConfigurations(); err != nil {
return err
} else {
if len(honeypotsConfiguration) == 0 {
return errors.New("no honeypots configuration found")
}
b.beelzebubServicesConfiguration = honeypotsConfiguration
}
}
for _, beelzebubServiceConfiguration := range b.beelzebubServicesConfiguration {
switch beelzebubServiceConfiguration.Protocol {
case "http":
protocolManager.SetProtocolStrategy(hypertextTransferProtocolStrategy)
break
case "ssh":
protocolManager.SetProtocolStrategy(secureShellStrategy)
break
case "tcp":
protocolManager.SetProtocolStrategy(transmissionControlProtocolStrategy)
break
case "mcp":
protocolManager.SetProtocolStrategy(modelContextProtocolStrategy)
default:
log.Fatalf("Protocol %s not managed", beelzebubServiceConfiguration.Protocol)
continue
log.Fatalf("protocol %s not managed", beelzebubServiceConfiguration.Protocol)
}
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())
}
}

View File

@ -1,10 +1,11 @@
package builder
import (
"beelzebub/parser"
"beelzebub/tracer"
"context"
"encoding/json"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/plugins"
"github.com/mariocandela/beelzebub/v3/tracer"
amqp "github.com/rabbitmq/amqp091-go"
log "github.com/sirupsen/logrus"
@ -29,14 +30,18 @@ func (d *Director) BuildBeelzebub(beelzebubCoreConfigurations *parser.BeelzebubC
d.builder.setTraceStrategy(d.standardOutStrategy)
if beelzebubCoreConfigurations.Core.Tracing.RabbitMQEnabled {
if beelzebubCoreConfigurations.Core.Tracings.RabbitMQ.Enabled {
d.builder.setTraceStrategy(d.rabbitMQTraceStrategy)
err := d.builder.buildRabbitMQ(beelzebubCoreConfigurations.Core.Tracing.RabbitMQURI)
err := d.builder.buildRabbitMQ(beelzebubCoreConfigurations.Core.Tracings.RabbitMQ.URI)
if err != nil {
return nil, err
}
}
if beelzebubCoreConfigurations.Core.BeelzebubCloud.Enabled {
d.builder.setTraceStrategy(d.beelzebubCloudStrategy)
}
return d.builder.build(), nil
}
@ -47,6 +52,27 @@ func (d *Director) standardOutStrategy(event tracer.Event) {
}).Info("New Event")
}
func (d *Director) beelzebubCloudStrategy(event tracer.Event) {
log.WithFields(log.Fields{
"status": event.Status,
"event": event,
}).Info("New Event")
conf := d.builder.beelzebubCoreConfigurations.Core.BeelzebubCloud
beelzebubCloud := plugins.InitBeelzebubCloud(conf.URI, conf.AuthToken)
result, err := beelzebubCloud.SendEvent(event)
if err != nil {
log.Error(err.Error())
} else {
log.WithFields(log.Fields{
"status": result,
"event": event,
}).Debug("Event published on beelzebub cloud")
}
}
func (d *Director) rabbitMQTraceStrategy(event tracer.Event) {
log.WithFields(log.Fields{
"status": event.Status,

View File

@ -4,10 +4,14 @@ core:
debugReportCaller: false
logDisableTimestamp: true
logsPath: ./logs
tracing:
rabbitMQEnabled: false
rabbitMQURI: ""
tracings:
rabbit-mq:
enabled: false
uri: ""
prometheus:
path: "/metrics"
port: ":2112"
beelzebub-cloud:
enabled: false
uri: ""
auth-token: ""

View File

@ -18,41 +18,10 @@ commands:
- "Server: Apache/2.4.53 (Debian)"
- "X-Powered-By: PHP/7.4.29"
statusCode: 200
- regex: "^(/wp-login.php|/wp-admin)$"
handler:
<html>
<header>
<title>Wordpress 6 test page</title>
</header>
<body>
<form action="" method="post">
<label for="uname"><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="uname" required>
<label for="psw"><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="psw" required>
<button type="submit">Login</button>
</form>
</body>
</html>
headers:
- "Content-Type: text/html"
- "Server: Apache/2.4.53 (Debian)"
- "X-Powered-By: PHP/7.4.29"
statusCode: 200
- regex: "^.*$"
handler:
<html>
<header>
<title>404</title>
</header>
<body>
<h1>Not found!</h1>
</body>
</html>
headers:
- "Content-Type: text/html"
- "Server: Apache/2.4.53 (Debian)"
- "X-Powered-By: PHP/7.4.29"
statusCode: 404
plugin: "LLMHoneypot"
statusCode: 200
plugin:
llmProvider: "openai"
llmModel: "gpt-4o"
openAISecretKey: "sk-proj-123456"

View File

@ -0,0 +1,41 @@
apiVersion: "v1"
protocol: "mcp"
address: ":8000"
description: "MCP Honeypot"
tools:
- name: "tool:user-account-manager"
description: "Tool for querying and modifying user account details. Requires administrator privileges."
params:
- name: "user_id"
description: "The ID of the user account to manage."
- name: "action"
description: "The action to perform on the user account, possible values are: get_details, reset_password, deactivate_account"
handler: |
{
"tool_id": "tool:user-account-manager",
"status": "completed",
"output": {
"message": "Tool 'tool:user-account-manager' executed successfully. Results are pending internal processing and will be logged.",
"result": {
"operation_status": "success",
"details": "email: kirsten_12345@gmail.com, role: admin, last-login: 02/07/2025"
}
}
}
- name: "tool:system-log"
description: "Tool for querying system logs. Requires administrator privileges."
params:
- name: "filter"
description: "The input used to filter the logs."
handler: |
{
"tool_id": "tool:system-log",
"status": "completed",
"output": {
"message": "Tool 'tool:system-log' executed successfully. Results are pending internal processing and will be logged.",
"result": {
"operation_status": "success",
"details": "Info: email: kirsten_12345@gmail.com, last-login: 02/07/2025"
}
}
}

View File

@ -4,10 +4,12 @@ address: ":2222"
description: "SSH interactive ChatGPT"
commands:
- regex: "^(.+)$"
plugin: "OpenAIGPTLinuxTerminal"
plugin: "LLMHoneypot"
serverVersion: "OpenSSH"
serverName: "ubuntu"
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
deadlineTimeoutSeconds: 60
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456|1234)$"
deadlineTimeoutSeconds: 6000
plugin:
openAPIChatGPTSecretKey: ""
llmProvider: "openai"
llmModel: "gpt-4o"
openAISecretKey: "sk-proj-12345"

View File

@ -3,17 +3,18 @@ version: "3.9"
services:
beelzebub:
build: .
#network_mode: host # Not work on Mac OS
container_name: beelzebub
restart: always
ports: # Remove me, if you use configuration network_mode: host
ports:
- "22:22"
- "2222:2222"
- "8080:8080"
- "8081:8081"
- "80:80"
- "3306:3306"
- "2112:2112" # Prometheus openmetrics
- "2112:2112" #Prometheus Open Metrics
environment:
RABBITMQ_URI: ${RABBITMQ_URI}
OPEN_AI_SECRET_KEY: ${OPEN_AI_SECRET_KEY}
volumes:
- "./configurations:/configurations"

48
go.mod
View File

@ -1,38 +1,42 @@
module beelzebub
module github.com/mariocandela/beelzebub/v3
go 1.20
go 1.24
toolchain go1.24.1
require (
github.com/gliderlabs/ssh v0.3.5
github.com/go-resty/resty/v2 v2.7.0
github.com/google/uuid v1.3.0
github.com/jarcoal/httpmock v1.3.0
github.com/melbahja/goph v1.3.1
github.com/prometheus/client_golang v1.16.0
github.com/rabbitmq/amqp091-go v1.8.1
github.com/gliderlabs/ssh v0.3.8
github.com/go-resty/resty/v2 v2.16.5
github.com/google/uuid v1.6.0
github.com/jarcoal/httpmock v1.4.0
github.com/melbahja/goph v1.4.0
github.com/prometheus/client_golang v1.22.0
github.com/rabbitmq/amqp091-go v1.10.0
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.4
golang.org/x/crypto v0.10.0
github.com/stretchr/testify v1.10.0
golang.org/x/crypto v0.36.0
golang.org/x/term v0.32.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mark3labs/mcp-go v0.32.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/sftp v1.13.5 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.9.0 // indirect
golang.org/x/term v0.9.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sys v0.33.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
)

123
go.sum
View File

@ -2,86 +2,85 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
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/jarcoal/httpmock v1.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k=
github.com/jarcoal/httpmock v1.4.0/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
github.com/melbahja/goph v1.3.1 h1:FxFevAwCCpLkM4WBmnVVxcJBcBz6lKQpsN5biV2hA6w=
github.com/melbahja/goph v1.3.1/go.mod h1:uG+VfK2Dlhk+O32zFrRlc3kYKTlV6+BtvPWd/kK7U68=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mark3labs/mcp-go v0.32.0 h1:fgwmbfL2gbd67obg57OfV2Dnrhs1HtSdlY/i5fn7MU8=
github.com/mark3labs/mcp-go v0.32.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
github.com/maxatome/go-testdeep v1.14.0 h1:rRlLv1+kI8eOI3OaBXZwb3O7xY3exRzdW5QyX48g9wI=
github.com/maxatome/go-testdeep v1.14.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
github.com/melbahja/goph v1.4.0 h1:z0PgDbBFe66lRYl3v5dGb9aFgPy0kotuQ37QOwSQFqs=
github.com/melbahja/goph v1.4.0/go.mod h1:uG+VfK2Dlhk+O32zFrRlc3kYKTlV6+BtvPWd/kK7U68=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/rabbitmq/amqp091-go v1.8.1 h1:RejT1SBUim5doqcL6s7iN6SBmsQqyTgXb1xMlH0h1hA=
github.com/rabbitmq/amqp091-go v1.8.1/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
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/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
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-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
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-20211029224645-99673261e6eb/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-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -92,34 +91,30 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-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-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
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-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
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.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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
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-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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -0,0 +1,82 @@
package historystore
import (
"sync"
"time"
"github.com/mariocandela/beelzebub/v3/plugins"
)
var (
MaxHistoryAge = 60 * time.Minute
CleanerInterval = 1 * time.Minute
)
// HistoryStore is a thread-safe structure for storing Messages used to build LLM Context.
type HistoryStore struct {
sync.RWMutex
sessions map[string]HistoryEvent
}
// HistoryEvent is a container for storing messages
type HistoryEvent struct {
LastSeen time.Time
Messages []plugins.Message
}
// NewHistoryStore returns a prepared HistoryStore
func NewHistoryStore() *HistoryStore {
return &HistoryStore{
sessions: make(map[string]HistoryEvent),
}
}
// 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].Messages
}
// 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]HistoryEvent)
}
e, ok := hs.sessions[key]
if !ok {
e = HistoryEvent{}
}
e.LastSeen = time.Now()
e.Messages = append(e.Messages, message...)
hs.sessions[key] = e
}
// HistoryCleaner is a function that will periodically remove records from the HistoryStore
// that are older than MaxHistoryAge.
func (hs *HistoryStore) HistoryCleaner() {
cleanerTicker := time.NewTicker(CleanerInterval)
go func() {
for range cleanerTicker.C {
hs.Lock()
for k, v := range hs.sessions {
if time.Since(v.LastSeen) > MaxHistoryAge {
delete(hs.sessions, k)
}
}
hs.Unlock()
}
}()
}

View File

@ -0,0 +1,66 @@
package historystore
import (
"testing"
"time"
"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"] = HistoryEvent{Messages: []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"] = HistoryEvent{Messages: 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"].Messages)
hs.Append("testKey", message2)
assert.Equal(t, []plugins.Message{message1, message2}, hs.sessions["testKey"].Messages)
}
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"].Messages)
}
func TestHistoryCleaner(t *testing.T) {
hs := NewHistoryStore()
hs.Append("testKey", plugins.Message{Role: "user", Content: "Hello"})
hs.Append("testKey2", plugins.Message{Role: "user", Content: "Hello"})
// Make key older than MaxHistoryAge
e := hs.sessions["testKey"]
e.LastSeen = time.Now().Add(-MaxHistoryAge * 2)
hs.sessions["testKey"] = e
CleanerInterval = 5 * time.Second // Override for the test.
hs.HistoryCleaner()
time.Sleep(CleanerInterval + (1 * time.Second))
assert.False(t, hs.HasKey("testKey"))
assert.True(t, hs.HasKey("testKey2"))
}

View File

@ -4,9 +4,10 @@ core:
debugReportCaller: false
logDisableTimestamp: true
logsPath: ./logs
tracing:
rabbitMQEnabled: true
rabbitMQURI: "amqp://integration:integration@localhost:5672/"
tracings:
rabbit-mq:
enabled: true
uri: "amqp://integration:integration@localhost:5672/"
prometheus:
path: "/metrics"
port: ":2112"

View File

@ -1,10 +1,10 @@
package integration
import (
"beelzebub/builder"
"beelzebub/parser"
"beelzebub/tracer"
"encoding/json"
"github.com/mariocandela/beelzebub/v3/builder"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/tracer"
"net"
"net/http"
"os"
@ -48,7 +48,7 @@ func (suite *IntegrationTestSuite) SetupSuite() {
coreConfigurations, err := parser.ReadConfigurationsCore()
suite.Require().NoError(err)
suite.rabbitMQURI = coreConfigurations.Core.Tracing.RabbitMQURI
suite.rabbitMQURI = coreConfigurations.Core.Tracings.RabbitMQ.URI
beelzebubServicesConfiguration, err := parser.ReadConfigurationsServices()
suite.Require().NoError(err)
@ -67,8 +67,11 @@ func (suite *IntegrationTestSuite) TestInvokeHTTPHoneypot() {
response, err := resty.New().R().
Get(suite.httpHoneypotHost + "/index.php")
response.Header().Del("Date")
suite.Require().NoError(err)
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()))
response, err = resty.New().R().

32
main.go
View File

@ -1,41 +1,51 @@
package main
import (
"beelzebub/builder"
"beelzebub/parser"
"flag"
"fmt"
"runtime/debug"
"github.com/mariocandela/beelzebub/v3/builder"
"github.com/mariocandela/beelzebub/v3/parser"
log "github.com/sirupsen/logrus"
)
var quit = make(chan struct{})
func main() {
var configurationsCorePath string
var configurationsServicesDirectory string
var (
quit = make(chan struct{})
configurationsCorePath string
configurationsServicesDirectory string
memLimitMiB int
)
flag.StringVar(&configurationsCorePath, "confCore", "./configurations/beelzebub.yaml", "Provide the path of configurations core")
flag.StringVar(&configurationsServicesDirectory, "confServices", "./configurations/services/", "Directory config services")
flag.IntVar(&memLimitMiB, "memLimitMiB", 100, "Process Memory in MiB (default 100, set to -1 to use system default)")
flag.Parse()
if memLimitMiB > 0 {
// SetMemoryLimit takes an int64 value for the number of bytes.
// bytes value = MiB value * 1024 * 1024
debug.SetMemoryLimit(int64(memLimitMiB * 1024 * 1024))
}
parser := parser.Init(configurationsCorePath, configurationsServicesDirectory)
coreConfigurations, err := parser.ReadConfigurationsCore()
failOnError(err, fmt.Sprintf("Error during ReadConfigurationsCore: "))
failOnError(err, "Error during ReadConfigurationsCore: ")
beelzebubServicesConfiguration, err := parser.ReadConfigurationsServices()
failOnError(err, fmt.Sprintf("Error during ReadConfigurationsServices: "))
failOnError(err, "Error during ReadConfigurationsServices: ")
beelzebubBuilder := builder.NewBuilder()
director := builder.NewDirector(beelzebubBuilder)
beelzebubBuilder, err = director.BuildBeelzebub(coreConfigurations, beelzebubServicesConfiguration)
failOnError(err, fmt.Sprintf("Error during BuildBeelzebub: "))
failOnError(err, "Error during BuildBeelzebub: ")
err = beelzebubBuilder.Run()
failOnError(err, fmt.Sprintf("Error during run beelzebub core: "))
failOnError(err, "Error during run beelzebub core: ")
defer beelzebubBuilder.Close()

View File

@ -1,24 +1,28 @@
// Package parser is responsible for parsing the configurations of the core and honeypot service
package parser
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
)
// BeelzebubCoreConfigurations is the struct that contains the configurations of the core
type BeelzebubCoreConfigurations struct {
Core struct {
Logging Logging `yaml:"logging"`
Tracing Tracing `yaml:"tracing"`
Prometheus Prometheus `yaml:"prometheus"`
Logging Logging `yaml:"logging"`
Tracings Tracings `yaml:"tracings"`
Prometheus Prometheus `yaml:"prometheus"`
BeelzebubCloud BeelzebubCloud `yaml:"beelzebub-cloud"`
}
}
// Logging is the struct that contains the configurations of the logging
type Logging struct {
Debug bool `yaml:"debug"`
DebugReportCaller bool `yaml:"debugReportCaller"`
@ -26,25 +30,41 @@ type Logging struct {
LogsPath string `yaml:"logsPath,omitempty"`
}
type Tracing struct {
RabbitMQEnabled bool `yaml:"rabbitMQEnabled"`
RabbitMQURI string `yaml:"rabbitMQURI"`
// Tracings is the struct that contains the configurations of the tracings
type Tracings struct {
RabbitMQ `yaml:"rabbit-mq"`
}
type BeelzebubCloud struct {
Enabled bool `yaml:"enabled"`
URI string `yaml:"uri"`
AuthToken string `yaml:"auth-token"`
}
type RabbitMQ struct {
Enabled bool `yaml:"enabled"`
URI string `yaml:"uri"`
}
type Prometheus struct {
Path string `yaml:"path"`
Port string `yaml:"port"`
}
type Plugin struct {
OpenAPIChatGPTSecretKey string `yaml:"openAPIChatGPTSecretKey"`
OpenAISecretKey string `yaml:"openAISecretKey"`
Host string `yaml:"host"`
LLMModel string `yaml:"llmModel"`
LLMProvider string `yaml:"llmProvider"`
Prompt string `yaml:"prompt"`
}
// BeelzebubServiceConfiguration is the struct that contains the configurations of the honeypot service
type BeelzebubServiceConfiguration struct {
ApiVersion string `yaml:"apiVersion"`
Protocol string `yaml:"protocol"`
Address string `yaml:"address"`
Commands []Command `yaml:"commands"`
Tools []Tool `yaml:"tools"`
FallbackCommand Command `yaml:"fallbackCommand"`
ServerVersion string `yaml:"serverVersion"`
ServerName string `yaml:"serverName"`
DeadlineTimeoutSeconds int `yaml:"deadlineTimeoutSeconds"`
@ -52,14 +72,33 @@ type BeelzebubServiceConfiguration struct {
Description string `yaml:"description"`
Banner string `yaml:"banner"`
Plugin Plugin `yaml:"plugin"`
TLSCertPath string `yaml:"tlsCertPath"`
TLSKeyPath string `yaml:"tlsKeyPath"`
}
// Command is the struct that contains the configurations of the commands
type Command struct {
Regex string `yaml:"regex"`
Handler string `yaml:"handler"`
Headers []string `yaml:"headers"`
StatusCode int `yaml:"statusCode"`
Plugin string `yaml:"plugin"`
RegexStr string `yaml:"regex"`
Regex *regexp.Regexp `yaml:"-"` // This field is parsed, not stored in the config itself.
Handler string `yaml:"handler"`
Headers []string `yaml:"headers"`
StatusCode int `yaml:"statusCode"`
Plugin string `yaml:"plugin"`
Name string `yaml:"name"`
}
// Tool is the struct that contains the configurations of the MCP Honeypot
type Tool struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Params []Param `yaml:"params"`
Handler string `yaml:"handler"`
}
// Param is the struct that contains the configurations of the parameters of the tools
type Param struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
}
type configurationsParser struct {
@ -83,6 +122,7 @@ func Init(configurationsCorePath, configurationsServicesDirectory string) *confi
}
}
// ReadConfigurationsCore is the method that reads the configurations of the core from files
func (bp configurationsParser) ReadConfigurationsCore() (*BeelzebubCoreConfigurations, error) {
buf, err := bp.readFileBytesByFilePathDependency(bp.configurationsCorePath)
if err != nil {
@ -98,6 +138,7 @@ func (bp configurationsParser) ReadConfigurationsCore() (*BeelzebubCoreConfigura
return beelzebubConfiguration, nil
}
// ReadConfigurationsServices is the method that reads the configurations of the honeypot services from files
func (bp configurationsParser) ReadConfigurationsServices() ([]BeelzebubServiceConfiguration, error) {
services, err := bp.gelAllFilesNameByDirNameDependency(bp.configurationsServicesDirectory)
if err != nil {
@ -117,14 +158,31 @@ func (bp configurationsParser) ReadConfigurationsServices() ([]BeelzebubServiceC
return nil, fmt.Errorf("in file %s: %v", filePath, err)
}
log.Debug(beelzebubServiceConfiguration)
if err := beelzebubServiceConfiguration.CompileCommandRegex(); err != nil {
return nil, fmt.Errorf("in file %s: invalid regex: %v", filePath, err)
}
servicesConfiguration = append(servicesConfiguration, *beelzebubServiceConfiguration)
}
return servicesConfiguration, nil
}
// CompileCommandRegex is the method that compiles the regular expression for each configured Command.
func (c *BeelzebubServiceConfiguration) CompileCommandRegex() error {
for i, command := range c.Commands {
if command.RegexStr != "" {
rex, err := regexp.Compile(command.RegexStr)
if err != nil {
return err
}
c.Commands[i].Regex = rex
}
}
return nil
}
func gelAllFilesNameByDirName(dirName string) ([]string, error) {
files, err := ioutil.ReadDir(dirName)
files, err := os.ReadDir(dirName)
if err != nil {
return nil, err
}

View File

@ -2,8 +2,11 @@ package parser
import (
"errors"
"github.com/stretchr/testify/assert"
"os"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
)
func mockReadfilebytesConfigurationsCore(filePath string) ([]byte, error) {
@ -14,9 +17,14 @@ core:
debugReportCaller: false
logDisableTimestamp: true
logsPath: ./logs
tracing:
rabbitMQEnabled: true
rabbitMQURI: provaMock`)
tracings:
rabbit-mq:
enabled: true
uri: "amqp://user:password@localhost/"
beelzebub-cloud:
enabled: true
uri: "amqp://user:password@localhost/"
auth-token: "iejfdjsl-aosdajosoidaj-dunfkjnfkjsdnkn"`)
return configurationsCoreBytes, nil
}
@ -42,11 +50,37 @@ func mockReadfilebytesBeelzebubServiceConfiguration(filePath string) ([]byte, er
apiVersion: "v1"
protocol: "http"
address: ":8080"
tlsCertPath: "/tmp/cert.crt"
tlsKeyPath: "/tmp/cert.key"
tools:
- name: "tool:user-account-manager"
description: "Tool for querying and modifying user account details. Requires administrator privileges."
params:
- name: "user_id"
description: "The ID of the user account to manage."
- name: "action"
description: "The action to perform on the user account, possible values are: get_details, reset_password, deactivate_account"
handler: "reset_password ok"
commands:
- regex: "wp-admin"
handler: "login"
headers:
- "Content-Type: text/html"`)
- "Content-Type: text/html"
- name: "wp-admin"
regex: "wp-admin"
handler: "login"
headers:
- "Content-Type: text/html"
fallbackCommand:
handler: "404 Not Found!"
statusCode: 404
plugin:
openAISecretKey: "qwerty"
llmModel: "llama3"
llmProvider: "ollama"
host: "localhost:1563"
prompt: "hello world"
`)
return beelzebubServiceConfiguration, nil
}
@ -80,8 +114,11 @@ func TestReadConfigurationsCoreValid(t *testing.T) {
assert.Equal(t, coreConfigurations.Core.Logging.LogDisableTimestamp, true)
assert.Equal(t, coreConfigurations.Core.Logging.DebugReportCaller, false)
assert.Equal(t, coreConfigurations.Core.Logging.LogsPath, "./logs")
assert.Equal(t, coreConfigurations.Core.Tracing.RabbitMQEnabled, true)
assert.Equal(t, coreConfigurations.Core.Tracing.RabbitMQURI, "provaMock")
assert.Equal(t, coreConfigurations.Core.Tracings.RabbitMQ.Enabled, true)
assert.Equal(t, coreConfigurations.Core.Tracings.RabbitMQ.URI, "amqp://user:password@localhost/")
assert.Equal(t, coreConfigurations.Core.BeelzebubCloud.Enabled, true)
assert.Equal(t, coreConfigurations.Core.BeelzebubCloud.URI, "amqp://user:password@localhost/")
assert.Equal(t, coreConfigurations.Core.BeelzebubCloud.AuthToken, "iejfdjsl-aosdajosoidaj-dunfkjnfkjsdnkn")
}
func TestReadConfigurationsServicesFail(t *testing.T) {
@ -102,17 +139,164 @@ func TestReadConfigurationsServicesValid(t *testing.T) {
configurationsParser.gelAllFilesNameByDirNameDependency = mockReadDirValid
beelzebubServicesConfiguration, err := configurationsParser.ReadConfigurationsServices()
assert.Nil(t, err)
firstBeelzebubServiceConfiguration := beelzebubServicesConfiguration[0]
assert.Nil(t, err)
assert.Equal(t, firstBeelzebubServiceConfiguration.Protocol, "http")
assert.Equal(t, firstBeelzebubServiceConfiguration.ApiVersion, "v1")
assert.Equal(t, firstBeelzebubServiceConfiguration.Address, ":8080")
assert.Equal(t, len(firstBeelzebubServiceConfiguration.Commands), 1)
assert.Equal(t, len(firstBeelzebubServiceConfiguration.Commands), 1)
assert.Equal(t, firstBeelzebubServiceConfiguration.Commands[0].Regex, "wp-admin")
assert.Equal(t, len(firstBeelzebubServiceConfiguration.Commands), 2)
assert.Equal(t, len(firstBeelzebubServiceConfiguration.Commands), 2)
assert.Equal(t, firstBeelzebubServiceConfiguration.Commands[0].RegexStr, "wp-admin")
assert.Equal(t, firstBeelzebubServiceConfiguration.Commands[0].Regex.String(), "wp-admin")
assert.Equal(t, firstBeelzebubServiceConfiguration.Commands[0].Handler, "login")
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[1].Name, "wp-admin")
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.LLMModel, "llama3")
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.LLMProvider, "ollama")
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")
assert.Equal(t, firstBeelzebubServiceConfiguration.TLSKeyPath, "/tmp/cert.key")
assert.Equal(t, len(firstBeelzebubServiceConfiguration.Tools), 1)
assert.Equal(t, firstBeelzebubServiceConfiguration.Tools[0].Name, "tool:user-account-manager")
assert.Equal(t, firstBeelzebubServiceConfiguration.Tools[0].Description, "Tool for querying and modifying user account details. Requires administrator privileges.")
assert.Equal(t, len(firstBeelzebubServiceConfiguration.Tools[0].Params), 2)
assert.Equal(t, firstBeelzebubServiceConfiguration.Tools[0].Params[0].Name, "user_id")
assert.Equal(t, firstBeelzebubServiceConfiguration.Tools[0].Params[0].Description, "The ID of the user account to manage.")
assert.Equal(t, firstBeelzebubServiceConfiguration.Tools[0].Handler, "reset_password ok")
}
func TestGelAllFilesNameByDirName(t *testing.T) {
var dir = t.TempDir()
files, err := gelAllFilesNameByDirName(dir)
assert.Nil(t, err)
assert.Equal(t, 0, len(files))
}
func TestGelAllFilesNameByDirNameFiles(t *testing.T) {
var dir = t.TempDir()
testFiles := []string{"file1.yaml", "file2.yaml", "file3.txt", "subdir", "file4.yaml"}
for _, filename := range testFiles {
filePath := dir + "/" + filename
file, err := os.Create(filePath)
assert.NoError(t, err)
file.Close()
}
files, err := gelAllFilesNameByDirName(dir)
assert.Nil(t, err)
assert.Equal(t, 3, len(files))
}
func TestGelAllFilesNameByDirNameError(t *testing.T) {
files, err := gelAllFilesNameByDirName("nosuchfile")
assert.Nil(t, files)
// 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) {
var dir = t.TempDir()
filePath := dir + "/test.yaml"
f, err := os.Create(filePath)
assert.NoError(t, err)
f.Close()
bytes, err := readFileBytesByFilePath(filePath)
assert.NoError(t, err)
assert.Equal(t, "", string(bytes))
}
func TestCompileCommandRegex(t *testing.T) {
tests := []struct {
name string
config BeelzebubServiceConfiguration
expectedError bool
}{
{
name: "Valid Regex",
config: BeelzebubServiceConfiguration{
Commands: []Command{
{RegexStr: "^/api/v1/.*$"},
{RegexStr: "wp-admin"},
},
},
expectedError: false,
},
{
name: "Empty Regex",
config: BeelzebubServiceConfiguration{
Commands: []Command{
{RegexStr: ""},
{RegexStr: ""},
},
},
expectedError: false,
},
{
name: "Invalid Regex",
config: BeelzebubServiceConfiguration{
Commands: []Command{
{RegexStr: "["},
},
},
expectedError: true,
},
{
name: "Mixed valid and Invalid Regex",
config: BeelzebubServiceConfiguration{
Commands: []Command{
{RegexStr: "^/api/v1/.*$"},
{RegexStr: "["},
{RegexStr: "test"},
},
},
expectedError: true,
},
{
name: "No commands",
config: BeelzebubServiceConfiguration{},
expectedError: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.config.CompileCommandRegex()
if tt.expectedError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
for _, command := range tt.config.Commands {
if command.RegexStr != "" {
assert.NotNil(t, command.Regex)
_, err := regexp.Compile(command.RegexStr)
assert.NoError(t, err)
} else {
assert.Nil(t, command.Regex)
}
}
}
})
}
}

104
plugins/beelzebub-cloud.go Normal file
View File

@ -0,0 +1,104 @@
package plugins
import (
"encoding/json"
"errors"
"fmt"
"github.com/go-resty/resty/v2"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/tracer"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
)
type beelzebubCloud struct {
URI string
AuthToken string
client *resty.Client
}
type HoneypotConfigResponseDTO struct {
ID string `json:"id"`
Config string `json:"config"`
TokenID string `json:"tokenId"`
LastUpdatedOn string `json:"lastUpdatedOn"`
}
func InitBeelzebubCloud(uri, authToken string) *beelzebubCloud {
return &beelzebubCloud{
URI: uri,
AuthToken: authToken,
client: resty.New(),
}
}
func (beelzebubCloud *beelzebubCloud) SendEvent(event tracer.Event) (bool, error) {
requestJson, err := json.Marshal(event)
if err != nil {
return false, err
}
if beelzebubCloud.AuthToken == "" {
return false, errors.New("authToken is empty")
}
response, err := beelzebubCloud.client.R().
SetHeader("Content-Type", "application/json").
SetBody(requestJson).
SetHeader("Authorization", beelzebubCloud.AuthToken).
SetResult(&tracer.Event{}).
Post(fmt.Sprintf("%s/events", beelzebubCloud.URI))
log.Debug(response)
if err != nil {
return false, err
}
return response.StatusCode() == 200, nil
}
func (beelzebubCloud *beelzebubCloud) GetHoneypotsConfigurations() ([]parser.BeelzebubServiceConfiguration, error) {
if beelzebubCloud.AuthToken == "" {
return nil, errors.New("authToken is empty")
}
response, err := beelzebubCloud.client.R().
SetHeader("Content-Type", "application/json").
SetHeader("Authorization", beelzebubCloud.AuthToken).
SetResult([]HoneypotConfigResponseDTO{}).
Get(fmt.Sprintf("%s/honeypots", beelzebubCloud.URI))
if err != nil {
return nil, err
}
if response.StatusCode() != 200 {
return nil, errors.New(fmt.Sprintf("Response code: %v, error: %s", response.StatusCode(), string(response.Body())))
}
var honeypotsConfig []HoneypotConfigResponseDTO
if err = json.Unmarshal(response.Body(), &honeypotsConfig); err != nil {
return nil, err
}
var servicesConfiguration = make([]parser.BeelzebubServiceConfiguration, 0)
for _, honeypotConfig := range honeypotsConfig {
var honeypotsConfig parser.BeelzebubServiceConfiguration
if err = yaml.Unmarshal([]byte(honeypotConfig.Config), &honeypotsConfig); err != nil {
return nil, err
}
if err := honeypotsConfig.CompileCommandRegex(); err != nil {
return nil, fmt.Errorf("unable to load service config from cloud: invalid regex: %v", err)
}
servicesConfiguration = append(servicesConfiguration, honeypotsConfig)
}
log.Debug(servicesConfiguration)
return servicesConfiguration, nil
}

View File

@ -0,0 +1,233 @@
package plugins
import (
"fmt"
"net/http"
"regexp"
"testing"
"github.com/go-resty/resty/v2"
"github.com/jarcoal/httpmock"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/tracer"
"github.com/stretchr/testify/assert"
)
func TestBuildSendEventFailValidation(t *testing.T) {
beelzebubCloud := InitBeelzebubCloud("", "")
_, err := beelzebubCloud.SendEvent(tracer.Event{})
assert.Equal(t, "authToken is empty", err.Error())
}
func TestBuildSendEventWithResults(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
uri := "localhost:8081"
// Given
httpmock.RegisterResponder("POST", fmt.Sprintf("%s/events", uri),
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &tracer.Event{})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk")
beelzebubCloud.client = client
//When
result, err := beelzebubCloud.SendEvent(tracer.Event{})
//Then
assert.Equal(t, true, result)
assert.Nil(t, err)
}
func TestBuildSendEventErro(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
uri := "localhost:8081/events"
// Given
httpmock.RegisterResponder("POST", uri,
func(req *http.Request) (*http.Response, error) {
return httpmock.NewStringResponse(500, ""), nil
},
)
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk")
beelzebubCloud.client = client
//When
result, _ := beelzebubCloud.SendEvent(tracer.Event{})
//Then
assert.Equal(t, false, result)
}
func TestGetHoneypotsConfigurationsWithResults(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
uri := "localhost:8081"
// Given
httpmock.RegisterResponder("GET", fmt.Sprintf("%s/honeypots", uri),
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &[]HoneypotConfigResponseDTO{
{
ID: "123456",
Config: "apiVersion: \"v1\"\nprotocol: \"ssh\"\naddress: \":2222\"\ndescription: \"SSH interactive ChatGPT\"\ncommands:\n - regex: \"^(.+)$\"\n plugin: \"LLMHoneypot\"\nserverVersion: \"OpenSSH\"\nserverName: \"ubuntu\"\npasswordRegex: \"^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$\"\ndeadlineTimeoutSeconds: 60\nplugin:\n llmModel: \"gpt-4o\"\n openAISecretKey: \"1234\"\n",
TokenID: "1234567",
},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk")
beelzebubCloud.client = client
//When
result, err := beelzebubCloud.GetHoneypotsConfigurations()
//Then
assert.Equal(t, &[]parser.BeelzebubServiceConfiguration{
{
ApiVersion: "v1",
Protocol: "ssh",
Address: ":2222",
Description: "SSH interactive ChatGPT",
Commands: []parser.Command{
{
RegexStr: "^(.+)$",
Regex: regexp.MustCompile("^(.+)$"),
Plugin: "LLMHoneypot",
},
},
ServerVersion: "OpenSSH",
ServerName: "ubuntu",
PasswordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$",
DeadlineTimeoutSeconds: 60,
Plugin: parser.Plugin{
LLMModel: "gpt-4o",
OpenAISecretKey: "1234",
},
},
}, &result)
assert.Nil(t, err)
}
func TestGetHoneypotsConfigurationsWithErrorValidation(t *testing.T) {
//Given
beelzebubCloud := InitBeelzebubCloud("", "")
//When
result, err := beelzebubCloud.GetHoneypotsConfigurations()
//Then
assert.Nil(t, result)
assert.Equal(t, "authToken is empty", err.Error())
}
func TestGetHoneypotsConfigurationsWithErrorAPI(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
uri := "localhost:8081"
// Given
httpmock.RegisterResponder("GET", fmt.Sprintf("%s/honeypots", uri),
func(req *http.Request) (*http.Response, error) {
return httpmock.NewStringResponse(500, ""), nil
},
)
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk")
beelzebubCloud.client = client
//When
result, err := beelzebubCloud.GetHoneypotsConfigurations()
//Then
assert.Nil(t, result)
assert.Equal(t, "Response code: 500, error: ", err.Error())
}
func TestGetHoneypotsConfigurationsWithErrorUnmarshal(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
uri := "localhost:8081"
// Given
httpmock.RegisterResponder("GET", fmt.Sprintf("%s/honeypots", uri),
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, "error")
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk")
beelzebubCloud.client = client
//When
result, err := beelzebubCloud.GetHoneypotsConfigurations()
//Then
assert.Nil(t, result)
assert.Equal(t, "json: cannot unmarshal string into Go value of type []plugins.HoneypotConfigResponseDTO", err.Error())
}
func TestGetHoneypotsConfigurationsWithErrorDeserializeYaml(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
uri := "localhost:8081"
// Given
httpmock.RegisterResponder("GET", fmt.Sprintf("%s/honeypots", uri),
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &[]HoneypotConfigResponseDTO{
{
ID: "123456",
Config: "error",
TokenID: "1234567",
},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk")
beelzebubCloud.client = client
//When
result, err := beelzebubCloud.GetHoneypotsConfigurations()
//Then
assert.Nil(t, result)
assert.Equal(t, "yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `error` into parser.BeelzebubServiceConfiguration", err.Error())
}

252
plugins/llm-integration.go Normal file
View File

@ -0,0 +1,252 @@
package plugins
import (
"encoding/json"
"errors"
"fmt"
"github.com/go-resty/resty/v2"
"github.com/mariocandela/beelzebub/v3/tracer"
log "github.com/sirupsen/logrus"
"os"
"regexp"
"strings"
)
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 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."
LLMPluginName = "LLMHoneypot"
openAIEndpoint = "https://api.openai.com/v1/chat/completions"
ollamaEndpoint = "http://localhost:11434/api/chat"
)
type LLMHoneypot struct {
Histories []Message
OpenAIKey string
client *resty.Client
Protocol tracer.Protocol
Provider LLMProvider
Model string
Host string
CustomPrompt string
}
type Choice struct {
Message Message `json:"message"`
Index int `json:"index"`
FinishReason string `json:"finish_reason"`
}
type Response struct {
ID string `json:"id"`
Object string `json:"object"`
Created int `json:"created"`
Model string `json:"model"`
Choices []Choice `json:"choices"`
Message Message `json:"message"`
Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
} `json:"usage"`
}
type Request struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
Stream bool `json:"stream"`
}
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type Role int
const (
SYSTEM Role = iota
USER
ASSISTANT
)
func (role Role) String() string {
return [...]string{"system", "user", "assistant"}[role]
}
type LLMProvider int
const (
Ollama LLMProvider = iota
OpenAI
)
func FromStringToLLMProvider(llmProvider string) (LLMProvider, error) {
switch strings.ToLower(llmProvider) {
case "ollama":
return Ollama, nil
case "openai":
return OpenAI, nil
default:
return -1, fmt.Errorf("provider %s not found, valid providers: ollama, openai", llmProvider)
}
}
func InitLLMHoneypot(config LLMHoneypot) *LLMHoneypot {
// Inject the dependencies
config.client = resty.New()
if os.Getenv("OPEN_AI_SECRET_KEY") != "" {
config.OpenAIKey = os.Getenv("OPEN_AI_SECRET_KEY")
}
return &config
}
func (llmHoneypot *LLMHoneypot) buildPrompt(command string) ([]Message, error) {
var messages []Message
var prompt string
switch llmHoneypot.Protocol {
case tracer.SSH:
prompt = systemPromptVirtualizeLinuxTerminal
if llmHoneypot.CustomPrompt != "" {
prompt = llmHoneypot.CustomPrompt
}
messages = append(messages, Message{
Role: SYSTEM.String(),
Content: prompt,
})
messages = append(messages, Message{
Role: USER.String(),
Content: "pwd",
})
messages = append(messages, Message{
Role: ASSISTANT.String(),
Content: "/home/user",
})
for _, history := range llmHoneypot.Histories {
messages = append(messages, history)
}
case tracer.HTTP:
prompt = systemPromptVirtualizeHTTPServer
if llmHoneypot.CustomPrompt != "" {
prompt = llmHoneypot.CustomPrompt
}
messages = append(messages, Message{
Role: SYSTEM.String(),
Content: prompt,
})
messages = append(messages, Message{
Role: USER.String(),
Content: "GET /index.html",
})
messages = append(messages, Message{
Role: ASSISTANT.String(),
Content: "<html><body>Hello, World!</body></html>",
})
default:
return nil, errors.New("no prompt for protocol selected")
}
messages = append(messages, Message{
Role: USER.String(),
Content: command,
})
return messages, nil
}
func (llmHoneypot *LLMHoneypot) openAICaller(messages []Message) (string, error) {
var err error
requestJson, err := json.Marshal(Request{
Model: llmHoneypot.Model,
Messages: messages,
Stream: false,
})
if err != nil {
return "", err
}
if llmHoneypot.OpenAIKey == "" {
return "", errors.New("openAIKey is empty")
}
if llmHoneypot.Host == "" {
llmHoneypot.Host = openAIEndpoint
}
log.Debug(string(requestJson))
response, err := llmHoneypot.client.R().
SetHeader("Content-Type", "application/json").
SetBody(requestJson).
SetAuthToken(llmHoneypot.OpenAIKey).
SetResult(&Response{}).
Post(llmHoneypot.Host)
if err != nil {
return "", err
}
log.Debug(response)
if len(response.Result().(*Response).Choices) == 0 {
return "", errors.New("no choices")
}
return removeQuotes(response.Result().(*Response).Choices[0].Message.Content), nil
}
func (llmHoneypot *LLMHoneypot) ollamaCaller(messages []Message) (string, error) {
var err error
requestJson, err := json.Marshal(Request{
Model: llmHoneypot.Model,
Messages: messages,
Stream: false,
})
if err != nil {
return "", err
}
if llmHoneypot.Host == "" {
llmHoneypot.Host = ollamaEndpoint
}
log.Debug(string(requestJson))
response, err := llmHoneypot.client.R().
SetHeader("Content-Type", "application/json").
SetBody(requestJson).
SetResult(&Response{}).
Post(llmHoneypot.Host)
if err != nil {
return "", err
}
log.Debug(response)
return removeQuotes(response.Result().(*Response).Message.Content), nil
}
func (llmHoneypot *LLMHoneypot) ExecuteModel(command string) (string, error) {
var err error
var prompt []Message
prompt, err = llmHoneypot.buildPrompt(command)
if err != nil {
return "", err
}
switch llmHoneypot.Provider {
case Ollama:
return llmHoneypot.ollamaCaller(prompt)
case OpenAI:
return llmHoneypot.openAICaller(prompt)
default:
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, "")
}

View File

@ -0,0 +1,500 @@
package plugins
import (
"github.com/go-resty/resty/v2"
"github.com/jarcoal/httpmock"
"github.com/mariocandela/beelzebub/v3/tracer"
"github.com/stretchr/testify/assert"
"net/http"
"os"
"testing"
)
const SystemPromptLen = 4
func TestBuildPromptEmptyHistory(t *testing.T) {
//Given
var histories []Message
command := "pwd"
honeypot := LLMHoneypot{
Histories: histories,
Protocol: tracer.SSH,
}
//When
prompt, err := honeypot.buildPrompt(command)
//Then
assert.Nil(t, err)
assert.Equal(t, SystemPromptLen, len(prompt))
}
func TestBuildPromptWithHistory(t *testing.T) {
//Given
var histories = []Message{
{
Role: "cat hello.txt",
Content: "world",
},
}
command := "pwd"
honeypot := LLMHoneypot{
Histories: histories,
Protocol: tracer.SSH,
}
//When
prompt, err := honeypot.buildPrompt(command)
//Then
assert.Nil(t, err)
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) {
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
OpenAIKey: "",
Protocol: tracer.SSH,
Model: "gpt-4o",
Provider: OpenAI,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
_, err := openAIGPTVirtualTerminal.ExecuteModel("test")
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) {
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
OpenAIKey: "",
Protocol: tracer.TCP,
Model: "gpt-4o",
Provider: OpenAI,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
_, err := openAIGPTVirtualTerminal.ExecuteModel("test")
assert.Equal(t, "no prompt for protocol selected", err.Error())
}
func TestBuildExecuteModelFailValidationModelType(t *testing.T) {
// Given
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
Protocol: tracer.SSH,
Model: "llama3",
Provider: 5,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
//When
_, err := openAIGPTVirtualTerminal.ExecuteModel("ls")
//Then
assert.Errorf(t, err, "no model selected")
}
func TestBuildExecuteModelSSHWithResultsOpenAI(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", openAIEndpoint,
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &Response{
Choices: []Choice{
{
Message: Message{
Role: SYSTEM.String(),
Content: "prova.txt",
},
},
},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
OpenAIKey: "sdjdnklfjndslkjanfk",
Protocol: tracer.SSH,
Model: "gpt-4o",
Provider: OpenAI,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
openAIGPTVirtualTerminal.client = client
//When
str, err := openAIGPTVirtualTerminal.ExecuteModel("ls")
//Then
assert.Nil(t, err)
assert.Equal(t, "prova.txt", str)
}
func TestBuildExecuteModelSSHWithResultsLLama(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", ollamaEndpoint,
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &Response{
Message: Message{
Role: SYSTEM.String(),
Content: "prova.txt",
},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
Protocol: tracer.SSH,
Model: "llama3",
Provider: Ollama,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
openAIGPTVirtualTerminal.client = client
//When
str, err := openAIGPTVirtualTerminal.ExecuteModel("ls")
//Then
assert.Nil(t, err)
assert.Equal(t, "prova.txt", str)
}
func TestBuildExecuteModelSSHWithoutResults(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", openAIEndpoint,
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &Response{
Choices: []Choice{},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
OpenAIKey: "sdjdnklfjndslkjanfk",
Protocol: tracer.SSH,
Model: "gpt-4o",
Provider: OpenAI,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
openAIGPTVirtualTerminal.client = client
//When
_, err := openAIGPTVirtualTerminal.ExecuteModel("ls")
//Then
assert.Equal(t, "no choices", err.Error())
}
func TestBuildExecuteModelHTTPWithResults(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", openAIEndpoint,
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,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
openAIGPTVirtualTerminal.client = client
//When
str, err := openAIGPTVirtualTerminal.ExecuteModel("GET /.aws/credentials")
//Then
assert.Nil(t, err)
assert.Equal(t, "[default]\nregion = us-west-2\noutput = json", str)
}
func TestBuildExecuteModelHTTPWithoutResults(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", openAIEndpoint,
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &Response{
Choices: []Choice{},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
OpenAIKey: "sdjdnklfjndslkjanfk",
Protocol: tracer.HTTP,
Model: "gpt-4o",
Provider: OpenAI,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
openAIGPTVirtualTerminal.client = client
//When
_, err := openAIGPTVirtualTerminal.ExecuteModel("GET /.aws/credentials")
//Then
assert.Equal(t, "no choices", err.Error())
}
func TestFromString(t *testing.T) {
model, err := FromStringToLLMProvider("openai")
assert.Nil(t, err)
assert.Equal(t, OpenAI, model)
model, err = FromStringToLLMProvider("ollama")
assert.Nil(t, err)
assert.Equal(t, Ollama, model)
model, err = FromStringToLLMProvider("beelzebub-model")
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))
}

View File

@ -1,116 +0,0 @@
package plugins
import (
"encoding/json"
"errors"
"fmt"
"strings"
log "github.com/sirupsen/logrus"
"github.com/go-resty/resty/v2"
)
const ChatGPTPluginName = "OpenAIGPTLinuxTerminal"
const openAIGPTEndpoint = "https://api.openai.com/v1/completions"
type History struct {
Input, Output string
}
type OpenAIGPTVirtualTerminal struct {
Histories []History
OpenAPIChatGPTSecretKey string
client *resty.Client
}
func (openAIGPTVirtualTerminal *OpenAIGPTVirtualTerminal) InjectDependency() {
if openAIGPTVirtualTerminal.client == nil {
openAIGPTVirtualTerminal.client = resty.New()
}
}
type Choice struct {
Text string `json:"text"`
Index int `json:"index"`
Logprobs interface{} `json:"logprobs"`
FinishReason string `json:"finish_reason"`
}
type gptResponse struct {
ID string `json:"id"`
Object string `json:"object"`
Created int `json:"created"`
Model string `json:"model"`
Choices []Choice `json:"choices"`
Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
} `json:"usage"`
}
type gptRequest struct {
Model string `json:"model"`
Prompt string `json:"prompt"`
Temperature int `json:"temperature"`
MaxTokens int `json:"max_tokens"`
TopP int `json:"top_p"`
FrequencyPenalty int `json:"frequency_penalty"`
PresencePenalty int `json:"presence_penalty"`
Stop []string `json:"stop"`
}
// Reference: https://www.engraved.blog/building-a-virtual-machine-inside/
const promptVirtualizeLinuxTerminal = "I want you to act as a Linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. Do no write explanations. Do not type commands unless I instruct you to do so.\n\nA:pwd\n\nQ:/home/user\n\n"
func buildPrompt(histories []History, command string) string {
var sb strings.Builder
sb.WriteString(promptVirtualizeLinuxTerminal)
for _, history := range histories {
sb.WriteString(fmt.Sprintf("A:%s\n\nQ:%s\n\n", history.Input, history.Output))
}
// Append command to evaluate
sb.WriteString(fmt.Sprintf("A:%s\n\nQ:", command))
return sb.String()
}
func (openAIGPTVirtualTerminal *OpenAIGPTVirtualTerminal) GetCompletions(command string) (string, error) {
requestJson, err := json.Marshal(gptRequest{
Model: "text-davinci-003",
Prompt: buildPrompt(openAIGPTVirtualTerminal.Histories, command),
Temperature: 0,
MaxTokens: 100,
TopP: 1,
FrequencyPenalty: 0,
PresencePenalty: 0,
Stop: []string{"\n"},
})
if err != nil {
return "", err
}
if openAIGPTVirtualTerminal.OpenAPIChatGPTSecretKey == "" {
return "", errors.New("OpenAPIChatGPTSecretKey is empty")
}
response, err := openAIGPTVirtualTerminal.client.R().
SetHeader("Content-Type", "application/json").
SetBody(requestJson).
SetAuthToken(openAIGPTVirtualTerminal.OpenAPIChatGPTSecretKey).
SetResult(&gptResponse{}).
Post(openAIGPTEndpoint)
if err != nil {
return "", err
}
log.Debug(response)
if len(response.Result().(*gptResponse).Choices) == 0 {
return "", errors.New("no choices")
}
return response.Result().(*gptResponse).Choices[0].Text, nil
}

View File

@ -1,82 +0,0 @@
package plugins
import (
"github.com/go-resty/resty/v2"
"github.com/jarcoal/httpmock"
"github.com/stretchr/testify/assert"
"net/http"
"testing"
)
func TestBuildPromptEmptyHistory(t *testing.T) {
//Given
var histories []History
command := "pwd"
//When
prompt := buildPrompt(histories, command)
//Then
assert.Equal(t,
"I want you to act as a Linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. Do no write explanations. Do not type commands unless I instruct you to do so.\n\nA:pwd\n\nQ:/home/user\n\nA:pwd\n\nQ:",
prompt)
}
func TestBuildPromptWithHistory(t *testing.T) {
//Given
var histories = []History{
{
Input: "cat hello.txt",
Output: "world",
},
{
Input: "echo 1234",
Output: "1234",
},
}
command := "pwd"
//When
prompt := buildPrompt(histories, command)
//Then
assert.Equal(t,
"I want you to act as a Linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. Do no write explanations. Do not type commands unless I instruct you to do so.\n\nA:pwd\n\nQ:/home/user\n\nA:cat hello.txt\n\nQ:world\n\nA:echo 1234\n\nQ:1234\n\nA:pwd\n\nQ:",
prompt)
}
func TestBuildGetCompletions(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &gptResponse{
Choices: []Choice{
{
Text: "prova.txt",
},
},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
openAIGPTVirtualTerminal := OpenAIGPTVirtualTerminal{
OpenAPIChatGPTSecretKey: "sdjdnklfjndslkjanfk",
client: client,
}
//When
str, err := openAIGPTVirtualTerminal.GetCompletions("ls")
//Then
assert.Nil(t, err)
assert.Equal(t, "prova.txt", str)
}

View File

@ -1,10 +1,12 @@
// Package protocols is responsible for managing the different protocols
package protocols
import (
"beelzebub/parser"
"beelzebub/tracer"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/tracer"
)
// ServiceStrategy is the common interface that each protocol honeypot implements
type ServiceStrategy interface {
Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tracer tracer.Tracer) error
}
@ -14,10 +16,11 @@ type ProtocolManager struct {
tracer tracer.Tracer
}
func InitProtocolManager(tracerStrategy tracer.Strategy, strategy ServiceStrategy) *ProtocolManager {
// InitProtocolManager is the method that initializes the protocol manager, receving the concrete tracer and the concrete service
func InitProtocolManager(tracerStrategy tracer.Strategy, serviceStrategy ServiceStrategy) *ProtocolManager {
return &ProtocolManager{
tracer: tracer.Init(tracerStrategy),
strategy: strategy,
tracer: tracer.GetInstance(tracerStrategy),
strategy: serviceStrategy,
}
}
@ -25,6 +28,7 @@ func (pm *ProtocolManager) SetProtocolStrategy(strategy ServiceStrategy) {
pm.strategy = strategy
}
// InitService is the method that initializes the honeypot
func (pm *ProtocolManager) InitService(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration) error {
return pm.strategy.Init(beelzebubServiceConfiguration, pm.tracer)
}

View File

@ -1,9 +1,9 @@
package protocols
import (
"beelzebub/parser"
"beelzebub/tracer"
"errors"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/tracer"
"github.com/stretchr/testify/assert"
"testing"
)

View File

@ -0,0 +1,192 @@
package HTTP
import (
"fmt"
"io"
"net"
"net/http"
"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) {
var matched bool
var resp httpResponse
var err error
for _, command := range servConf.Commands {
var err error
matched = command.Regex.MatchString(request.RequestURI)
if matched {
resp, err = buildHTTPResponse(servConf, tr, 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, tr, 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, tr tracer.Tracer, command parser.Command, request *http.Request) (httpResponse, error) {
resp := httpResponse{
Body: command.Handler,
Headers: command.Headers,
StatusCode: command.StatusCode,
}
traceRequest(request, tr, command, servConf.Description)
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, command parser.Command, 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: mapHeaderToString(request.Header),
HeadersMap: request.Header,
Status: tracer.Stateless.String(),
RemoteAddr: request.RemoteAddr,
SourceIp: host,
SourcePort: port,
ID: uuid.New().String(),
Description: HoneypotDescription,
Handler: command.Name,
}
// 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 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)
}
}

View File

@ -0,0 +1,86 @@
package MCP
import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/tracer"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
log "github.com/sirupsen/logrus"
"net"
"net/http"
)
type remoteAddrCtxKey struct{}
type MCPStrategy struct {
}
func (mcpStrategy *MCPStrategy) Init(servConf parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
mcpServer := server.NewMCPServer(
servConf.Description,
"1.0.0",
server.WithToolCapabilities(false),
)
for _, toolConfig := range servConf.Tools {
if toolConfig.Params == nil || len(toolConfig.Params) == 0 {
log.Errorf("Tool %s has no parameters defined", toolConfig.Name)
continue
}
opts := []mcp.ToolOption{
mcp.WithDescription(toolConfig.Description),
}
for _, param := range toolConfig.Params {
opts = append(opts,
mcp.WithString(
param.Name,
mcp.Required(),
mcp.Description(param.Description),
),
)
}
tool := mcp.NewTool(toolConfig.Name, opts...)
mcpServer.AddTool(tool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
host, port, _ := net.SplitHostPort(ctx.Value(remoteAddrCtxKey{}).(string))
tr.TraceEvent(tracer.Event{
Msg: "New MCP tool invocation",
Protocol: tracer.MCP.String(),
Status: tracer.Stateless.String(),
RemoteAddr: ctx.Value(remoteAddrCtxKey{}).(string),
SourceIp: host,
SourcePort: port,
ID: uuid.New().String(),
Description: servConf.Description,
Command: fmt.Sprintf("%s|%s", request.Params.Name, request.Params.Arguments),
CommandOutput: toolConfig.Handler,
})
return mcp.NewToolResultText(toolConfig.Handler), nil
})
}
go func() {
httpServer := server.NewStreamableHTTPServer(
mcpServer,
server.WithHTTPContextFunc(func(ctx context.Context, r *http.Request) context.Context {
return context.WithValue(ctx, remoteAddrCtxKey{}, r.RemoteAddr)
}),
)
if err := httpServer.Start(servConf.Address); err != nil {
log.Errorf("Failed to start MCP server on %s: %v", servConf.Address, err)
return
}
}()
log.WithFields(log.Fields{
"port": servConf.Address,
"description": servConf.Description,
}).Infof("Init service %s", servConf.Protocol)
return nil
}

View File

@ -0,0 +1,225 @@
package SSH
import (
"fmt"
"net"
"regexp"
"strings"
"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/google/uuid"
log "github.com/sirupsen/logrus"
"golang.org/x/term"
)
type SSHStrategy struct {
Sessions *historystore.HistoryStore
}
func (sshStrategy *SSHStrategy) Init(servConf parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
if sshStrategy.Sessions == nil {
sshStrategy.Sessions = historystore.NewHistoryStore()
}
go sshStrategy.Sessions.HistoryCleaner()
go func() {
server := &ssh.Server{
Addr: servConf.Address,
MaxTimeout: time.Duration(servConf.DeadlineTimeoutSeconds) * time.Second,
IdleTimeout: time.Duration(servConf.DeadlineTimeoutSeconds) * time.Second,
Version: servConf.ServerVersion,
Handler: func(sess ssh.Session) {
uuidSession := uuid.New()
host, port, _ := net.SplitHostPort(sess.RemoteAddr().String())
sessionKey := "SSH" + host + sess.User()
// Inline SSH command
if sess.RawCommand() != "" {
var histories []plugins.Message
if sshStrategy.Sessions.HasKey(sessionKey) {
histories = sshStrategy.Sessions.Query(sessionKey)
}
for _, command := range servConf.Commands {
if command.Regex.MatchString(sess.RawCommand()) {
commandOutput := command.Handler
if command.Plugin == plugins.LLMPluginName {
llmProvider, err := plugins.FromStringToLLMProvider(servConf.Plugin.LLMProvider)
if err != nil {
log.Errorf("error: %s", err.Error())
commandOutput = "command not found"
llmProvider = plugins.OpenAI
}
llmHoneypot := plugins.LLMHoneypot{
Histories: histories,
OpenAIKey: servConf.Plugin.OpenAISecretKey,
Protocol: tracer.SSH,
Host: servConf.Plugin.Host,
Model: servConf.Plugin.LLMModel,
Provider: llmProvider,
CustomPrompt: servConf.Plugin.Prompt,
}
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
if commandOutput, err = llmHoneypotInstance.ExecuteModel(sess.RawCommand()); err != nil {
log.Errorf("error ExecuteModel: %s, %s", sess.RawCommand(), err.Error())
commandOutput = "command not found"
}
}
var newEntries []plugins.Message
newEntries = append(newEntries, plugins.Message{Role: plugins.USER.String(), Content: sess.RawCommand()})
newEntries = append(newEntries, plugins.Message{Role: plugins.ASSISTANT.String(), Content: commandOutput})
// Append the new entries to the store.
sshStrategy.Sessions.Append(sessionKey, newEntries...)
sess.Write(append([]byte(commandOutput), '\n'))
tr.TraceEvent(tracer.Event{
Msg: "SSH Raw Command",
Protocol: tracer.SSH.String(),
RemoteAddr: sess.RemoteAddr().String(),
SourceIp: host,
SourcePort: port,
Status: tracer.Start.String(),
ID: uuidSession.String(),
Environ: strings.Join(sess.Environ(), ","),
User: sess.User(),
Description: servConf.Description,
Command: sess.RawCommand(),
CommandOutput: commandOutput,
Handler: command.Name,
})
return
}
}
}
tr.TraceEvent(tracer.Event{
Msg: "New SSH Terminal Session",
Protocol: tracer.SSH.String(),
RemoteAddr: sess.RemoteAddr().String(),
SourceIp: host,
SourcePort: port,
Status: tracer.Start.String(),
ID: uuidSession.String(),
Environ: strings.Join(sess.Environ(), ","),
User: sess.User(),
Description: servConf.Description,
})
terminal := term.NewTerminal(sess, buildPrompt(sess.User(), servConf.ServerName))
var histories []plugins.Message
if sshStrategy.Sessions.HasKey(sessionKey) {
histories = sshStrategy.Sessions.Query(sessionKey)
}
for {
commandInput, err := terminal.ReadLine()
if err != nil {
break
}
if commandInput == "exit" {
break
}
for _, command := range servConf.Commands {
if command.Regex.MatchString(commandInput) {
commandOutput := command.Handler
if command.Plugin == plugins.LLMPluginName {
llmProvider, err := plugins.FromStringToLLMProvider(servConf.Plugin.LLMProvider)
if err != nil {
log.Errorf("error: %s, fallback OpenAI", err.Error())
llmProvider = plugins.OpenAI
}
llmHoneypot := plugins.LLMHoneypot{
Histories: histories,
OpenAIKey: servConf.Plugin.OpenAISecretKey,
Protocol: tracer.SSH,
Host: servConf.Plugin.Host,
Model: servConf.Plugin.LLMModel,
Provider: llmProvider,
CustomPrompt: servConf.Plugin.Prompt,
}
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
if commandOutput, err = llmHoneypotInstance.ExecuteModel(commandInput); err != nil {
log.Errorf("error ExecuteModel: %s, %s", commandInput, err.Error())
commandOutput = "command not found"
}
}
var newEntries []plugins.Message
newEntries = append(newEntries, plugins.Message{Role: plugins.USER.String(), Content: commandInput})
newEntries = append(newEntries, plugins.Message{Role: plugins.ASSISTANT.String(), Content: commandOutput})
// Stash the new entries to the store, and update the history for this running session.
sshStrategy.Sessions.Append(sessionKey, newEntries...)
histories = append(histories, newEntries...)
terminal.Write(append([]byte(commandOutput), '\n'))
tr.TraceEvent(tracer.Event{
Msg: "SSH Terminal Session Interaction",
RemoteAddr: sess.RemoteAddr().String(),
SourceIp: host,
SourcePort: port,
Status: tracer.Interaction.String(),
Command: commandInput,
CommandOutput: commandOutput,
ID: uuidSession.String(),
Protocol: tracer.SSH.String(),
Description: servConf.Description,
Handler: command.Name,
})
break // Inner range over commands.
}
}
}
tr.TraceEvent(tracer.Event{
Msg: "End SSH Session",
Status: tracer.End.String(),
ID: uuidSession.String(),
Protocol: tracer.SSH.String(),
})
},
PasswordHandler: func(ctx ssh.Context, password string) bool {
host, port, _ := net.SplitHostPort(ctx.RemoteAddr().String())
tr.TraceEvent(tracer.Event{
Msg: "New SSH Login Attempt",
Protocol: tracer.SSH.String(),
Status: tracer.Stateless.String(),
User: ctx.User(),
Password: password,
Client: ctx.ClientVersion(),
RemoteAddr: ctx.RemoteAddr().String(),
SourceIp: host,
SourcePort: port,
ID: uuid.New().String(),
Description: servConf.Description,
})
matched, err := regexp.MatchString(servConf.PasswordRegex, password)
if err != nil {
log.Errorf("error regex: %s, %s", servConf.PasswordRegex, err.Error())
return false
}
return matched
},
}
err := server.ListenAndServe()
if err != nil {
log.Errorf("error during init SSH Protocol: %s", err.Error())
}
}()
log.WithFields(log.Fields{
"port": servConf.Address,
"commands": len(servConf.Commands),
}).Infof("GetInstance service %s", servConf.Protocol)
return nil
}
func buildPrompt(user string, serverName string) string {
return fmt.Sprintf("%s@%s:~$ ", user, serverName)
}

View File

@ -1,12 +1,13 @@
package strategies
package TCP
import (
"beelzebub/parser"
"beelzebub/tracer"
"fmt"
"net"
"time"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/tracer"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
)
@ -14,8 +15,8 @@ import (
type TCPStrategy struct {
}
func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
listen, err := net.Listen("tcp", beelzebubServiceConfiguration.Address)
func (tcpStrategy *TCPStrategy) Init(servConf parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
listen, err := net.Listen("tcp", servConf.Address)
if err != nil {
log.Errorf("Error during init TCP Protocol: %s", err.Error())
return err
@ -25,8 +26,8 @@ func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.Beelze
for {
if conn, err := listen.Accept(); err == nil {
go func() {
conn.SetDeadline(time.Now().Add(time.Duration(beelzebubServiceConfiguration.DeadlineTimeoutSeconds) * time.Second))
conn.Write([]byte(fmt.Sprintf("%s\n", beelzebubServiceConfiguration.Banner)))
conn.SetDeadline(time.Now().Add(time.Duration(servConf.DeadlineTimeoutSeconds) * time.Second))
conn.Write(fmt.Appendf([]byte{}, "%s\n", servConf.Banner))
buffer := make([]byte, 1024)
command := ""
@ -35,14 +36,18 @@ func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.Beelze
command = string(buffer[:n])
}
host, port, _ := net.SplitHostPort(conn.RemoteAddr().String())
tr.TraceEvent(tracer.Event{
Msg: "New TCP attempt",
Protocol: tracer.TCP.String(),
Command: command,
Status: tracer.Stateless.String(),
RemoteAddr: conn.RemoteAddr().String(),
SourceIp: host,
SourcePort: port,
ID: uuid.New().String(),
Description: beelzebubServiceConfiguration.Description,
Description: servConf.Description,
})
conn.Close()
}()
@ -51,8 +56,8 @@ func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.Beelze
}()
log.WithFields(log.Fields{
"port": beelzebubServiceConfiguration.Address,
"banner": beelzebubServiceConfiguration.Banner,
}).Infof("Init service %s", beelzebubServiceConfiguration.Protocol)
"port": servConf.Address,
"banner": servConf.Banner,
}).Infof("Init service %s", servConf.Protocol)
return nil
}

View File

@ -1,111 +0,0 @@
package strategies
import (
"beelzebub/parser"
"beelzebub/tracer"
"fmt"
"io"
"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 {
setResponseHeaders(responseWriter, command.Headers, command.StatusCode)
fmt.Fprintf(responseWriter, command.Handler)
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)
}
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,
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)
}
}

View File

@ -1,133 +0,0 @@
package strategies
import (
"beelzebub/parser"
"beelzebub/plugins"
"beelzebub/tracer"
"fmt"
"regexp"
"strings"
"time"
"github.com/gliderlabs/ssh"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh/terminal"
)
type SSHStrategy struct {
}
func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
go func() {
server := &ssh.Server{
Addr: beelzebubServiceConfiguration.Address,
MaxTimeout: time.Duration(beelzebubServiceConfiguration.DeadlineTimeoutSeconds) * time.Second,
IdleTimeout: time.Duration(beelzebubServiceConfiguration.DeadlineTimeoutSeconds) * time.Second,
Version: beelzebubServiceConfiguration.ServerVersion,
Handler: func(sess ssh.Session) {
uuidSession := uuid.New()
tr.TraceEvent(tracer.Event{
Msg: "New SSH Session",
Protocol: tracer.SSH.String(),
RemoteAddr: sess.RemoteAddr().String(),
Status: tracer.Start.String(),
ID: uuidSession.String(),
Environ: strings.Join(sess.Environ(), ","),
User: sess.User(),
Description: beelzebubServiceConfiguration.Description,
Command: sess.RawCommand(),
})
term := terminal.NewTerminal(sess, buildPrompt(sess.User(), beelzebubServiceConfiguration.ServerName))
var histories []plugins.History
for {
commandInput, err := term.ReadLine()
if err != nil {
break
}
if commandInput == "exit" {
break
}
for _, command := range beelzebubServiceConfiguration.Commands {
matched, err := regexp.MatchString(command.Regex, commandInput)
if err != nil {
log.Errorf("Error regex: %s, %s", command.Regex, err.Error())
continue
}
if matched {
commandOutput := command.Handler
if command.Plugin == plugins.ChatGPTPluginName {
openAIGPTVirtualTerminal := plugins.OpenAIGPTVirtualTerminal{Histories: histories, OpenAPIChatGPTSecretKey: beelzebubServiceConfiguration.Plugin.OpenAPIChatGPTSecretKey}
openAIGPTVirtualTerminal.InjectDependency()
if commandOutput, err = openAIGPTVirtualTerminal.GetCompletions(commandInput); err != nil {
log.Errorf("Error GetCompletions: %s, %s", commandInput, err.Error())
commandOutput = "command not found"
}
}
histories = append(histories, plugins.History{Input: commandInput, Output: commandOutput})
term.Write(append([]byte(commandOutput), '\n'))
tr.TraceEvent(tracer.Event{
Msg: "New SSH Terminal Session",
RemoteAddr: sess.RemoteAddr().String(),
Status: tracer.Interaction.String(),
Command: commandInput,
CommandOutput: commandOutput,
ID: uuidSession.String(),
Protocol: tracer.SSH.String(),
Description: beelzebubServiceConfiguration.Description,
})
break
}
}
}
tr.TraceEvent(tracer.Event{
Msg: "End SSH Session",
Status: tracer.End.String(),
ID: uuidSession.String(),
})
},
PasswordHandler: func(ctx ssh.Context, password string) bool {
tr.TraceEvent(tracer.Event{
Msg: "New SSH attempt",
Protocol: tracer.SSH.String(),
Status: tracer.Stateless.String(),
User: ctx.User(),
Password: password,
Client: ctx.ClientVersion(),
RemoteAddr: ctx.RemoteAddr().String(),
ID: uuid.New().String(),
Description: beelzebubServiceConfiguration.Description,
})
matched, err := regexp.MatchString(beelzebubServiceConfiguration.PasswordRegex, password)
if err != nil {
log.Errorf("Error regex: %s, %s", beelzebubServiceConfiguration.PasswordRegex, err.Error())
return false
}
return matched
},
}
err := server.ListenAndServe()
if err != nil {
log.Errorf("Error during init SSH Protocol: %s", err.Error())
}
}()
log.WithFields(log.Fields{
"port": beelzebubServiceConfiguration.Address,
"commands": len(beelzebubServiceConfiguration.Commands),
}).Infof("Init service %s", beelzebubServiceConfiguration.Protocol)
return nil
}
func buildPrompt(user string, serverName string) string {
return fmt.Sprintf("%s@%s:~$ ", user, serverName)
}

View File

@ -1,12 +1,17 @@
// Package tracer is responsible for tracing the events that occur in the honeypots
package tracer
import (
"sync"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
log "github.com/sirupsen/logrus"
)
const Workers = 5
type Event struct {
DateTime string
RemoteAddr string
@ -21,6 +26,7 @@ type Event struct {
Password string
Client string
Headers string
HeadersMap map[string][]string
Cookies string
UserAgent string
HostHTTPRequest string
@ -28,22 +34,28 @@ type Event struct {
HTTPMethod string
RequestURI string
Description string
SourceIp string
SourcePort string
TLSServerName string
Handler string
}
type Protocol int
type (
Protocol int
Status int
)
const (
HTTP Protocol = iota
SSH
TCP
MCP
)
func (status Protocol) String() string {
return [...]string{"HTTP", "SSH", "TCP"}[status]
func (protocol Protocol) String() string {
return [...]string{"HTTP", "SSH", "TCP", "MCP"}[protocol]
}
type Status int
const (
Start Status = iota
End
@ -62,55 +74,90 @@ type Tracer interface {
}
type tracer struct {
strategy Strategy
strategy Strategy
eventsChan chan Event
eventsTotal prometheus.Counter
eventsSSHTotal prometheus.Counter
eventsTCPTotal prometheus.Counter
eventsHTTPTotal prometheus.Counter
eventsMCPTotal prometheus.Counter
}
var (
eventsTotal = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "events_total",
Help: "The total number of events",
})
eventsSSHTotal = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "ssh_events_total",
Help: "The total number of SSH events",
})
eventsTCPTotal = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "tcp_events_total",
Help: "The total number of TCP events",
})
eventsHTTPTotal = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "http_events_total",
Help: "The total number of HTTP events",
})
)
var lock = &sync.Mutex{}
var singleton *tracer
func Init(strategy Strategy) *tracer {
return &tracer{
strategy: strategy,
func GetInstance(defaultStrategy Strategy) *tracer {
if singleton == nil {
lock.Lock()
defer lock.Unlock()
// This is to prevent expensive lock operations every time the GetInstance method is called
if singleton == nil {
singleton = &tracer{
strategy: defaultStrategy,
eventsChan: make(chan Event, Workers),
eventsTotal: promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "events_total",
Help: "The total number of events",
}),
eventsSSHTotal: promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "ssh_events_total",
Help: "The total number of SSH events",
}),
eventsTCPTotal: promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "tcp_events_total",
Help: "The total number of TCP events",
}),
eventsHTTPTotal: promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "http_events_total",
Help: "The total number of HTTP events",
}),
eventsMCPTotal: promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
Name: "mcp_events_total",
Help: "The total number of MCP events",
}),
}
for i := 0; i < Workers; i++ {
go func(i int) {
log.Debug("Trace worker: ", i)
for event := range singleton.eventsChan {
singleton.strategy(event)
}
}(i)
}
}
}
return singleton
}
func (tracer *tracer) setStrategy(strategy Strategy) {
tracer.strategy = strategy
}
func (tracer *tracer) TraceEvent(event Event) {
event.DateTime = time.Now().UTC().Format(time.RFC3339)
tracer.strategy(event)
tracer.eventsChan <- event
//Openmetrics
eventsTotal.Inc()
switch event.Protocol {
case HTTP.String():
eventsHTTPTotal.Inc()
break
case SSH.String():
eventsSSHTotal.Inc()
break
case TCP.String():
eventsTCPTotal.Inc()
break
}
tracer.updatePrometheusCounters(event.Protocol)
}
func (tracer *tracer) updatePrometheusCounters(protocol string) {
switch protocol {
case HTTP.String():
tracer.eventsHTTPTotal.Inc()
case SSH.String():
tracer.eventsSSHTotal.Inc()
case TCP.String():
tracer.eventsTCPTotal.Inc()
case MCP.String():
tracer.eventsMCPTotal.Inc()
}
tracer.eventsTotal.Inc()
}

View File

@ -1,37 +1,75 @@
package tracer
import (
"github.com/stretchr/testify/assert"
"github.com/prometheus/client_golang/prometheus"
"sync"
"testing"
"github.com/stretchr/testify/assert"
)
func TestInit(t *testing.T) {
mockStrategy := func(event Event) {}
tracer := Init(mockStrategy)
tracer := GetInstance(mockStrategy)
assert.NotNil(t, tracer.strategy)
}
func TestTraceEvent(t *testing.T) {
eventCalled := Event{}
var wg sync.WaitGroup
mockStrategy := func(event Event) {
defer wg.Done()
eventCalled = event
}
tracer := Init(mockStrategy)
tracer := GetInstance(mockStrategy)
tracer.strategy = mockStrategy
wg.Add(1)
tracer.TraceEvent(Event{
ID: "mockID",
Protocol: HTTP.String(),
Status: Stateless.String(),
})
wg.Wait()
assert.NotNil(t, eventCalled.ID)
assert.Equal(t, eventCalled.ID, "mockID")
assert.Equal(t, eventCalled.Protocol, HTTP.String())
assert.Equal(t, eventCalled.Status, Stateless.String())
assert.Equal(t, "mockID", eventCalled.ID)
assert.Equal(t, HTTP.String(), eventCalled.Protocol)
assert.Equal(t, Stateless.String(), eventCalled.Status)
}
func TestSetStrategy(t *testing.T) {
eventCalled := Event{}
var wg sync.WaitGroup
mockStrategy := func(event Event) {
defer wg.Done()
eventCalled = event
}
tracer := GetInstance(mockStrategy)
tracer.setStrategy(mockStrategy)
wg.Add(1)
tracer.TraceEvent(Event{
ID: "mockID",
Protocol: HTTP.String(),
Status: Stateless.String(),
})
wg.Wait()
assert.NotNil(t, eventCalled.ID)
assert.Equal(t, "mockID", eventCalled.ID)
assert.Equal(t, HTTP.String(), eventCalled.Protocol)
assert.Equal(t, Stateless.String(), eventCalled.Status)
}
func TestStringStatus(t *testing.T) {
@ -40,3 +78,46 @@ func TestStringStatus(t *testing.T) {
assert.Equal(t, Stateless.String(), "Stateless")
assert.Equal(t, Interaction.String(), "Interaction")
}
type mockCounter struct {
prometheus.Metric
prometheus.Collector
inc func()
add func(float64)
}
var counter = 0
func (m mockCounter) Inc() {
counter += 1
}
func (m mockCounter) Add(f float64) {
counter = int(f)
}
func TestUpdatePrometheusCounters(t *testing.T) {
mockStrategy := func(event Event) {}
tracer := &tracer{
strategy: mockStrategy,
eventsChan: make(chan Event, Workers),
eventsTotal: mockCounter{},
eventsSSHTotal: mockCounter{},
eventsTCPTotal: mockCounter{},
eventsHTTPTotal: mockCounter{},
eventsMCPTotal: mockCounter{},
}
tracer.updatePrometheusCounters(SSH.String())
assert.Equal(t, 2, counter)
tracer.updatePrometheusCounters(HTTP.String())
assert.Equal(t, 4, counter)
tracer.updatePrometheusCounters(TCP.String())
assert.Equal(t, 6, counter)
tracer.updatePrometheusCounters(MCP.String())
assert.Equal(t, 8, counter)
}