Compare commits

...

179 Commits
v1.0.1 ... 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
a90c3974bd changed unit test coverage threshold 2023-06-17 00:00:12 +02:00
c9fe11c371 tiny refactor 2023-06-16 23:57:35 +02:00
0639c347df tiny refactor 2023-06-16 23:40:48 +02:00
6c9f6dd477 fix typo 2023-06-16 23:37:30 +02:00
b05a44a3c5 tiny refactoring 2023-06-16 23:32:49 +02:00
5fbe6c0211 Bump github.com/prometheus/client_golang from 1.15.1 to 1.16.0 (#52)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.15.1 to 1.16.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.15.1...v1.16.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>
2023-06-16 11:04:48 +02:00
09d955711d Bump golang.org/x/crypto from 0.9.0 to 0.10.0 (#51)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.9.0 to 0.10.0.
- [Commits](https://github.com/golang/crypto/compare/v0.9.0...v0.10.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-06-14 09:53:18 +02:00
f191f7d307 Bump github.com/sirupsen/logrus from 1.9.2 to 1.9.3 (#50)
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.9.2 to 1.9.3.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.9.2...v1.9.3)

---
updated-dependencies:
- dependency-name: github.com/sirupsen/logrus
  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-06-05 08:56:18 +02:00
4f2c84fc60 Update README.md (#49)
Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-06-04 23:25:43 +02:00
5ba0987527 refactoring example and readme.md 2023-06-04 23:21:22 +02:00
b09a1e908b Update FUNDING.yml
removed patreon

Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-06-01 00:24:21 +02:00
a7c69c8849 refactoring: Tiny refactoring (#47)
* tiny refactoring name convention, and file name.

* added command line arguments files

* fix typo comment
2023-06-01 00:15:21 +02:00
7d46c9544c Bump github.com/stretchr/testify from 1.8.3 to 1.8.4 (#46)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.3 to 1.8.4.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.3...v1.8.4)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  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-05-31 09:23:42 +02:00
10d16d7dd1 update: add .PHONY notation to Makefile commands (#45)
* update: add .PHONY notation to Makefile commands

* patch: Makefile according to style guide

Signed-off-by: Mario Candela <m4r10.php@gmail.com>

---------

Signed-off-by: Mario Candela <m4r10.php@gmail.com>
Co-authored-by: v0id <v0id@TheLab>
Co-authored-by: Mario Candela <m4r10.php@gmail.com>
2023-05-25 15:30:40 +02:00
48489e0abb Bump github.com/stretchr/testify from 1.8.2 to 1.8.3 (#44)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.2 to 1.8.3.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.2...v1.8.3)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  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-05-19 10:10:15 +02:00
ca51e076b6 Bump github.com/sirupsen/logrus from 1.9.1 to 1.9.2 (#43)
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.9.1 to 1.9.2.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.9.1...v1.9.2)

---
updated-dependencies:
- dependency-name: github.com/sirupsen/logrus
  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-05-18 09:33:37 +02:00
9025edee1e Bump github.com/sirupsen/logrus from 1.9.0 to 1.9.1 (#41)
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.9.0 to 1.9.1.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.9.0...v1.9.1)

---
updated-dependencies:
- dependency-name: github.com/sirupsen/logrus
  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-05-17 10:03:23 +02:00
853708a598 Bump golang.org/x/crypto from 0.8.0 to 0.9.0 (#40)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.8.0 to 0.9.0.
- [Commits](https://github.com/golang/crypto/compare/v0.8.0...v0.9.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-05-10 09:35:14 +02:00
8d9f1b48da Bump github.com/rabbitmq/amqp091-go from 1.8.0 to 1.8.1 (#39)
Bumps [github.com/rabbitmq/amqp091-go](https://github.com/rabbitmq/amqp091-go) from 1.8.0 to 1.8.1.
- [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.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/rabbitmq/amqp091-go
  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-05-05 09:06:22 +02:00
9d0a92e74e Bump github.com/prometheus/client_golang from 1.15.0 to 1.15.1 (#38) 2023-05-04 07:53:46 +02:00
49a4022f56 feat: Trace command output for SSH Strategy (#35)
manage command output SSH strategy
2023-04-14 22:35:11 +02:00
fe6d95ecd6 Create pull_request_template.md
Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-04-14 22:26:45 +02:00
d24a580a78 Bump github.com/prometheus/client_golang from 1.14.0 to 1.15.0 (#34) 2023-04-14 07:51:58 +02:00
9a354add6f fix typo 2023-04-13 21:50:25 +02:00
3652af6dc3 refactoring pipeline CD docker image 2023-04-13 21:48:38 +02:00
5532d3c629 Bump golang.org/x/crypto from 0.7.0 to 0.8.0 (#32) 2023-04-10 07:08:11 +02:00
97ab2f6e95 Fixed Backward Compatibility 2023-03-31 22:06:08 +02:00
f50f5185df feature: Configured prometheus, improve readme (#31) 2023-03-31 20:03:42 +02:00
5a70e5c900 Update SECURITY.md
Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-03-26 23:08:15 +02:00
142dccabf1 Bump github.com/rabbitmq/amqp091-go from 1.7.0 to 1.8.0 (#30)
Bumps [github.com/rabbitmq/amqp091-go](https://github.com/rabbitmq/amqp091-go) from 1.7.0 to 1.8.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.7.0...v1.8.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-03-22 07:42:05 +01:00
8337281f21 test: add rabbitMQ integration test (#29)
* added integration test dependencies

* added rabbitMQ use case integration test

* configured rabbitMQ integration test URI

* fix typo, configured integration test pipeline
2023-03-21 11:20:12 +01:00
1c402141bf Update README.md
Add telegram channel
2023-03-09 07:32:36 +01:00
9c9fd0b676 Bump golang.org/x/crypto from 0.6.0 to 0.7.0 (#28)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.6.0...v0.7.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-03-06 07:45:00 +01:00
97cfdcc8af Update codeql.yml
improve performance and cost CodeQL

Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-03-05 17:26:07 +01:00
c7c169c96d Create SECURITY.md
Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-03-05 17:23:40 +01:00
f733a0ea96 Create codeql.yml
Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-03-05 17:09:21 +01:00
31a75cc128 Update ci.yml
Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-03-05 15:22:21 +01:00
fd7db83fd8 Update README.md (#27)
Signed-off-by: Ikko Eltociear Ashimine <eltociear@gmail.com>
2023-03-03 18:29:43 +01:00
1b01933946 Bump github.com/stretchr/testify from 1.8.1 to 1.8.2 (#26)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  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-02-27 06:34:54 +01:00
6548cf62ce Bump golang.org/x/net from 0.6.0 to 0.7.0 (#25)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.6.0...v0.7.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>
2023-02-26 18:09:25 +01:00
6468b5aa6f refactor:Added Integration test and tiny refactoring (#23)
* Refactoring name convention

* Added integration test

* Added Makefile

* Bump golang.org/x/crypto from 0.0.0-20220826181053-bd7e27e6170d to 0.6.0

Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.0.0-20220826181053-bd7e27e6170d to 0.6.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/commits/v0.6.0)

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

* Upgrade go from 1.16 to 1.20

* Added integration test: HTTP, TCP, SSH

* Added Makefile Improve README.md

* Fixed unit test CI

* Fixed go-version

* Added integration test into C.I. actions

---------

Signed-off-by: Mario Candela <m4r10.php@gmail.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-26 18:04:05 +01:00
bbcc8c9094 Bump github.com/rabbitmq/amqp091-go from 1.5.0 to 1.7.0 (#22)
Bumps [github.com/rabbitmq/amqp091-go](https://github.com/rabbitmq/amqp091-go) from 1.5.0 to 1.7.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.5.0...v1.7.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-02-16 20:27:32 +01:00
6ff99e4eed Bump github.com/jarcoal/httpmock from 1.2.0 to 1.3.0 (#20)
Bumps [github.com/jarcoal/httpmock](https://github.com/jarcoal/httpmock) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/jarcoal/httpmock/releases)
- [Commits](https://github.com/jarcoal/httpmock/compare/v1.2.0...v1.3.0)

---
updated-dependencies:
- dependency-name: github.com/jarcoal/httpmock
  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-02-09 18:06:39 +01:00
0ebb6fa1e2 Update README.md
Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-01-29 12:34:56 +01:00
6f9781f06f Update FUNDING.yml
Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-01-27 10:49:47 +01:00
e4ca84589b feat!: Tiny refactoring, changed plugin name (#17)
* tiny refactoring naming
2023-01-22 13:00:52 +01:00
31f5ca9cb1 Update README.md
Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2023-01-08 13:10:19 +01:00
c6855e8511 Update README.md
Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2022-12-21 00:12:43 +01:00
6d2500d0f5 copy the ca-certificate.crt into scratch container 2022-12-19 23:08:21 +01:00
c98b888985 Add 2222:2222 ports mapping.
Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2022-12-18 18:49:13 +01:00
d062435818 feat: Add ChatBot GPT-3 integration (#16)
* Added dependency go-resty

* Configured Parser to read plugin configurations

* Add example ssh with ChatGPT plugin

* Add client ChatBot

* Improve logging

* Add integration with plugin OpenAIChatGPT

* Improve readme with ChatBot Example

* Add contributed ChatGPT question

* Refactoring

* Refactoring and improve unit test
2022-12-16 23:02:16 +01:00
9ddb076621 Update Builder.go (#15) Fixed typo
Fixed typo
2022-11-15 09:54:38 +01:00
5bfdff7097 Refactored initialization Beelzebub core with Builder Pattern (#14)
* Fixed deprecated function

* Coded builder pattern

* coded the first version builder

* coded rabbitmq into Builder.go

* Refactored builder, and configured director

* refactoring rabbitmq eventtracing

* Refactoring builder, managed close connections

* Fixed typos

Co-authored-by: mariocandela <mario.candela@nttdata.com>
2022-11-04 20:06:50 +01:00
fa7d17f817 Bump github.com/rabbitmq/amqp091-go from 1.4.0 to 1.5.0 (#11)
Bumps [github.com/rabbitmq/amqp091-go](https://github.com/rabbitmq/amqp091-go) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/rabbitmq/amqp091-go/releases)
- [Commits](https://github.com/rabbitmq/amqp091-go/compare/v1.4.0...v1.5.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-29 17:28:47 +02:00
fafa2bcb05 Bump github.com/stretchr/testify from 1.8.0 to 1.8.1 (#13)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.1)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-26 11:47:08 +02:00
3801628ef1 Create CODE_OF_CONDUCT.md
Signed-off-by: Mario Candela <m4r10.php@gmail.com>
2022-10-20 14:41:29 +02:00
62 changed files with 4870 additions and 1174 deletions

2
.github/FUNDING.yml vendored
View File

@ -1 +1 @@
patreon: mariocandela
github: mariocandela

View File

@ -2,13 +2,18 @@ name: CI
on:
push:
branches: [ main ]
branches: [ "main" ]
pull_request:
branches: [ main ]
branches: [ "main" ]
jobs:
build:
CI:
strategy:
fail-fast: false
matrix:
go-version:
- "1.24.1"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@ -16,9 +21,9 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.16
go-version: ${{ matrix.go-version }}
- name: Dependences
- name: Dependencies
run: go mod download
- name: Vet
@ -30,17 +35,17 @@ jobs:
- name: Unit tests
run: |
go test ./... -coverprofile coverage.tmp.out -covermode count
go test ./... -v -coverprofile coverage.tmp.out -covermode count
go tool cover -func coverage.tmp.out
- name: Quality Gate - Test coverage shall be above threshold
env:
TESTCOVERAGE_THRESHOLD: 70
TESTCOVERAGE_THRESHOLD: 80
run: |
echo "Quality Gate: checking test coverage is above threshold ..."
echo "Threshold : $TESTCOVERAGE_THRESHOLD %"
# Excluded the concrete strategy
cat coverage.tmp.out | grep -v "secureShellStrategy.go" | grep -v "hypertextTransferProtocolStrategy.go" | grep -v "transmissionControlProtocolStrategy.go" > coverage.out
# Excluded the concrete strategy from the unit test coverage, because covered by integration tests
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
@ -50,3 +55,33 @@ jobs:
echo "Failed"
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
- name: Wait for RabbitMQ to be ready
run: |
sleep 2
count=0
until docker exec rabbitmq rabbitmqctl list_queues > /dev/null 2>&1; do
count=$((count+1))
if [ $count -gt 10 ]; then
echo "RabbitMQ did not start within the specified time"
exit 1
fi
echo "Waiting for RabbitMQ to start..."
sleep 5
done
- name: Integration tests
run: |
make test.integration.verbose
make test.dependencies.down

45
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: "CodeQL"
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: '36 10 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
steps:
- 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@v3
with:
languages: ${{ matrix.language }}
- name: Build beelzebub
run: go build ./...
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View File

@ -1,26 +1,32 @@
---
name: Docker Hub Image
on:
push:
tags:
- 'v*.*.*'
jobs:
build:
CD:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Docker login
env:
DOCKER_USER: ${{secrets.DOCKER_USER}}
DOCKER_ACCESS_TOKEN: ${{secrets.DOCKER_ACCESS_TOKEN}}
run: |
docker login -u $DOCKER_USER -p $DOCKER_ACCESS_TOKEN
- name: Build the Docker image
run: docker build . --file Dockerfile --tag m4r10/beelzebub:${{ github.ref_name }}
- name: Docker Push
run: docker push m4r10/beelzebub:${{ github.ref_name }}
- name: Checkout
uses: actions/checkout@v3
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- 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
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
push: true
tags: |
m4r10/beelzebub:${{ github.ref_name }}
m4r10/beelzebub:latest
platforms: linux/amd64,linux/arm64

5
.gitignore vendored
View File

@ -1,3 +1,6 @@
.DS_Store
.idea
logs
logs
.vscode
.history
coverage*.out

128
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
mario.candela.personal@gmail.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

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,33 +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 --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

38
Makefile Normal file
View File

@ -0,0 +1,38 @@
DOCKER_COMPOSE := $(shell which docker-compose)
ifeq (${DOCKER_COMPOSE},)
DOCKER_COMPOSE = docker compose
endif
.PHONY: beelzebub.start
beelzebub.start:
${DOCKER_COMPOSE} build;
${DOCKER_COMPOSE} up -d;
.PHONY: beelzebub.stop
beelzebub.stop:
${DOCKER_COMPOSE} down;
.PHONY: test.unit
test.unit:
go test ./...
.PHONY: test.unit.verbose
test.unit.verbose:
go test ./... -v
.PHONY: test.dependencies.start
test.dependencies.start:
${DOCKER_COMPOSE} -f ./integration_test/docker-compose.yml up -d
.PHONY: test.dependencies.down
test.dependencies.down:
${DOCKER_COMPOSE} -f ./integration_test/docker-compose.yml down
.PHONY: test.integration
test.integration:
INTEGRATION=1 go test ./...
.PHONY: test.integration.verbose
test.integration.verbose:
INTEGRATION=1 go test ./... -v

367
README.md

File diff suppressed because one or more lines are too long

13
SECURITY.md Normal file
View File

@ -0,0 +1,13 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| >= 2.0.0 | :white_check_mark: |
| < 2.0.0 | :x: |
## Reporting a Vulnerability
Please report (suspected) security vulnerabilities to mario.candela.personal@gmail.com.
You will receive a response from us within 48 hours. If the issue is confirmed, we will release a patch as soon as possible depending on complexity but historically within a few days.

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: {}

168
builder/builder.go Normal file
View File

@ -0,0 +1,168 @@
package builder
import (
"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"
)
const RabbitmqQueueName = "event"
type Builder struct {
beelzebubServicesConfiguration []parser.BeelzebubServiceConfiguration
beelzebubCoreConfigurations *parser.BeelzebubCoreConfigurations
traceStrategy tracer.Strategy
rabbitMQChannel *amqp.Channel
rabbitMQConnection *amqp.Connection
logsFile *os.File
}
func (b *Builder) setTraceStrategy(traceStrategy tracer.Strategy) {
b.traceStrategy = traceStrategy
}
func (b *Builder) buildLogger(configurations parser.Logging) error {
logsFile, err := os.OpenFile(configurations.LogsPath, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
return err
}
log.SetOutput(io.MultiWriter(os.Stdout, logsFile))
log.SetFormatter(&log.JSONFormatter{
DisableTimestamp: configurations.LogDisableTimestamp,
})
log.SetReportCaller(configurations.DebugReportCaller)
if configurations.Debug {
log.SetLevel(log.DebugLevel)
} else {
log.SetLevel(log.InfoLevel)
}
b.logsFile = logsFile
return nil
}
func (b *Builder) buildRabbitMQ(rabbitMQURI string) error {
rabbitMQConnection, err := amqp.Dial(rabbitMQURI)
if err != nil {
return err
}
b.rabbitMQChannel, err = rabbitMQConnection.Channel()
if err != nil {
return err
}
//creates a queue if it doesn't already exist, or ensures that an existing queue matches the same parameters.
if _, err = b.rabbitMQChannel.QueueDeclare(RabbitmqQueueName, false, false, false, false, nil); err != nil {
return err
}
b.rabbitMQConnection = rabbitMQConnection
return nil
}
func (b *Builder) Close() error {
if b.rabbitMQConnection != nil {
if err := b.rabbitMQChannel.Close(); err != nil {
return err
}
if err := b.rabbitMQConnection.Close(); err != nil {
return err
}
}
return nil
}
func (b *Builder) Run() error {
fmt.Println(
`
██████ ███████ ███████ ██ ███████ ███████ ██████ ██ ██ ██████
██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██
██████ █████ █████ ██ ███ █████ ██████ ██ ██ ██████
██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██
██████ ███████ ███████ ███████ ███████ ███████ ██████ ██████ ██████
Honeypot Framework, happy hacking!`)
// Init Prometheus openmetrics
go func() {
if (b.beelzebubCoreConfigurations.Core.Prometheus != parser.Prometheus{}) {
http.Handle(b.beelzebubCoreConfigurations.Core.Prometheus.Path, promhttp.Handler())
if err := http.ListenAndServe(b.beelzebubCoreConfigurations.Core.Prometheus.Port, nil); err != nil {
log.Fatalf("Error init Prometheus: %s", err.Error())
}
}
}()
// Init Protocol strategies
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)
case "ssh":
protocolManager.SetProtocolStrategy(secureShellStrategy)
case "tcp":
protocolManager.SetProtocolStrategy(transmissionControlProtocolStrategy)
case "mcp":
protocolManager.SetProtocolStrategy(modelContextProtocolStrategy)
default:
log.Fatalf("protocol %s not managed", beelzebubServiceConfiguration.Protocol)
}
if err := protocolManager.InitService(beelzebubServiceConfiguration); err != nil {
return fmt.Errorf("error during init protocol: %s, %s", beelzebubServiceConfiguration.Protocol, err.Error())
}
}
return nil
}
func (b *Builder) build() *Builder {
return &Builder{
beelzebubServicesConfiguration: b.beelzebubServicesConfiguration,
traceStrategy: b.traceStrategy,
beelzebubCoreConfigurations: b.beelzebubCoreConfigurations,
}
}
func NewBuilder() *Builder {
return &Builder{}
}

98
builder/director.go Normal file
View File

@ -0,0 +1,98 @@
package builder
import (
"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"
)
type Director struct {
builder *Builder
}
func NewDirector(builder *Builder) *Director {
return &Director{
builder: builder,
}
}
func (d *Director) BuildBeelzebub(beelzebubCoreConfigurations *parser.BeelzebubCoreConfigurations, beelzebubServicesConfiguration []parser.BeelzebubServiceConfiguration) (*Builder, error) {
d.builder.beelzebubServicesConfiguration = beelzebubServicesConfiguration
d.builder.beelzebubCoreConfigurations = beelzebubCoreConfigurations
if err := d.builder.buildLogger(beelzebubCoreConfigurations.Core.Logging); err != nil {
return nil, err
}
d.builder.setTraceStrategy(d.standardOutStrategy)
if beelzebubCoreConfigurations.Core.Tracings.RabbitMQ.Enabled {
d.builder.setTraceStrategy(d.rabbitMQTraceStrategy)
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
}
func (d *Director) standardOutStrategy(event tracer.Event) {
log.WithFields(log.Fields{
"status": event.Status,
"event": 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,
"event": event,
}).Info("New Event")
eventJSON, err := json.Marshal(event)
if err != nil {
log.Error(err.Error())
return
}
publishing := amqp.Publishing{ContentType: "application/json", Body: eventJSON}
if err = d.builder.rabbitMQChannel.PublishWithContext(context.TODO(), "", RabbitmqQueueName, false, false, publishing); err != nil {
log.Error(err.Error())
} else {
log.WithFields(log.Fields{
"status": event.Status,
"event": event,
}).Debug("Event published")
}
}

View File

@ -4,6 +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: ""

File diff suppressed because one or more lines are too long

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

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

View File

@ -3,15 +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 Open Metrics
environment:
RABBITMQ_URI: ${RABBITMQ_URI}
OPEN_AI_SECRET_KEY: ${OPEN_AI_SECRET_KEY}
volumes:
- "./configurations:/configurations"

45
go.mod
View File

@ -1,13 +1,42 @@
module beelzebub
module github.com/mariocandela/beelzebub/v3
go 1.16
go 1.24
toolchain go1.24.1
require (
github.com/gliderlabs/ssh v0.3.5
github.com/google/uuid v1.3.0
github.com/rabbitmq/amqp091-go v1.4.0
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.0
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d
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.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.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/fs v0.1.0 // 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.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
)

133
go.sum
View File

@ -1,75 +1,120 @@
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
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.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/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
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/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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
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/rabbitmq/amqp091-go v1.4.0 h1:T2G+J9W9OY4p64Di23J6yH7tOkMocgnESvYeBjuG9cY=
github.com/rabbitmq/amqp091-go v1.4.0/go.mod h1:JsV0ofX5f1nwOGafb8L5rBItt9GyhfQfcJj+oyz0dGg=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
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 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
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.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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.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-20210220032951-036812b2e83c/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=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.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 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
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.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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
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

@ -0,0 +1,13 @@
core:
logging:
debug: false
debugReportCaller: false
logDisableTimestamp: true
logsPath: ./logs
tracings:
rabbit-mq:
enabled: true
uri: "amqp://integration:integration@localhost:5672/"
prometheus:
path: "/metrics"
port: ":2112"

View File

@ -0,0 +1,19 @@
apiVersion: "v1"
protocol: "http"
address: ":8080"
description: "Wordpress 6.0"
commands:
- regex: "index.php"
handler: "mocked response"
headers:
- "Content-Type: text/html"
- "Server: Apache/2.4.53 (Debian)"
- "X-Powered-By: PHP/7.4.29"
statusCode: 200
- regex: "^(/wp-login.php|/wp-admin)$"
handler: "mocked response"
headers:
- "Content-Type: text/html"
- "Server: Apache/2.4.53 (Debian)"
- "X-Powered-By: PHP/7.4.29"
statusCode: 400

View File

@ -0,0 +1,25 @@
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

View File

@ -0,0 +1,6 @@
apiVersion: "v1"
protocol: "tcp"
address: ":3306"
description: "Mysql 8.0.29"
banner: "8.0.29"
deadlineTimeoutSeconds: 10

View File

@ -0,0 +1,10 @@
version: "3.9"
services:
rabbitmq:
image: rabbitmq:3-alpine
container_name: 'rabbitmq'
ports:
- 5672:5672
environment:
- RABBITMQ_DEFAULT_USER=integration
- RABBITMQ_DEFAULT_PASS=integration

View File

@ -0,0 +1,166 @@
package integration
import (
"encoding/json"
"github.com/mariocandela/beelzebub/v3/builder"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/tracer"
"net"
"net/http"
"os"
"testing"
"github.com/go-resty/resty/v2"
"github.com/melbahja/goph"
amqp "github.com/rabbitmq/amqp091-go"
"github.com/stretchr/testify/suite"
"golang.org/x/crypto/ssh"
)
type IntegrationTestSuite struct {
suite.Suite
beelzebubBuilder *builder.Builder
prometheusHost string
httpHoneypotHost string
tcpHoneypotHost string
sshHoneypotHost string
rabbitMQURI string
}
func TestIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(IntegrationTestSuite))
}
func (suite *IntegrationTestSuite) SetupSuite() {
suite.T().Helper()
if os.Getenv("INTEGRATION") == "" {
suite.T().Skip("skipping integration tests, set environment variable INTEGRATION")
}
suite.httpHoneypotHost = "http://localhost:8080"
suite.tcpHoneypotHost = "localhost:3306"
suite.sshHoneypotHost = "localhost"
suite.prometheusHost = "http://localhost:2112/metrics"
beelzebubConfigPath := "./configurations/beelzebub.yaml"
servicesConfigDirectory := "./configurations/services/"
parser := parser.Init(beelzebubConfigPath, servicesConfigDirectory)
coreConfigurations, err := parser.ReadConfigurationsCore()
suite.Require().NoError(err)
suite.rabbitMQURI = coreConfigurations.Core.Tracings.RabbitMQ.URI
beelzebubServicesConfiguration, err := parser.ReadConfigurationsServices()
suite.Require().NoError(err)
suite.beelzebubBuilder = builder.NewBuilder()
director := builder.NewDirector(suite.beelzebubBuilder)
suite.beelzebubBuilder, err = director.BuildBeelzebub(coreConfigurations, beelzebubServicesConfiguration)
suite.Require().NoError(err)
suite.Require().NoError(suite.beelzebubBuilder.Run())
}
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().
Get(suite.httpHoneypotHost + "/wp-admin")
suite.Require().NoError(err)
suite.Equal(http.StatusBadRequest, response.StatusCode())
suite.Equal("mocked response", string(response.Body()))
}
func (suite *IntegrationTestSuite) TestInvokeTCPHoneypot() {
tcpAddr, err := net.ResolveTCPAddr("tcp", suite.tcpHoneypotHost)
suite.Require().NoError(err)
conn, err := net.DialTCP("tcp", nil, tcpAddr)
suite.Require().NoError(err)
defer conn.Close()
_, err = conn.Write([]byte("hello!"))
suite.Require().NoError(err)
reply := make([]byte, 1024)
n, err := conn.Read(reply)
suite.Require().NoError(err)
suite.Equal("8.0.29\n", string(reply[:n]))
}
func (suite *IntegrationTestSuite) TestInvokeSSHHoneypot() {
client, err := goph.NewConn(
&goph.Config{
User: "root",
Addr: suite.sshHoneypotHost,
Port: 2222,
Auth: goph.Password("root"),
Callback: ssh.InsecureIgnoreHostKey(),
})
suite.Require().NoError(err)
defer client.Close()
out, err := client.Run("")
suite.Require().NoError(err)
suite.Equal("root@ubuntu:~$ ", string(out))
}
func (suite *IntegrationTestSuite) TestRabbitMQ() {
conn, err := amqp.Dial(suite.rabbitMQURI)
suite.Require().NoError(err)
defer conn.Close()
ch, err := conn.Channel()
suite.Require().NoError(err)
defer ch.Close()
msgs, err := ch.Consume("event", "", true, false, false, false, nil)
suite.Require().NoError(err)
//Invoke HTTP Honeypot
response, err := resty.New().R().Get(suite.httpHoneypotHost + "/index.php")
suite.Require().NoError(err)
suite.Equal(http.StatusOK, response.StatusCode())
for msg := range msgs {
var event tracer.Event
err := json.Unmarshal(msg.Body, &event)
suite.Require().NoError(err)
suite.Equal("GET", event.HTTPMethod)
suite.Equal("/index.php", event.RequestURI)
break
}
}
func (suite *IntegrationTestSuite) TestPrometheus() {
//Invoke HTTP Honeypot
response, err := resty.New().R().Get(suite.httpHoneypotHost + "/index.php")
suite.Require().NoError(err)
suite.Equal(http.StatusOK, response.StatusCode())
response, err = resty.New().R().Get(suite.prometheusHost)
suite.Require().NoError(err)
suite.Equal(http.StatusOK, response.StatusCode())
}
func (suite *IntegrationTestSuite) TestShutdownBeelzebub() {
suite.Require().NoError(suite.beelzebubBuilder.Close())
}

142
main.go
View File

@ -1,74 +1,54 @@
package main
import (
"beelzebub/parser"
"beelzebub/protocols"
"beelzebub/tracer"
"encoding/json"
"fmt"
amqp "github.com/rabbitmq/amqp091-go"
"flag"
"runtime/debug"
"github.com/mariocandela/beelzebub/v3/builder"
"github.com/mariocandela/beelzebub/v3/parser"
log "github.com/sirupsen/logrus"
"io"
"os"
)
var quit = make(chan struct{})
var channel *amqp.Channel
func main() {
parser := parser.Init("./configurations/beelzebub.yaml", "./configurations/services/")
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: "))
fileLogs := configureLoggingByConfigurations(coreConfigurations.Core.Logging)
defer fileLogs.Close()
failOnError(err, "Error during ReadConfigurationsCore: ")
beelzebubServicesConfiguration, err := parser.ReadConfigurationsServices()
failOnError(err, fmt.Sprintf("Error during ReadConfigurationsServices: "))
failOnError(err, "Error during ReadConfigurationsServices: ")
if coreConfigurations.Core.Tracing.RabbitMQEnabled {
rabbitMQURI, configured := os.LookupEnv("RABBITMQ_URI")
if !configured {
rabbitMQURI = coreConfigurations.Core.Tracing.RabbitMQURI
}
conn, err := amqp.Dial(rabbitMQURI)
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
beelzebubBuilder := builder.NewBuilder()
channel, err = conn.Channel()
failOnError(err, "Failed to open a channel")
defer channel.Close()
}
director := builder.NewDirector(beelzebubBuilder)
// Init Protocol strategies
secureShellStrategy := &protocols.SecureShellStrategy{}
hypertextTransferProtocolStrategy := &protocols.HypertextTransferProtocolStrategy{}
transmissionControlProtocolStrategy := &protocols.TransmissionControlProtocolStrategy{}
beelzebubBuilder, err = director.BuildBeelzebub(coreConfigurations, beelzebubServicesConfiguration)
failOnError(err, "Error during BuildBeelzebub: ")
// Init protocol manager, with simple log on stout trace strategy and default protocol HTTP
protocolManager := protocols.InitProtocolManager(traceStrategyStdoutAndRabbitMQ, hypertextTransferProtocolStrategy)
err = beelzebubBuilder.Run()
failOnError(err, "Error during run beelzebub core: ")
for _, beelzebubServiceConfiguration := range beelzebubServicesConfiguration {
switch beelzebubServiceConfiguration.Protocol {
case "http":
protocolManager.SetProtocolStrategy(hypertextTransferProtocolStrategy)
break
case "ssh":
protocolManager.SetProtocolStrategy(secureShellStrategy)
break
case "tcp":
protocolManager.SetProtocolStrategy(transmissionControlProtocolStrategy)
break
default:
log.Fatalf("Protocol %s not managed", beelzebubServiceConfiguration.Protocol)
continue
}
defer beelzebubBuilder.Close()
err := protocolManager.InitService(beelzebubServiceConfiguration)
failOnError(err, fmt.Sprintf("Error during init protocol: %s, ", beelzebubServiceConfiguration.Protocol))
}
<-quit
}
@ -77,57 +57,3 @@ func failOnError(err error, msg string) {
log.Fatalf("%s: %s", msg, err)
}
}
func traceStrategyStdoutAndRabbitMQ(event tracer.Event) {
log.WithFields(log.Fields{
"status": event.Status,
"event": event,
}).Info("New Event")
if channel != nil {
log.Debug("Push Event on queue")
eventJSON, err := json.Marshal(event)
failOnError(err, "Failed to Marshal Event")
queue, err := channel.QueueDeclare(
"event",
false,
false,
false,
false,
nil,
)
failOnError(err, "Failed to declare a queue")
err = channel.Publish(
"",
queue.Name,
false,
false,
amqp.Publishing{
ContentType: "application/json",
Body: eventJSON,
})
failOnError(err, "Failed to publish a message")
}
}
func configureLoggingByConfigurations(configurations parser.Logging) *os.File {
file, err := os.OpenFile(configurations.LogsPath, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
log.Fatalf("error opening file: %v", err)
}
log.SetOutput(io.MultiWriter(os.Stdout, file))
log.SetFormatter(&log.JSONFormatter{
DisableTimestamp: configurations.LogDisableTimestamp,
})
log.SetReportCaller(configurations.DebugReportCaller)
if configurations.Debug {
log.SetLevel(log.DebugLevel)
} else {
log.SetLevel(log.InfoLevel)
}
return file
}

View File

@ -1,118 +0,0 @@
package parser
import (
"errors"
"github.com/stretchr/testify/assert"
"testing"
)
func mockReadfilebytesConfigurationsCore(filePath string) ([]byte, error) {
configurationsCoreBytes := []byte(`
core:
logging:
debug: false
debugReportCaller: false
logDisableTimestamp: true
logsPath: ./logs
tracing:
rabbitMQEnabled: true
rabbitMQURI: provaMock`)
return configurationsCoreBytes, nil
}
func mockReadfilebytesFormatError(filePath string) ([]byte, error) {
configurationsCoreBytes := []byte(`{{}`)
return configurationsCoreBytes, nil
}
func mockReadfilebytesError(filePath string) ([]byte, error) {
return nil, errors.New("mockErrorReadFileBytes")
}
func mockReadDirError(dirPath string) ([]string, error) {
return nil, errors.New("mockErrorReadFileBytes")
}
func mockReadDirValid(dirPath string) ([]string, error) {
return []string{""}, nil
}
func mockReadfilebytesBeelzebubServiceConfiguration(filePath string) ([]byte, error) {
beelzebubServiceConfiguration := []byte(`
apiVersion: "v1"
protocol: "http"
address: ":8080"
commands:
- regex: "wp-admin"
handler: "login"
headers:
- "Content-Type: text/html"`)
return beelzebubServiceConfiguration, nil
}
func TestReadConfigurationsCoreError(t *testing.T) {
configurationsParser := Init("mockConfigurationsCorePath", "mockConfigurationsServicesDirectory")
configurationsParser.readFileBytesByFilePathDependency = mockReadfilebytesError
beelzebubCoreConfigurations, err := configurationsParser.ReadConfigurationsCore()
assert.Nil(t, beelzebubCoreConfigurations)
assert.Error(t, err)
assert.Equal(t, "in file mockConfigurationsCorePath: mockErrorReadFileBytes", err.Error())
configurationsParser.readFileBytesByFilePathDependency = mockReadfilebytesFormatError
beelzebubCoreConfigurations, err = configurationsParser.ReadConfigurationsCore()
assert.Nil(t, beelzebubCoreConfigurations)
assert.Error(t, err)
assert.Equal(t, "in file mockConfigurationsCorePath: yaml: line 1: did not find expected ',' or '}'", err.Error())
}
func TestReadConfigurationsCoreValid(t *testing.T) {
configurationsParser := Init("", "")
configurationsParser.readFileBytesByFilePathDependency = mockReadfilebytesConfigurationsCore
coreConfigurations, err := configurationsParser.ReadConfigurationsCore()
assert.Nil(t, err)
assert.NotNil(t, coreConfigurations.Core)
assert.NotNil(t, coreConfigurations.Core.Logging)
assert.Equal(t, coreConfigurations.Core.Logging.Debug, false)
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")
}
func TestReadConfigurationsServicesFail(t *testing.T) {
configurationsParser := Init("", "")
configurationsParser.readFileBytesByFilePathDependency = mockReadfilebytesError
configurationsParser.gelAllFilesNameByDirNameDependency = mockReadDirError
beelzebubServiceConfiguration, err := configurationsParser.ReadConfigurationsServices()
assert.Nil(t, beelzebubServiceConfiguration)
assert.Error(t, err)
}
func TestReadConfigurationsServicesValid(t *testing.T) {
configurationsParser := Init("", "")
configurationsParser.readFileBytesByFilePathDependency = mockReadfilebytesBeelzebubServiceConfiguration
configurationsParser.gelAllFilesNameByDirNameDependency = mockReadDirValid
beelzebubServicesConfiguration, err := configurationsParser.ReadConfigurationsServices()
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, 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")
}

View File

@ -1,21 +1,28 @@
// Package parser is responsible for parsing the configurations of the core and honeypot service
package parser
import (
"fmt"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
"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"`
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"`
@ -23,29 +30,75 @@ 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 {
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"`
PasswordRegex string `yaml:"passwordRegex"`
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"`
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 {
@ -69,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 {
@ -84,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 {
@ -103,21 +158,40 @@ 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) {
var filesName []string
files, err := ioutil.ReadDir(dirName)
files, err := os.ReadDir(dirName)
if err != nil {
return nil, err
}
var filesName []string
for _, file := range files {
filesName = append(filesName, file.Name())
if !file.IsDir() && strings.HasSuffix(file.Name(), ".yaml") {
filesName = append(filesName, file.Name())
}
}
return filesName, nil
}

View File

@ -0,0 +1,302 @@
package parser
import (
"errors"
"os"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
)
func mockReadfilebytesConfigurationsCore(filePath string) ([]byte, error) {
configurationsCoreBytes := []byte(`
core:
logging:
debug: false
debugReportCaller: false
logDisableTimestamp: true
logsPath: ./logs
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
}
func mockReadfilebytesFormatError(filePath string) ([]byte, error) {
configurationsCoreBytes := []byte(`{{}`)
return configurationsCoreBytes, nil
}
func mockReadfilebytesError(filePath string) ([]byte, error) {
return nil, errors.New("mockErrorReadFileBytes")
}
func mockReadDirError(dirPath string) ([]string, error) {
return nil, errors.New("mockErrorReadFileBytes")
}
func mockReadDirValid(dirPath string) ([]string, error) {
return []string{""}, nil
}
func mockReadfilebytesBeelzebubServiceConfiguration(filePath string) ([]byte, error) {
beelzebubServiceConfiguration := []byte(`
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"
- 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
}
func TestReadConfigurationsCoreError(t *testing.T) {
configurationsParser := Init("mockConfigurationsCorePath", "mockConfigurationsServicesDirectory")
configurationsParser.readFileBytesByFilePathDependency = mockReadfilebytesError
beelzebubCoreConfigurations, err := configurationsParser.ReadConfigurationsCore()
assert.Nil(t, beelzebubCoreConfigurations)
assert.Error(t, err)
assert.Equal(t, "in file mockConfigurationsCorePath: mockErrorReadFileBytes", err.Error())
configurationsParser.readFileBytesByFilePathDependency = mockReadfilebytesFormatError
beelzebubCoreConfigurations, err = configurationsParser.ReadConfigurationsCore()
assert.Nil(t, beelzebubCoreConfigurations)
assert.Error(t, err)
assert.Equal(t, "in file mockConfigurationsCorePath: yaml: line 1: did not find expected ',' or '}'", err.Error())
}
func TestReadConfigurationsCoreValid(t *testing.T) {
configurationsParser := Init("", "")
configurationsParser.readFileBytesByFilePathDependency = mockReadfilebytesConfigurationsCore
coreConfigurations, err := configurationsParser.ReadConfigurationsCore()
assert.Nil(t, err)
assert.NotNil(t, coreConfigurations.Core)
assert.NotNil(t, coreConfigurations.Core.Logging)
assert.Equal(t, coreConfigurations.Core.Logging.Debug, false)
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.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) {
configurationsParser := Init("", "")
configurationsParser.readFileBytesByFilePathDependency = mockReadfilebytesError
configurationsParser.gelAllFilesNameByDirNameDependency = mockReadDirError
beelzebubServiceConfiguration, err := configurationsParser.ReadConfigurationsServices()
assert.Nil(t, beelzebubServiceConfiguration)
assert.Error(t, err)
}
func TestReadConfigurationsServicesValid(t *testing.T) {
configurationsParser := Init("", "")
configurationsParser.readFileBytesByFilePathDependency = mockReadfilebytesBeelzebubServiceConfiguration
configurationsParser.gelAllFilesNameByDirNameDependency = mockReadDirValid
beelzebubServicesConfiguration, err := configurationsParser.ReadConfigurationsServices()
assert.Nil(t, err)
firstBeelzebubServiceConfiguration := beelzebubServicesConfiguration[0]
assert.Equal(t, firstBeelzebubServiceConfiguration.Protocol, "http")
assert.Equal(t, firstBeelzebubServiceConfiguration.ApiVersion, "v1")
assert.Equal(t, firstBeelzebubServiceConfiguration.Address, ":8080")
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,110 +0,0 @@
package protocols
import (
"beelzebub/parser"
"beelzebub/tracer"
"fmt"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"io"
"net/http"
"regexp"
"strings"
)
type HypertextTransferProtocolStrategy struct {
beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration
}
func (httpStrategy HypertextTransferProtocolStrategy) 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.Protocol)
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,30 +0,0 @@
package protocols
import (
"beelzebub/parser"
"beelzebub/tracer"
)
type ServiceStrategy interface {
Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tracer tracer.Tracer) error
}
type ProtocolManager struct {
strategy ServiceStrategy
tracer tracer.Tracer
}
func InitProtocolManager(tracerStrategy tracer.Strategy, strategy ServiceStrategy) *ProtocolManager {
return &ProtocolManager{
tracer: tracer.Init(tracerStrategy),
strategy: strategy,
}
}
func (pm *ProtocolManager) SetProtocolStrategy(strategy ServiceStrategy) {
pm.strategy = strategy
}
func (pm *ProtocolManager) InitService(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration) error {
return pm.strategy.Init(beelzebubServiceConfiguration, pm.tracer)
}

View File

@ -0,0 +1,34 @@
// Package protocols is responsible for managing the different protocols
package protocols
import (
"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
}
type ProtocolManager struct {
strategy ServiceStrategy
tracer tracer.Tracer
}
// 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.GetInstance(tracerStrategy),
strategy: serviceStrategy,
}
}
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

@ -1,113 +0,0 @@
package protocols
import (
"beelzebub/parser"
"beelzebub/tracer"
"fmt"
"github.com/gliderlabs/ssh"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh/terminal"
"regexp"
"strings"
"time"
)
type SecureShellStrategy struct {
}
func (SSHStrategy *SecureShellStrategy) 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))
for {
commandInput, err := term.ReadLine()
if err != nil {
break
}
tr.TraceEvent(tracer.Event{
Msg: "New SSH Terminal Session",
RemoteAddr: sess.RemoteAddr().String(),
Status: tracer.Interaction.String(),
Command: commandInput,
ID: uuidSession.String(),
Protocol: tracer.SSH.String(),
Description: beelzebubServiceConfiguration.Description,
})
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 {
term.Write(append([]byte(command.Handler), '\n'))
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

@ -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,20 +1,22 @@
package protocols
package TCP
import (
"beelzebub/parser"
"beelzebub/tracer"
"fmt"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"net"
"time"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/tracer"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
)
type TransmissionControlProtocolStrategy struct {
type TCPStrategy struct {
}
func (TCPStrategy *TransmissionControlProtocolStrategy) 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
@ -24,8 +26,8 @@ func (TCPStrategy *TransmissionControlProtocolStrategy) Init(beelzebubServiceCon
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 := ""
@ -34,14 +36,18 @@ func (TCPStrategy *TransmissionControlProtocolStrategy) Init(beelzebubServiceCon
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()
}()
@ -50,8 +56,8 @@ func (TCPStrategy *TransmissionControlProtocolStrategy) Init(beelzebubServiceCon
}()
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
}

17
pull_request_template.md Normal file
View File

@ -0,0 +1,17 @@
All Submissions:
* [ ] Have you followed the guidelines in our Contributing document?
* [ ] Have you checked to ensure there aren't other open [Pull Requests](../../pulls) for the same update/change?
<!-- You can erase any parts of this template not applicable to your Pull Request. -->
### New Feature Submissions:
1. [ ] Does your submission pass tests?
2. [ ] Have you lint your code locally before submission?
### Changes to Core Features:
* [ ] Have you added an explanation of what your changes do and why you'd like us to include them?
* [ ] Have you written new tests for your core changes, as applicable?
* [ ] Have you successfully run tests with your changes locally?

View File

@ -1,35 +1,23 @@
// 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"
)
type Strategy func(event Event)
type Tracer interface {
TraceEvent(event Event)
}
type tracer struct {
strategy Strategy
}
func Init(strategy Strategy) *tracer {
return &tracer{
strategy: strategy,
}
}
func (tracer *tracer) TraceEvent(event Event) {
event.DateTime = time.Now().UTC().Format(time.RFC3339)
tracer.strategy(event)
}
const Workers = 5
type Event struct {
DateTime string
RemoteAddr string
Protocol string
Command string
CommandOutput string
Status string
Msg string
ID string
@ -38,6 +26,7 @@ type Event struct {
Password string
Client string
Headers string
HeadersMap map[string][]string
Cookies string
UserAgent string
HostHTTPRequest string
@ -45,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
@ -71,3 +66,98 @@ const (
func (status Status) String() string {
return [...]string{"Start", "End", "Stateless", "Interaction"}[status]
}
type Strategy func(event Event)
type Tracer interface {
TraceEvent(event Event)
}
type tracer struct {
strategy Strategy
eventsChan chan Event
eventsTotal prometheus.Counter
eventsSSHTotal prometheus.Counter
eventsTCPTotal prometheus.Counter
eventsHTTPTotal prometheus.Counter
eventsMCPTotal prometheus.Counter
}
var lock = &sync.Mutex{}
var singleton *tracer
func GetInstance(defaultStrategy Strategy) *tracer {
if singleton == nil {
lock.Lock()
defer lock.Unlock()
// This is to prevent expensive lock operations every time the GetInstance method is called
if singleton == nil {
singleton = &tracer{
strategy: defaultStrategy,
eventsChan: make(chan Event, Workers),
eventsTotal: promauto.NewCounter(prometheus.CounterOpts{
Namespace: "beelzebub",
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.eventsChan <- event
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)
}