Skip to content

Commit 6435971

Browse files
authored
Feat: Improve cloud plugin (#240)
* Implement hot reload
1 parent e168bb4 commit 6435971

File tree

7 files changed

+290
-37
lines changed

7 files changed

+290
-37
lines changed

builder/builder.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ package builder
33
import (
44
"errors"
55
"fmt"
6-
"github.com/mariocandela/beelzebub/v3/protocols/strategies/MCP"
76
"io"
87
"net/http"
98
"os"
109

10+
"github.com/mariocandela/beelzebub/v3/protocols/strategies/MCP"
11+
1112
"github.com/mariocandela/beelzebub/v3/parser"
1213
"github.com/mariocandela/beelzebub/v3/plugins"
1314
"github.com/mariocandela/beelzebub/v3/protocols"
@@ -121,9 +122,9 @@ Honeypot Framework, happy hacking!`)
121122
if b.beelzebubCoreConfigurations.Core.BeelzebubCloud.Enabled {
122123
conf := b.beelzebubCoreConfigurations.Core.BeelzebubCloud
123124

124-
beelzebubCloud := plugins.InitBeelzebubCloud(conf.URI, conf.AuthToken)
125+
beelzebubCloud := plugins.InitBeelzebubCloud(conf.URI, conf.AuthToken, true)
125126

126-
if honeypotsConfiguration, err := beelzebubCloud.GetHoneypotsConfigurations(); err != nil {
127+
if honeypotsConfiguration, _, err := beelzebubCloud.GetHoneypotsConfigurations(); err != nil {
127128
return err
128129
} else {
129130
if len(honeypotsConfiguration) == 0 {

builder/director.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func (d *Director) beelzebubCloudStrategy(event tracer.Event) {
6060

6161
conf := d.builder.beelzebubCoreConfigurations.Core.BeelzebubCloud
6262

63-
beelzebubCloud := plugins.InitBeelzebubCloud(conf.URI, conf.AuthToken)
63+
beelzebubCloud := plugins.InitBeelzebubCloud(conf.URI, conf.AuthToken, false)
6464

6565
result, err := beelzebubCloud.SendEvent(event)
6666
if err != nil {

docker-compose.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
version: "3.9"
2-
31
services:
42
beelzebub:
53
build: .

parser/configurations_parser.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
package parser
33

44
import (
5+
"crypto/sha256"
6+
"encoding/hex"
7+
"encoding/json"
58
"fmt"
69
"os"
710
"path/filepath"
@@ -80,6 +83,15 @@ type BeelzebubServiceConfiguration struct {
8083
TLSKeyPath string `yaml:"tlsKeyPath"`
8184
}
8285

86+
func (bsc BeelzebubServiceConfiguration) HashCode() (string, error) {
87+
data, err := json.Marshal(bsc)
88+
if err != nil {
89+
return "", err
90+
}
91+
hash := sha256.Sum256(data)
92+
return hex.EncodeToString(hash[:]), nil
93+
}
94+
8395
// Command is the struct that contains the configurations of the commands
8496
type Command struct {
8597
RegexStr string `yaml:"regex"`

parser/configurations_parser_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,21 @@ func TestReadConfigurationsServicesValid(t *testing.T) {
182182
assert.Equal(t, firstBeelzebubServiceConfiguration.Tools[0].Handler, "reset_password ok")
183183
}
184184

185+
func TestReadConfigurationsServicesGenerateHashCode(t *testing.T) {
186+
configurationsParser := Init("", "")
187+
188+
configurationsParser.readFileBytesByFilePathDependency = mockReadfilebytesBeelzebubServiceConfiguration
189+
configurationsParser.gelAllFilesNameByDirNameDependency = mockReadDirValid
190+
191+
beelzebubServicesConfiguration, err := configurationsParser.ReadConfigurationsServices()
192+
193+
hashCode, errHashCode := beelzebubServicesConfiguration[0].HashCode()
194+
195+
assert.Nil(t, err)
196+
assert.Nil(t, errHashCode)
197+
assert.Equal(t, hashCode, "9c349217fdf25f8a1751c33de9e06799a6c96fa996c2dba40df6d2c34c3025a0")
198+
}
199+
185200
func TestReadConfigurationsPluginGuardrailsValid(t *testing.T) {
186201
configurationsParser := Init("", "")
187202

plugins/beelzebub-cloud.go

Lines changed: 127 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import (
44
"encoding/json"
55
"errors"
66
"fmt"
7+
"os"
8+
"strings"
9+
"time"
710

811
"github.com/go-resty/resty/v2"
912
"github.com/mariocandela/beelzebub/v3/parser"
@@ -12,10 +15,38 @@ import (
1215
"gopkg.in/yaml.v3"
1316
)
1417

18+
type EventDTO struct {
19+
DateTime string
20+
RemoteAddr string
21+
Protocol string
22+
Command string
23+
CommandOutput string
24+
Status string
25+
Msg string
26+
ID string
27+
Environ string
28+
User string
29+
Password string
30+
Client string
31+
Headers string
32+
HeadersMap map[string][]string
33+
Cookies string
34+
UserAgent string
35+
HostHTTPRequest string
36+
Body string
37+
HTTPMethod string
38+
RequestURI string
39+
Description string
40+
SourceIp string
41+
SourcePort string
42+
TLSServerName string
43+
}
44+
1545
type beelzebubCloud struct {
16-
URI string
17-
AuthToken string
18-
client *resty.Client
46+
URI string
47+
AuthToken string
48+
client *resty.Client
49+
PollingInterval time.Duration
1950
}
2051

2152
type HoneypotConfigResponseDTO struct {
@@ -25,16 +56,30 @@ type HoneypotConfigResponseDTO struct {
2556
LastUpdatedOn string `json:"lastUpdatedOn"`
2657
}
2758

28-
func InitBeelzebubCloud(uri, authToken string) *beelzebubCloud {
29-
return &beelzebubCloud{
30-
URI: uri,
31-
AuthToken: authToken,
32-
client: resty.New(),
59+
func InitBeelzebubCloud(uri, authToken string, enableVerifyConfigurationsChanged bool) *beelzebubCloud {
60+
beelzebubCloud := &beelzebubCloud{
61+
URI: uri,
62+
AuthToken: authToken,
63+
client: resty.New(),
64+
PollingInterval: 15 * time.Second,
65+
}
66+
if enableVerifyConfigurationsChanged {
67+
go func() {
68+
if err := beelzebubCloud.verifyConfigurationsChanged(); err != nil {
69+
log.Fatalf("Error verify configurations changed: %s", err.Error())
70+
}
71+
}()
3372
}
73+
return beelzebubCloud
3474
}
3575

3676
func (beelzebubCloud *beelzebubCloud) SendEvent(event tracer.Event) (bool, error) {
37-
requestJson, err := json.Marshal(event)
77+
eventDTO, err := beelzebubCloud.mapToEventDTO(event)
78+
if err != nil {
79+
return false, err
80+
}
81+
82+
requestJson, err := json.Marshal(eventDTO)
3883
if err != nil {
3984
return false, err
4085
}
@@ -59,9 +104,9 @@ func (beelzebubCloud *beelzebubCloud) SendEvent(event tracer.Event) (bool, error
59104
return response.StatusCode() == 200, nil
60105
}
61106

62-
func (beelzebubCloud *beelzebubCloud) GetHoneypotsConfigurations() ([]parser.BeelzebubServiceConfiguration, error) {
107+
func (beelzebubCloud *beelzebubCloud) GetHoneypotsConfigurations() ([]parser.BeelzebubServiceConfiguration, string, error) {
63108
if beelzebubCloud.AuthToken == "" {
64-
return nil, errors.New("authToken is empty")
109+
return nil, "", errors.New("authToken is empty")
65110
}
66111

67112
response, err := beelzebubCloud.client.R().
@@ -71,34 +116,98 @@ func (beelzebubCloud *beelzebubCloud) GetHoneypotsConfigurations() ([]parser.Bee
71116
Get(fmt.Sprintf("%s/honeypots", beelzebubCloud.URI))
72117

73118
if err != nil {
74-
return nil, err
119+
return nil, "", err
75120
}
76121

77122
if response.StatusCode() != 200 {
78-
return nil, errors.New(fmt.Sprintf("Response code: %v, error: %s", response.StatusCode(), string(response.Body())))
123+
return nil, "", errors.New(fmt.Sprintf("Response code: %v, error: %s", response.StatusCode(), string(response.Body())))
79124
}
80125

81126
var honeypotsConfig []HoneypotConfigResponseDTO
82127

83128
if err = json.Unmarshal(response.Body(), &honeypotsConfig); err != nil {
84-
return nil, err
129+
return nil, "", err
85130
}
86131

87132
var servicesConfiguration = make([]parser.BeelzebubServiceConfiguration, 0)
133+
var localHashBuilder strings.Builder
88134

89135
for _, honeypotConfig := range honeypotsConfig {
90136
var honeypotsConfig parser.BeelzebubServiceConfiguration
91137

92138
if err = yaml.Unmarshal([]byte(honeypotConfig.Config), &honeypotsConfig); err != nil {
93-
return nil, err
139+
return nil, "", err
94140
}
95141
if err := honeypotsConfig.CompileCommandRegex(); err != nil {
96-
return nil, fmt.Errorf("unable to load service config from cloud: invalid regex: %v", err)
142+
return nil, "", fmt.Errorf("unable to load service config from cloud: invalid regex: %v", err)
97143
}
98144
servicesConfiguration = append(servicesConfiguration, honeypotsConfig)
145+
146+
if hashCode, err := honeypotsConfig.HashCode(); err != nil {
147+
return nil, "", err
148+
} else {
149+
localHashBuilder.WriteString(hashCode)
150+
}
151+
99152
}
100153

101-
log.Debug(servicesConfiguration)
154+
return servicesConfiguration, localHashBuilder.String(), nil
155+
}
156+
157+
var exitFunction func(code int) = os.Exit
158+
159+
func (beelzebubCloud *beelzebubCloud) verifyConfigurationsChanged() error {
160+
var lastConfigurationsHash = ""
161+
for {
162+
log.Debug("Checking configurations...")
163+
_, configurationsHash, err := beelzebubCloud.GetHoneypotsConfigurations()
164+
if err != nil {
165+
return err
166+
}
167+
if len(lastConfigurationsHash) == 0 {
168+
lastConfigurationsHash = configurationsHash
169+
}
170+
if lastConfigurationsHash != configurationsHash {
171+
log.Debug("Configurations changed.")
172+
exitFunction(0)
173+
}
174+
time.Sleep(beelzebubCloud.PollingInterval)
175+
}
176+
}
177+
178+
func (beelzebubCloud *beelzebubCloud) mapToEventDTO(event tracer.Event) (EventDTO, error) {
179+
eventDTO := EventDTO{
180+
DateTime: event.DateTime,
181+
RemoteAddr: event.RemoteAddr,
182+
Protocol: event.Protocol,
183+
Command: event.Command,
184+
CommandOutput: event.CommandOutput,
185+
Status: event.Status,
186+
Msg: event.Msg,
187+
ID: event.ID,
188+
Environ: event.Environ,
189+
User: event.User,
190+
Password: event.Password,
191+
Client: event.Client,
192+
Cookies: event.Cookies,
193+
UserAgent: event.UserAgent,
194+
HostHTTPRequest: event.HostHTTPRequest,
195+
Body: event.Body,
196+
HTTPMethod: event.HTTPMethod,
197+
RequestURI: event.RequestURI,
198+
Description: event.Description,
199+
SourceIp: event.SourceIp,
200+
SourcePort: event.SourcePort,
201+
TLSServerName: event.TLSServerName,
202+
}
203+
204+
if len(event.Headers) > 0 {
205+
headersJSON, err := json.Marshal(event.Headers)
206+
if err != nil {
207+
return EventDTO{}, fmt.Errorf("failed to marshal headers: %w", err)
208+
}
209+
eventDTO.Headers = string(headersJSON)
210+
}
102211

103-
return servicesConfiguration, nil
212+
return eventDTO, nil
104213
}

0 commit comments

Comments
 (0)