Skip to content

Commit

Permalink
more work on reloading configuration
Browse files Browse the repository at this point in the history
continuation of previous commit bde5d34

 - Allow to reconfigure stats limits (how many events we keep on the
   daemon, number of workers, ...)
 - Allow to reconfigure loggers.
  • Loading branch information
gustavo-iniguez-goya committed May 11, 2024
1 parent 7d08b2b commit 0b67c1a
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 67 deletions.
22 changes: 18 additions & 4 deletions daemon/statistics/stats.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package statistics

import (
"context"
"strconv"
"sync"
"time"
Expand Down Expand Up @@ -29,6 +30,8 @@ type conEvent struct {
// Statistics holds the connections and statistics the daemon intercepts.
// The connections are stored in the Events slice.
type Statistics struct {
ctx context.Context
cancel context.CancelFunc
Started time.Time
logger *loggers.LoggerManager
rules *rule.Loader
Expand Down Expand Up @@ -59,7 +62,10 @@ type Statistics struct {

// New returns a new Statistics object and initializes the go routines to update the stats.
func New(rules *rule.Loader) (stats *Statistics) {
ctx, cancel := context.WithCancel(context.Background())
stats = &Statistics{
ctx: ctx,
cancel: cancel,
Started: time.Now(),
Events: make([]*Event, 0),
ByProto: make(map[string]uint64),
Expand All @@ -79,14 +85,18 @@ func New(rules *rule.Loader) (stats *Statistics) {
}

// SetLoggers sets the configured loggers where we'll write the events.
func (s *Statistics) SetLoggers(loggers *loggers.LoggerManager) {
s.logger = loggers
func (s *Statistics) SetLoggers(loggermgr *loggers.LoggerManager) {
s.Lock()
s.logger = loggermgr
s.Unlock()
}

// SetLimits configures the max events to keep in the backlog before sending
// the stats to the UI, or while the UI is not connected.
// if the backlog is full, it'll be shifted by one.
func (s *Statistics) SetLimits(config StatsConfig) {
s.cancel()
s.ctx, s.cancel = context.WithCancel(context.Background())
if config.MaxEvents > 0 {
s.maxEvents = config.MaxEvents
}
Expand All @@ -99,7 +109,7 @@ func (s *Statistics) SetLimits(config StatsConfig) {
}
log.Info("Stats, max events: %d, max stats: %d, max workers: %d", s.maxStats, s.maxEvents, s.maxWorkers)
for i := 0; i < s.maxWorkers; i++ {
go s.eventWorker(i)
go s.eventWorker(i, s.ctx.Done())
}

}
Expand Down Expand Up @@ -164,15 +174,19 @@ func (s *Statistics) incMap(m *map[string]uint64, key string) {
}
}

func (s *Statistics) eventWorker(id int) {
func (s *Statistics) eventWorker(id int, done <-chan struct{}) {
log.Debug("Stats worker #%d started.", id)

for true {
select {
case <-done:
goto Exit
case job := <-s.jobs:
s.onConnection(job.con, job.match, job.wasMissed)
}
}
Exit:
log.Debug("stats.worker() %d exited", id)
}

func (s *Statistics) onConnection(con *conman.Connection, match *rule.Rule, wasMissed bool) {
Expand Down
4 changes: 0 additions & 4 deletions daemon/ui/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,9 @@ const (
// New returns the configuration that the UI will use
// to connect with the server.
func New(config *config.Config) (grpc.DialOption, error) {
config.RLock()

credsType := config.Server.Authentication.Type
tlsOpts := config.Server.Authentication.TLSOptions

config.RUnlock()

if credsType == "" || credsType == AuthSimple {
log.Debug("UI auth: simple")
return grpc.WithInsecure(), nil
Expand Down
29 changes: 14 additions & 15 deletions daemon/ui/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ var (
// While the GUI is connected, deny by default everything until the user takes an action.
clientConnectedRule = rule.Create("ui.client.connected", "", true, false, false, rule.Deny, rule.Once, dummyOperator)
clientErrorRule = rule.Create("ui.client.error", "", true, false, false, rule.Allow, rule.Once, dummyOperator)
clientConfig config.Config

maxQueuedAlerts = 1024
)
Expand All @@ -42,6 +41,7 @@ type Client struct {
streamNotifications protocol.UI_NotificationsClient
clientCtx context.Context
clientCancel context.CancelFunc
config config.Config

loggers *loggers.LoggerManager
stats *statistics.Statistics
Expand Down Expand Up @@ -88,9 +88,8 @@ func NewClient(socketPath, localConfigFile string, stats *statistics.Statistics,
if socketPath != "" {
c.setSocketPath(c.getSocketPath(socketPath))
}
procmon.EventsCache.SetComputeChecksums(clientConfig.Rules.EnableChecksums)
rules.EnableChecksums(clientConfig.Rules.EnableChecksums)
stats.SetLimits(clientConfig.Stats)
procmon.EventsCache.SetComputeChecksums(c.config.Rules.EnableChecksums)
rules.EnableChecksums(c.config.Rules.EnableChecksums)

return c
}
Expand All @@ -108,26 +107,26 @@ func (c *Client) Close() {
// ProcMonitorMethod returns the monitor method configured.
// If it's not present in the config file, it'll return an empty string.
func (c *Client) ProcMonitorMethod() string {
clientConfig.RLock()
defer clientConfig.RUnlock()
return clientConfig.ProcMonitorMethod
c.RLock()
defer c.RUnlock()
return c.config.ProcMonitorMethod
}

// InterceptUnknown returns
func (c *Client) InterceptUnknown() bool {
clientConfig.RLock()
defer clientConfig.RUnlock()
return clientConfig.InterceptUnknown
c.RLock()
defer c.RUnlock()
return c.config.InterceptUnknown
}

// GetFirewallType returns the firewall to use
func (c *Client) GetFirewallType() string {
clientConfig.RLock()
defer clientConfig.RUnlock()
if clientConfig.Firewall == "" {
c.RLock()
defer c.RUnlock()
if c.config.Firewall == "" {
return iptables.Name
}
return clientConfig.Firewall
return c.config.Firewall
}

// DefaultAction returns the default configured action for
Expand Down Expand Up @@ -255,7 +254,7 @@ func (c *Client) openSocket() (err error) {
c.Lock()
defer c.Unlock()

dialOption, err := auth.New(&clientConfig)
dialOption, err := auth.New(&c.config)
if err != nil {
return fmt.Errorf("Invalid client auth options: %s", err)
}
Expand Down
38 changes: 21 additions & 17 deletions daemon/ui/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import (
"io/ioutil"
"os"
"reflect"
"sync"

"github.com/evilsocket/opensnitch/daemon/log"
"github.com/evilsocket/opensnitch/daemon/log/loggers"
"github.com/evilsocket/opensnitch/daemon/statistics"
)

type (
serverTLSOptions struct {
// ServerTLSOptions struct
ServerTLSOptions struct {
CACert string `json:"CACert"`
ServerCert string `json:"ServerCert"`
ServerKey string `json:"ServerKey"`
Expand All @@ -31,36 +31,42 @@ type (
// VerifyPeerCertificate bool
}

serverAuth struct {
// ServerAuth struct
ServerAuth struct {
// token?, google?, simple-tls, mutual-tls
Type string `json:"Type"`
TLSOptions serverTLSOptions `json:"TLSOptions"`
TLSOptions ServerTLSOptions `json:"TLSOptions"`
}

serverConfig struct {
// ServerConfig struct
ServerConfig struct {
Address string `json:"Address"`
Authentication serverAuth `json:"Authentication"`
Authentication ServerAuth `json:"Authentication"`
LogFile string `json:"LogFile"`
Loggers []loggers.LoggerConfig `json:"Loggers"`
}

rulesOptions struct {
// RulesOptions struct
RulesOptions struct {
Path string `json:"Path"`
EnableChecksums bool `json:"EnableChecksums"`
}

fwOptions struct {
// FwOptions struct
FwOptions struct {
Firewall string `json:"Firewall"`
ConfigPath string `json:"ConfigPath"`
BypassQueue string `json:"BypassQueue"`
MonitorInterval string `json:"MonitorInterval"`
}

ebpfOptions struct {
// EbpfOptions struct
EbpfOptions struct {
ModulesPath string `json:"ModulesPath"`
}

internalOptions struct {
// InternalOptions struct
InternalOptions struct {
GCPercent int `json:"GCPercent"`
}
)
Expand All @@ -72,18 +78,16 @@ type Config struct {
DefaultAction string `json:"DefaultAction"`
DefaultDuration string `json:"DefaultDuration"`
ProcMonitorMethod string `json:"ProcMonitorMethod"`
FwOptions fwOptions `json:"FwOptions"`
Ebpf ebpfOptions `json:"Ebpf"`
Server serverConfig `json:"Server"`
Rules rulesOptions `json:"Rules"`
FwOptions FwOptions `json:"FwOptions"`
Ebpf EbpfOptions `json:"Ebpf"`
Server ServerConfig `json:"Server"`
Rules RulesOptions `json:"Rules"`
Internal InternalOptions `json:"Internal"`
Stats statistics.StatsConfig `json:"Stats"`
Internal internalOptions `json:"Internal"`

InterceptUnknown bool `json:"InterceptUnknown"`
LogUTC bool `json:"LogUTC"`
LogMicro bool `json:"LogMicro"`

sync.RWMutex
}

// Parse determines if the given configuration is ok.
Expand Down
59 changes: 32 additions & 27 deletions daemon/ui/config_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ui

import (
"fmt"
"reflect"
"strings"

"runtime/debug"
Expand Down Expand Up @@ -41,10 +42,10 @@ func (c *Client) setSocketPath(socketPath string) {
}

func (c *Client) isProcMonitorEqual(newMonitorMethod string) bool {
clientConfig.RLock()
defer clientConfig.RUnlock()
c.RLock()
defer c.RUnlock()

return newMonitorMethod == clientConfig.ProcMonitorMethod
return newMonitorMethod == c.config.ProcMonitorMethod
}

func (c *Client) loadDiskConfiguration(reload bool) {
Expand Down Expand Up @@ -83,7 +84,9 @@ func (c *Client) loadConfiguration(reload bool, rawConfig []byte) error {
if err := c.reloadConfiguration(reload, newConfig); err != nil {
return fmt.Errorf("reloading configuration: %s", err.Msg)
}
clientConfig = newConfig
c.Lock()
c.config = newConfig
c.Unlock()
return nil
}

Expand All @@ -100,15 +103,24 @@ func (c *Client) reloadConfiguration(reload bool, newConfig config.Config) *moni
log.Close()
log.OpenFile(newConfig.Server.LogFile)
}
if !reflect.DeepEqual(c.config.Server.Loggers, newConfig.Server.Loggers) {
log.Debug("[config] reloading config.server.loggers")
c.loggers.Stop()
c.loggers.Load(newConfig.Server.Loggers)
c.stats.SetLoggers(c.loggers)
} else {
log.Debug("[config] config.server.loggers not changed")
}

if !reflect.DeepEqual(newConfig.Stats, c.config.Stats) {
log.Debug("[config] reloading config.stats")
c.stats.SetLimits(newConfig.Stats)
} else {
log.Debug("[config] config.stats not changed")
}

reconnect := newConfig.Server.Authentication.Type != clientConfig.Server.Authentication.Type ||
newConfig.Server.Authentication.TLSOptions.CACert != clientConfig.Server.Authentication.TLSOptions.CACert ||
newConfig.Server.Authentication.TLSOptions.ServerCert != clientConfig.Server.Authentication.TLSOptions.ServerCert ||
newConfig.Server.Authentication.TLSOptions.ServerKey != clientConfig.Server.Authentication.TLSOptions.ServerKey ||
newConfig.Server.Authentication.TLSOptions.ClientCert != clientConfig.Server.Authentication.TLSOptions.ClientCert ||
newConfig.Server.Authentication.TLSOptions.ClientKey != clientConfig.Server.Authentication.TLSOptions.ClientKey ||
newConfig.Server.Authentication.TLSOptions.ClientAuthType != clientConfig.Server.Authentication.TLSOptions.ClientAuthType ||
newConfig.Server.Authentication.TLSOptions.SkipVerify != clientConfig.Server.Authentication.TLSOptions.SkipVerify
reconnect := newConfig.Server.Authentication.Type != c.config.Server.Authentication.Type ||
!reflect.DeepEqual(newConfig.Server.Authentication.TLSOptions, c.config.Server.Authentication.TLSOptions)

if newConfig.Server.Address != "" {
tempSocketPath := c.getSocketPath(newConfig.Server.Address)
Expand Down Expand Up @@ -137,31 +149,24 @@ func (c *Client) reloadConfiguration(reload bool, newConfig config.Config) *moni
clientErrorRule.Duration = rule.Duration(newConfig.DefaultDuration)
}

if newConfig.Internal.GCPercent > 0 && newConfig.Internal.GCPercent != clientConfig.Internal.GCPercent {
if newConfig.Internal.GCPercent > 0 && newConfig.Internal.GCPercent != c.config.Internal.GCPercent {
oldgcpercent := debug.SetGCPercent(newConfig.Internal.GCPercent)
log.Debug("[config] GC percent set to %d, previously was %d", newConfig.Internal.GCPercent, oldgcpercent)
} else {
log.Debug("[config] config.internal.gcpercent not changed")
}

c.rules.EnableChecksums(newConfig.Rules.EnableChecksums)
if clientConfig.Rules.Path != newConfig.Rules.Path {
if c.config.Rules.Path != newConfig.Rules.Path {
c.rules.Reload(newConfig.Rules.Path)
log.Debug("[config] reloading config.rules.path: %s", newConfig.Rules.Path)
} else {
log.Debug("[config] config.rules.path not changed")
}
// TODO:
//c.stats.SetLimits(clientConfig.Stats)
if reload {
c.loggers.Stop()
}
c.loggers.Load(clientConfig.Server.Loggers, clientConfig.Stats.Workers)
c.stats.SetLoggers(c.loggers)

if reload && c.GetFirewallType() != newConfig.Firewall ||
newConfig.FwOptions.ConfigPath != clientConfig.FwOptions.ConfigPath ||
newConfig.FwOptions.MonitorInterval != clientConfig.FwOptions.MonitorInterval {
newConfig.FwOptions.ConfigPath != c.config.FwOptions.ConfigPath ||
newConfig.FwOptions.MonitorInterval != c.config.FwOptions.MonitorInterval {
log.Debug("[config] reloading config.firewall")

firewall.Reload(
Expand All @@ -174,15 +179,15 @@ func (c *Client) reloadConfiguration(reload bool, newConfig config.Config) *moni
}

reloadProc := false
if clientConfig.ProcMonitorMethod == "" ||
newConfig.ProcMonitorMethod != clientConfig.ProcMonitorMethod {
log.Debug("[config] reloading config.ProcMonMethod, old: %s -> new: %s", clientConfig.ProcMonitorMethod, newConfig.ProcMonitorMethod)
if c.config.ProcMonitorMethod == "" ||
newConfig.ProcMonitorMethod != c.config.ProcMonitorMethod {
log.Debug("[config] reloading config.ProcMonMethod, old: %s -> new: %s", c.config.ProcMonitorMethod, newConfig.ProcMonitorMethod)
reloadProc = true
} else {
log.Debug("[config] config.ProcMonMethod not changed")
}

if reload && procmon.MethodIsEbpf() && newConfig.Ebpf.ModulesPath != "" && clientConfig.Ebpf.ModulesPath != newConfig.Ebpf.ModulesPath {
if reload && procmon.MethodIsEbpf() && newConfig.Ebpf.ModulesPath != "" && c.config.Ebpf.ModulesPath != newConfig.Ebpf.ModulesPath {
log.Debug("[config] reloading config.Ebpf.ModulesPath: %s", newConfig.Ebpf.ModulesPath)
reloadProc = true
} else {
Expand Down

0 comments on commit 0b67c1a

Please sign in to comment.