Skip to content

Commit

Permalink
Merge pull request #646 from Security-Onion-Solutions/2.4/dev
Browse files Browse the repository at this point in the history
2.4.110
  • Loading branch information
TOoSmOotH authored Oct 7, 2024
2 parents fd9c3b8 + 227344c commit c052960
Show file tree
Hide file tree
Showing 44 changed files with 538 additions and 100 deletions.
4 changes: 3 additions & 1 deletion Dockerfile.kratos
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
FROM ghcr.io/security-onion-solutions/golang:1.22.6 AS builder

ARG OWNER=ory
ARG VERSION=v1.2.0
ARG VERSION=v1.3.0

RUN addgroup --system ory; \
adduser --system ory --no-create-home --disabled-password --ingroup ory --disabled-login
Expand Down Expand Up @@ -36,6 +36,8 @@ FROM ghcr.io/security-onion-solutions/ubuntu:24.10

RUN apt-get update && apt-get upgrade -y

RUN apt-get update && apt-get install -y ca-certificates

ENV DSN=sqlite:///kratos-data/db.sqlite?_fk=true

ARG UID=928
Expand Down
22 changes: 17 additions & 5 deletions agent/jobmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ package agent
import (
"errors"
"io"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"syscall"
"time"

"github.com/apex/log"
Expand Down Expand Up @@ -120,12 +120,24 @@ func (mgr *JobManager) CleanupJob(job *model.Job) {
}

func (mgr *JobManager) updateOnlineTime(src string) {
fi, err := os.Stat(src)
cmd := exec.Command("stat", src, "-c", "%W")
out, err := cmd.CombinedOutput()
if err != nil {
log.WithFields(log.Fields{"statSrcFile": src, "statOutput": out}).WithError(err).Error("unable to run stat against original dir")
return
}
stat := fi.Sys().(*syscall.Stat_t)
mgr.node.OnlineTime = time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec))
log.WithFields(log.Fields{
"statSrcFile": src,
"statOutput": out,
"statExitCode": cmd.ProcessState.ExitCode(),
}).Debug("ran stat against original dir")
secondsSinceEpochStr := strings.TrimSpace(string(out))
secondsSinceEpoch, parseerr := strconv.ParseInt(secondsSinceEpochStr, 10, 64)
if parseerr != nil {
log.WithField("statOutput", secondsSinceEpochStr).WithError(parseerr).Error("unable to convert stat output to number")
return
}
mgr.node.OnlineTime = time.Unix(int64(secondsSinceEpoch), 0)
log.WithField("onlineTime", mgr.node.OnlineTime).Info("Updated online time (node installation time)")
}

Expand Down
1 change: 1 addition & 0 deletions agent/jobmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ func TestUpdateDataEpoch(t *testing.T) {
}

func TestOnlineTime(t *testing.T) {
t.Skip("Skipping due to docker build base image not having the correct version of stat tool")
// prep test object
jm := &JobManager{
node: &model.Node{},
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ require (
github.com/pierrec/lz4/v4 v4.1.21
github.com/pkg/errors v0.9.1
github.com/samber/lo v1.47.0
github.com/tj/assert v0.0.3
go.uber.org/mock v0.4.0
golang.org/x/mod v0.20.0
)
Expand Down
2 changes: 1 addition & 1 deletion html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2131,7 +2131,7 @@ <h2 v-text="i18n.licensing"></h2>
<v-row class="my-1">
<v-col cols="3" class="pa-1 text-right font-weight-bold text-no-wrap">{{ i18n.features }}:</v-col>
<v-col v-if="$root.licenseKey.features" cols="9" class="pa-1">
<v-chip v-for="feature in $root.licenseKey.features" small class="mx-1">
<v-chip v-for="feature in $root.licenseKey.features" v-if="$root.localizeMessage(feature) != feature" small class="mx-1">
{{ $root.localizeMessage(feature) }}
</v-chip>
</v-col>
Expand Down
2 changes: 2 additions & 0 deletions licensing/license_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const FEAT_NTF = "ntf"
const FEAT_ODC = "odc"
const FEAT_STG = "stg"
const FEAT_TTR = "ttr"
const FEAT_RPT = "rpt"

const PUBLIC_KEY = `
-----BEGIN PUBLIC KEY-----
Expand Down Expand Up @@ -173,6 +174,7 @@ func CreateAvailableFeatureList() []string {
available = append(available, FEAT_ODC)
available = append(available, FEAT_STG)
available = append(available, FEAT_TTR)
available = append(available, FEAT_RPT)
return available
}

Expand Down
19 changes: 5 additions & 14 deletions licensing/license_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,15 @@ func TestListAvailableFeatures(tester *testing.T) {

Init(EXPIRED_KEY)
manager.status = LICENSE_STATUS_ACTIVE
assert.Len(tester, ListAvailableFeatures(), 7)
assert.Len(tester, ListAvailableFeatures(), 8)
assert.Equal(tester, ListAvailableFeatures()[0], FEAT_FPS)
assert.Equal(tester, ListAvailableFeatures()[1], FEAT_GMD)
assert.Equal(tester, ListAvailableFeatures()[2], FEAT_LKS)
assert.Equal(tester, ListAvailableFeatures()[3], FEAT_NTF)
assert.Equal(tester, ListAvailableFeatures()[4], FEAT_ODC)
assert.Equal(tester, ListAvailableFeatures()[5], FEAT_STG)
assert.Equal(tester, ListAvailableFeatures()[6], FEAT_TTR)
assert.Equal(tester, ListAvailableFeatures()[7], FEAT_RPT)
}

func TestListEnabledFeaturesUnprovisioned(tester *testing.T) {
Expand All @@ -139,16 +140,6 @@ func TestListEnabledFeaturesUnprovisioned(tester *testing.T) {
Init("")
assert.Len(tester, ListEnabledFeatures(), 0)

Init(EXPIRED_KEY)
assert.Len(tester, ListEnabledFeatures(), 7)
assert.Equal(tester, ListEnabledFeatures()[0], FEAT_FPS)
assert.Equal(tester, ListEnabledFeatures()[1], FEAT_GMD)
assert.Equal(tester, ListEnabledFeatures()[2], FEAT_LKS)
assert.Equal(tester, ListEnabledFeatures()[3], FEAT_NTF)
assert.Equal(tester, ListEnabledFeatures()[4], FEAT_ODC)
assert.Equal(tester, ListEnabledFeatures()[5], FEAT_STG)
assert.Equal(tester, ListEnabledFeatures()[6], FEAT_TTR)

Init(EXPIRED_KEY)
manager.licenseKey.Features = append(manager.licenseKey.Features, "foo")
manager.licenseKey.Features = append(manager.licenseKey.Features, "bar")
Expand All @@ -166,14 +157,14 @@ func TestGetLicenseKey(tester *testing.T) {
assert.Equal(tester, key.Nodes, 1)
assert.Equal(tester, key.SocUrl, "https://somewhere.invalid")
assert.Equal(tester, key.DataUrl, "https://another.place")
assert.Len(tester, key.Features, 7)
assert.Len(tester, key.Features, 8)

// Modify the returned object and make sure it doesn't affect the orig object
key.Users = 100
key.Features = append(key.Features, "foo")
assert.Equal(tester, GetLicenseKey().Users, 1)
assert.Len(tester, key.Features, 8)
assert.Len(tester, GetLicenseKey().Features, 7)
assert.Len(tester, key.Features, 9)
assert.Len(tester, GetLicenseKey().Features, 8)
}

func TestGetStatus(tester *testing.T) {
Expand Down
11 changes: 11 additions & 0 deletions model/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Setting struct {
Syntax string `json:"syntax"`
ForcedType string `json:"forcedType"`
Duplicates bool `json:"duplicates"`
JinjaEscaped bool `json:"jinjaEscaped"`
}

func NewSetting(id string) *Setting {
Expand All @@ -44,6 +45,16 @@ func (setting *Setting) SetId(id string) {
setting.Id = id
}

func (setting *Setting) SupportsJinja() bool {
// Assume duplicated settings should support Jinja, since those lose their annotations.
return setting.JinjaEscaped || setting.IsDuplicatedSetting()
}

func (setting *Setting) IsDuplicatedSetting() bool {
// Assume descriptionless settings are duplicated, since annotations are lost for duplicated settings
return len(setting.Description) == 0
}

func IsValidMinionId(id string) bool {
return regexp.MustCompile(`^[a-zA-Z0-9_.-]+$`).MatchString(id)
}
Expand Down
2 changes: 1 addition & 1 deletion model/custom_ruleset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ package model
import (
"testing"

"github.com/tj/assert"
"github.com/stretchr/testify/assert"
)

func TestGetCustomRulesetsDefault(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion model/detection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (
"testing"

"github.com/security-onion-solutions/securityonion-soc/util"
"github.com/tj/assert"

"github.com/stretchr/testify/assert"
)

func TestDetectionOverrideValidate(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion model/rulerepo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

"github.com/security-onion-solutions/securityonion-soc/util"

"github.com/tj/assert"
"github.com/stretchr/testify/assert"
)

func TestGetRepos(t *testing.T) {
Expand Down
21 changes: 15 additions & 6 deletions model/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ type Status struct {
}

type GridStatus struct {
TotalNodeCount int `json:"totalNodeCount"`
UnhealthyNodeCount int `json:"unhealthyNodeCount"`
Eps int `json:"eps"`
TotalNodeCount int `json:"totalNodeCount"`
UnhealthyNodeCount int `json:"unhealthyNodeCount"`
AwaitingRebootNodeCount int `json:"awaitingRebootNodeCount"`
Eps int `json:"eps"`
}

type AlertsStatus struct {
Expand All @@ -37,11 +38,19 @@ type EngineState struct {
SyncFailure bool `json:"syncFailure"`
}

func (state *EngineState) IsFailureState() bool {
return state.IntegrityFailure || state.MigrationFailure || state.SyncFailure
}

func NewStatus() *Status {
newStatus := &Status{
Grid: &GridStatus{},
Alerts: &AlertsStatus{},
Detections: &DetectionsStatus{},
Grid: &GridStatus{},
Alerts: &AlertsStatus{},
Detections: &DetectionsStatus{
ElastAlert: &EngineState{},
Strelka: &EngineState{},
Suricata: &EngineState{},
},
}
return newStatus
}
27 changes: 27 additions & 0 deletions model/status_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2019 Jason Ertel (github.com/jertel).
// Copyright 2020-2024 Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
// or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
// https://securityonion.net/license; you may not use this file except in compliance with the
// Elastic License 2.0.

package model

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestIsFailureState(tester *testing.T) {
status := NewStatus()
assert.False(tester, status.Detections.ElastAlert.IsFailureState())
assert.False(tester, status.Detections.Strelka.IsFailureState())
assert.False(tester, status.Detections.Suricata.IsFailureState())

status.Detections.ElastAlert.IntegrityFailure = true
status.Detections.Strelka.MigrationFailure = true
status.Detections.Suricata.SyncFailure = true
assert.True(tester, status.Detections.ElastAlert.IsFailureState())
assert.True(tester, status.Detections.Strelka.IsFailureState())
assert.True(tester, status.Detections.Suricata.IsFailureState())
}
3 changes: 3 additions & 0 deletions module/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ package module

import (
"errors"

"github.com/security-onion-solutions/securityonion-soc/syntax"
)

func GetString(options map[string]interface{}, key string) (string, error) {
Expand All @@ -33,6 +35,7 @@ func GetString(options map[string]interface{}, key string) (string, error) {
} else {
err = errors.New("Required option is missing: " + key + " (string)")
}
value = syntax.UnescapeJinja(value)
return value, err
}

Expand Down
4 changes: 2 additions & 2 deletions module/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ func TestGetString(tester *testing.T) {
_, err := GetString(options, "MyKey")
assert.Error(tester, err)

options["MyKey"] = "MyValue"
options["MyKey"] = "MyValue [SO_JINJA_SL_START] foo [SO_JINJA_SL_END]"
actual, err := GetString(options, "MyKey")
if assert.Nil(tester, err) {
assert.Equal(tester, "MyValue", actual)
assert.Equal(tester, "MyValue {{ foo }}", actual)
}
}

Expand Down
17 changes: 11 additions & 6 deletions server/detectionhandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func (h *DetectionHandler) createDetection(w http.ResponseWriter, r *http.Reques
return
}

errMap, err := SyncLocalDetections(ctx, h.server, []*model.Detection{detect})
errMap, err := syncLocalDetections(ctx, h.server, []*model.Detection{detect})
if err != nil {
web.Respond(w, r, http.StatusInternalServerError, err)
return
Expand Down Expand Up @@ -368,7 +368,7 @@ func (h *DetectionHandler) updateDetection(w http.ResponseWriter, r *http.Reques

detect.PersistChange = true

errMap, err := SyncLocalDetections(ctx, h.server, []*model.Detection{detect})
errMap, err := syncLocalDetections(ctx, h.server, []*model.Detection{detect})
if err != nil {
fixed := false
if detect.IsEnabled && !filterApplied {
Expand All @@ -380,7 +380,7 @@ func (h *DetectionHandler) updateDetection(w http.ResponseWriter, r *http.Reques

detect, uerr = h.server.Detectionstore.UpdateDetection(ctx, detect)
if uerr == nil {
errMap, err = SyncLocalDetections(ctx, h.server, []*model.Detection{detect})
errMap, err = syncLocalDetections(ctx, h.server, []*model.Detection{detect})
fixed = true
}
}
Expand Down Expand Up @@ -434,7 +434,7 @@ func (h *DetectionHandler) deleteDetection(w http.ResponseWriter, r *http.Reques
old.IsEnabled = false
old.PendingDelete = true

errMap, err := SyncLocalDetections(ctx, h.server, []*model.Detection{old})
errMap, err := syncLocalDetections(ctx, h.server, []*model.Detection{old})
if err != nil {
web.Respond(w, r, http.StatusInternalServerError, err)
return
Expand Down Expand Up @@ -746,7 +746,7 @@ func (h *DetectionHandler) bulkUpdateDetectionAsync(ctx context.Context, body *B

start = time.Now()

errMap, err = SyncLocalDetections(ctx, h.server, dirty)
errMap, err = syncLocalDetections(ctx, h.server, dirty)
if err != nil {
logger.WithError(err).WithField("errMap", detections.TruncateMap(errMap, 5)).Error("unable to sync detections after bulk update")
return
Expand All @@ -763,14 +763,19 @@ func (h *DetectionHandler) bulkUpdateDetectionAsync(ctx context.Context, body *B
syncDur = time.Since(start)
}

func SyncLocalDetections(ctx context.Context, srv *Server, detections []*model.Detection) (errMap map[string]string, err error) {
func syncLocalDetections(ctx context.Context, srv *Server, detections []*model.Detection) (errMap map[string]string, err error) {
errMap = map[string]string{} // map[det.PublicID]error
defer func() {
if len(errMap) == 0 {
errMap = nil
}
}()

err = srv.CheckAuthorized(ctx, "write", "detections")
if err != nil {
return nil, err
}

byEngine := map[model.EngineName][]*model.Detection{}
for _, detect := range detections {
byEngine[detect.Engine] = append(byEngine[detect.Engine], detect)
Expand Down
Loading

0 comments on commit c052960

Please sign in to comment.