Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add alert heartbeats #94

Merged
merged 2 commits into from
Dec 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,38 @@ $ head -n 100000 huge.eve.json | scripts/makelpush | redis-cli > /dev/null

FEVER can optionally inject in-band test data into downstream submissions, such as passive DNS observations, so allow automated checks that receiving components are updated correctly.

* For injecting test alerts into the forwarded stream, use the `heartbeat.alert-times` list to specify when an alert heartbeat should be injected. The approach is identical to the one for the general heartbeats: at each specified time, an alert like
```json
{
"timestamp": "2021-12-09T09:49:35.641252+0000",
"event_type": "alert",
"src_ip": "192.0.2.1",
"src_port": 39106,
"dest_ip": "192.0.2.2",
"dest_port": 80,
"proto": "TCP",
"alert": {
"action": "allowed",
"gid": 0,
"signature_id": 0,
"rev": 0,
"signature": "DCSO FEVER TEST alert",
"category": "Not Suspicious Traffic",
"severity": 0
},
"http": {
"hostname": "test-2021-12-09.vast",
"url": "/just-visiting",
"http_user_agent": "FEVER",
"http_content_type": "text/html",
"http_method": "GET",
"protocol": "HTTP/1.1",
"status": 200,
"length": 42
}
}
```
will be created and forwarded.
* For passive DNS observation submissions, use the `pdns.test-domain` config item to insert a dummy entry for that domain, e.g. for `pdns.tests-domain` set to `heartbeat.fever-heartbeat`:
```json
{
Expand Down
3 changes: 2 additions & 1 deletion cmd/fever/cmds/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -513,8 +513,9 @@ func mainfunc(cmd *cobra.Command, args []string) {
// Heartbeat injector
enableHeartbeat := viper.GetBool("heartbeat.enable")
heartbeatTimes := viper.GetStringSlice("heartbeat.times")
heartbeatAlertTimes := viper.GetStringSlice("heartbeat.alert-times")
if enableHeartbeat {
hi, err := processing.MakeHeartbeatInjector(forwardHandler, heartbeatTimes)
hi, err := processing.MakeHeartbeatInjector(forwardHandler, heartbeatTimes, heartbeatAlertTimes)
if err != nil {
log.Fatal(err)
}
Expand Down
7 changes: 5 additions & 2 deletions fever.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,15 @@ stenosis:
#add-fields:
# sensor-id: foobar

# Send 'heartbeat' HTTP event
# Send 'heartbeat' HTTP or alert event
heartbeat:
enable: false
# 24h HH:MM strings with local times to send heartbeat
# 24h HH:MM strings with local times to send heartbeat as HTTP event
times:
- "00:01"
# 24h HH:MM strings with local times to send heartbeat as alert
#alert-times:
# - "00:02"

# Configuration for detailed flow metadata submission.
flowextract:
Expand Down
40 changes: 32 additions & 8 deletions processing/heartbeat_injector.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package processing

// DCSO FEVER
// Copyright (c) 2020, DCSO GmbH
// Copyright (c) 2020, 2021, DCSO GmbH

import (
"encoding/json"
Expand Down Expand Up @@ -29,13 +29,14 @@ var (
type HeartbeatInjector struct {
SensorID string
Times []string
AlertTimes []string
CloseChan chan bool
Logger *log.Entry
ForwardHandler Handler
}

// MakeHeartbeatInjector creates a new HeartbeatInjector.
func MakeHeartbeatInjector(forwardHandler Handler, injectTimes []string) (*HeartbeatInjector, error) {
func MakeHeartbeatInjector(forwardHandler Handler, injectTimes []string, alertTimes []string) (*HeartbeatInjector, error) {
sensorID, err := util.GetSensorID()
if err != nil {
return nil, err
Expand All @@ -45,27 +46,33 @@ func MakeHeartbeatInjector(forwardHandler Handler, injectTimes []string) (*Heart
return nil, fmt.Errorf("invalid time specification in heartbeat injector config: '%s'", v)
}
}
for _, v := range alertTimes {
if !injectTimeRegex.Match([]byte(v)) {
return nil, fmt.Errorf("invalid alert time specification in heartbeat injector config: '%s'", v)
}
}
a := &HeartbeatInjector{
ForwardHandler: forwardHandler,
Logger: log.WithFields(log.Fields{
"domain": "heartbeat_injector",
}),
Times: injectTimes,
CloseChan: make(chan bool),
SensorID: sensorID,
Times: injectTimes,
AlertTimes: alertTimes,
CloseChan: make(chan bool),
SensorID: sensorID,
}
return a, nil
}

func makeHeartbeatEvent() types.Entry {
func makeHeartbeatEvent(eventType string) types.Entry {
now := time.Now()
entry := types.Entry{
SrcIP: "192.0.2.1",
SrcPort: int64(rand.Intn(60000) + 1025),
DestIP: "192.0.2.2",
DestPort: 80,
Timestamp: time.Now().Format(types.SuricataTimestampFormat),
EventType: "http",
EventType: eventType,
Proto: "TCP",
HTTPHost: fmt.Sprintf("test-%d-%02d-%02d.vast",
now.Year(), now.Month(), now.Day()),
Expand Down Expand Up @@ -93,6 +100,15 @@ func makeHeartbeatEvent() types.Entry {
HTTPContentType: "text/html",
},
}
if eventType == "alert" {
eve.Alert = &types.AlertEvent{
Action: "allowed",
Category: "Not Suspicious Traffic",
Signature: "DCSO FEVER TEST alert",
}
entry.HTTPHost = "testalert.fever"
eve.HTTP.Hostname = entry.HTTPHost
}
json, err := json.Marshal(eve)
if err != nil {
log.Warn(err)
Expand All @@ -113,7 +129,15 @@ func (a *HeartbeatInjector) Run() {
curTime := time.Now().Format("15:04")
for _, timeVal := range a.Times {
if curTime == timeVal {
ev := makeHeartbeatEvent()
ev := makeHeartbeatEvent("http")
a.Logger.Debugf("creating heartbeat event for %s: %s",
curTime, string(ev.JSONLine))
a.ForwardHandler.Consume(&ev)
}
}
for _, timeVal := range a.AlertTimes {
if curTime == timeVal {
ev := makeHeartbeatEvent("alert")
a.Logger.Debugf("creating heartbeat event for %s: %s",
curTime, string(ev.JSONLine))
a.ForwardHandler.Consume(&ev)
Expand Down
53 changes: 50 additions & 3 deletions processing/heartbeat_injector_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package processing

// DCSO FEVER
// Copyright (c) 2020, DCSO GmbH
// Copyright (c) 2020, 2021, DCSO GmbH

import (
"fmt"
Expand Down Expand Up @@ -39,7 +39,18 @@ func TestHeartbeatInjectorInvalidTime(t *testing.T) {
Entries: make([]types.Entry, 0),
}

_, err := MakeHeartbeatInjector(&hbth, []string{"foo"})
_, err := MakeHeartbeatInjector(&hbth, []string{"foo"}, []string{})
if err == nil {
t.Fatal("invalid time not caught")
}
}

func TestHeartbeatInjectorInvalidAlertTime(t *testing.T) {
hbth := HeartbeatTestFwdHandler{
Entries: make([]types.Entry, 0),
}

_, err := MakeHeartbeatInjector(&hbth, []string{}, []string{"foo"})
if err == nil {
t.Fatal("invalid time not caught")
}
Expand All @@ -53,7 +64,7 @@ func TestHeartbeatInjector(t *testing.T) {
now := time.Now()
ctime := []string{now.Format("15:04")}

hbi, err := MakeHeartbeatInjector(&hbth, ctime)
hbi, err := MakeHeartbeatInjector(&hbth, ctime, []string{})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -82,3 +93,39 @@ func TestHeartbeatInjector(t *testing.T) {
t.Fatalf("wrong hostname for heartbeat: %s", seenHost)
}
}

func TestHeartbeatAlertInjector(t *testing.T) {
hbth := HeartbeatTestFwdHandler{
Entries: make([]types.Entry, 0),
}

now := time.Now()
atime := []string{now.Format("15:04")}

hbi, err := MakeHeartbeatInjector(&hbth, []string{}, atime)
if err != nil {
t.Fatal(err)
}

hbi.Run()
for {
hbth.Lock.Lock()
if len(hbth.Entries) > 0 {
hbth.Lock.Unlock()
break
}
hbth.Lock.Unlock()
time.Sleep(100 * time.Millisecond)
}
hbi.Stop()

hbJSON := hbth.Entries[0].JSONLine

sig, err := jsonparser.GetString([]byte(hbJSON), "alert", "signature")
if err != nil {
t.Fatal(err)
}
if sig != "DCSO FEVER TEST alert" {
t.Fatalf("wrong signature for test alert: %s", sig)
}
}