Skip to content

Commit

Permalink
reload more config options without restarting the daemon
Browse files Browse the repository at this point in the history
Reload the configuration without restarting the daemon when changing:
 - server authentication options.
 - GC percentage.
 - Rules path.
 - Loggers.
 - FW options.
 - eBPF modules path.

Also, try to avoid unnecessary changes.
  • Loading branch information
gustavo-iniguez-goya committed May 2, 2024
1 parent f5f30b1 commit bde5d34
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 69 deletions.
25 changes: 23 additions & 2 deletions daemon/log/loggers/logger.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package loggers

import "fmt"
import (
"context"
"fmt"

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

const logTag = "opensnitch"

Expand Down Expand Up @@ -31,14 +36,19 @@ type LoggerConfig struct {

// LoggerManager represents the LoggerManager.
type LoggerManager struct {
ctx context.Context
cancel context.CancelFunc
loggers map[string]Logger
msgs chan []interface{}
count int
}

// NewLoggerManager instantiates all the configured loggers.
func NewLoggerManager() *LoggerManager {
ctx, cancel := context.WithCancel(context.Background())
lm := &LoggerManager{
ctx: ctx,
cancel: cancel,
loggers: make(map[string]Logger),
}

Expand Down Expand Up @@ -81,6 +91,12 @@ func (l *LoggerManager) Load(configs []LoggerConfig, workers int) {

}

func (l *LoggerManager) Stop() {
l.cancel()
l.count = 0
l.loggers = make(map[string]Logger)
}

func (l *LoggerManager) write(args ...interface{}) {
for _, logger := range l.loggers {
logger.Write(logger.Transform(args...))
Expand All @@ -89,10 +105,15 @@ func (l *LoggerManager) write(args ...interface{}) {

func newWorker(id int, l *LoggerManager) {
for {
for msg := range l.msgs {
select {
case <-l.ctx.Done():
goto Exit
case msg := <-l.msgs:
l.write(msg)
}
}
Exit:
log.Debug("logger worker %d exited", id)
}

// Log sends data to the loggers.
Expand Down
4 changes: 0 additions & 4 deletions daemon/procmon/monitor/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@ func stopProcMonitors() {

// ReconfigureMonitorMethod configures a new method for parsing connections.
func ReconfigureMonitorMethod(newMonitorMethod, ebpfModulesPath string) *Error {
if procmon.GetMonitorMethod() == newMonitorMethod {
return nil
}

oldMethod := procmon.GetMonitorMethod()
if oldMethod == "" {
oldMethod = procmon.MethodProc
Expand Down
8 changes: 8 additions & 0 deletions daemon/rule/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ func (l *Loader) HasChecksums(op Operand) {
}
}

// Reload loads rules from the specified path, deleting existing loaded
// rules from memory.
func (l *Loader) Reload(path string) error {
l.rulesKeys = make([]string, 0)
l.rules = make(map[string]*Rule)
return l.Load(path)
}

// Load loads rules files from disk.
func (l *Loader) Load(path string) error {
if core.Exists(path) == false {
Expand Down
4 changes: 2 additions & 2 deletions daemon/ui/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type Client struct {
clientCtx context.Context
clientCancel context.CancelFunc

loggers *loggers.LoggerManager
stats *statistics.Statistics
rules *rule.Loader
con *grpc.ClientConn
Expand All @@ -67,6 +68,7 @@ func NewClient(socketPath, localConfigFile string, stats *statistics.Statistics,
configFile = localConfigFile
}
c := &Client{
loggers: loggers,
stats: stats,
rules: rules,
isUnixSocket: false,
Expand All @@ -88,9 +90,7 @@ func NewClient(socketPath, localConfigFile string, stats *statistics.Statistics,
}
procmon.EventsCache.SetComputeChecksums(clientConfig.Rules.EnableChecksums)
rules.EnableChecksums(clientConfig.Rules.EnableChecksums)
loggers.Load(clientConfig.Server.Loggers, clientConfig.Stats.Workers)
stats.SetLimits(clientConfig.Stats)
stats.SetLoggers(loggers)

return c
}
Expand Down
161 changes: 114 additions & 47 deletions daemon/ui/config_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/evilsocket/opensnitch/daemon/firewall"
"github.com/evilsocket/opensnitch/daemon/log"
"github.com/evilsocket/opensnitch/daemon/procmon"
"github.com/evilsocket/opensnitch/daemon/procmon/monitor"
"github.com/evilsocket/opensnitch/daemon/rule"
"github.com/evilsocket/opensnitch/daemon/ui/config"
Expand Down Expand Up @@ -55,86 +56,152 @@ func (c *Client) loadDiskConfiguration(reload bool) {
return
}

if ok := c.loadConfiguration(raw); ok {
err = c.loadConfiguration(reload, raw)
if err == nil {
if err := c.configWatcher.Add(configFile); err != nil {
log.Error("Could not watch path: %s", err)
return
}
} else {
log.Error("[client] error loading config file: %s", err.Error())
c.SendWarningAlert(err.Error())
}

if reload {
firewall.Reload(
clientConfig.Firewall,
clientConfig.FwOptions.ConfigPath,
clientConfig.FwOptions.MonitorInterval,
)
return
}

go c.monitorConfigWorker()
}

func (c *Client) loadConfiguration(rawConfig []byte) bool {
func (c *Client) loadConfiguration(reload bool, rawConfig []byte) error {
var err error
clientConfig, err = config.Parse(rawConfig)
newConfig, err := config.Parse(rawConfig)
if err != nil {
msg := fmt.Sprintf("Error parsing configuration %s: %s", configFile, err)
log.Error(msg)
c.SendWarningAlert(msg)
return false
return fmt.Errorf("parsing configuration %s: %s", configFile, err)
}

clientConfig.Lock()
defer clientConfig.Unlock()
if err := c.reloadConfiguration(reload, newConfig); err != nil {
return fmt.Errorf("reloading configuration: %s", err.Msg)
}
clientConfig = newConfig
return nil
}

func (c *Client) reloadConfiguration(reload bool, newConfig config.Config) *monitor.Error {

// firstly load config level, to detect further errors if any
if clientConfig.LogLevel != nil {
log.SetLogLevel(int(*clientConfig.LogLevel))
if newConfig.LogLevel != nil {
log.SetLogLevel(int(*newConfig.LogLevel))
}
log.SetLogUTC(clientConfig.LogUTC)
log.SetLogMicro(clientConfig.LogMicro)
if clientConfig.Server.LogFile != "" {
log.SetLogUTC(newConfig.LogUTC)
log.SetLogMicro(newConfig.LogMicro)
if newConfig.Server.LogFile != "" {
log.Debug("[config] using config.server.logfile: %s", newConfig.Server.LogFile)
log.Close()
log.OpenFile(clientConfig.Server.LogFile)
log.OpenFile(newConfig.Server.LogFile)
}

if clientConfig.Server.Address != "" {
tempSocketPath := c.getSocketPath(clientConfig.Server.Address)
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

if newConfig.Server.Address != "" {
tempSocketPath := c.getSocketPath(newConfig.Server.Address)
log.Debug("[config] using config.server.address: %s", newConfig.Server.Address)
if tempSocketPath != c.socketPath {
// disconnect, and let the connection poller reconnect to the new address
c.disconnect()
reconnect = true
}
c.setSocketPath(tempSocketPath)
}
if clientConfig.DefaultAction != "" {
clientDisconnectedRule.Action = rule.Action(clientConfig.DefaultAction)
clientErrorRule.Action = rule.Action(clientConfig.DefaultAction)

if reconnect {
log.Debug("[config] config.server.address.* changed, reconnecting")
c.disconnect()
}

if newConfig.DefaultAction != "" {
clientDisconnectedRule.Action = rule.Action(newConfig.DefaultAction)
clientErrorRule.Action = rule.Action(newConfig.DefaultAction)
// TODO: reconfigure connected rule if changed, but not save it to disk.
//clientConnectedRule.Action = rule.Action(clientConfig.DefaultAction)
}
if clientConfig.DefaultDuration != "" {
clientDisconnectedRule.Duration = rule.Duration(clientConfig.DefaultDuration)
clientErrorRule.Duration = rule.Duration(clientConfig.DefaultDuration)
}
if clientConfig.ProcMonitorMethod != "" {
err := monitor.ReconfigureMonitorMethod(clientConfig.ProcMonitorMethod, clientConfig.Ebpf.ModulesPath)
if err != nil {
msg := fmt.Sprintf("Unable to set new process monitor (%s) method from disk: %v", clientConfig.ProcMonitorMethod, err.Msg)
log.Warning(msg)
c.SendWarningAlert(msg)
}
//clientConnectedRule.Action = rule.Action(newConfig.DefaultAction)
}

if clientConfig.Internal.GCPercent > 0 {
oldgcpercent := debug.SetGCPercent(clientConfig.Internal.GCPercent)
log.Info("GC percent set to %d, previously was %d", clientConfig.Internal.GCPercent, oldgcpercent)
if newConfig.DefaultDuration != "" {
clientDisconnectedRule.Duration = rule.Duration(newConfig.DefaultDuration)
clientErrorRule.Duration = rule.Duration(newConfig.DefaultDuration)
}

c.rules.EnableChecksums(clientConfig.Rules.EnableChecksums)
if newConfig.Internal.GCPercent > 0 && newConfig.Internal.GCPercent != clientConfig.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 {
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)
//loggers.Load(clientConfig.Server.Loggers, clientConfig.Stats.Workers)
//stats.SetLoggers(loggers)
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 {
log.Debug("[config] reloading config.firewall")

firewall.Reload(
newConfig.Firewall,
newConfig.FwOptions.ConfigPath,
newConfig.FwOptions.MonitorInterval,
)
} else {
log.Debug("[config] config.firewall not changed")
}

reloadProc := false
if clientConfig.ProcMonitorMethod == "" ||
newConfig.ProcMonitorMethod != clientConfig.ProcMonitorMethod {
log.Debug("[config] reloading config.ProcMonMethod, old: %s -> new: %s", clientConfig.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 {
log.Debug("[config] reloading config.Ebpf.ModulesPath: %s", newConfig.Ebpf.ModulesPath)
reloadProc = true
} else {
log.Debug("[config] config.Ebpf.ModulesPath not changed")
}
if reloadProc {
monitor.End()
procmon.SetMonitorMethod(newConfig.ProcMonitorMethod)
clientConfig.ProcMonitorMethod = newConfig.ProcMonitorMethod
err := monitor.Init(newConfig.Ebpf.ModulesPath)
if err.What > monitor.NoError {
log.Error("[config] config.procmon error: %s", err.Msg)
procmon.SetMonitorMethod(clientConfig.ProcMonitorMethod)
monitor.Init(clientConfig.Ebpf.ModulesPath)
return err
}
} else {
log.Debug("[config] config.procmon not changed")
}

return true
return nil
}
16 changes: 2 additions & 14 deletions daemon/ui/notifications.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/evilsocket/opensnitch/daemon/firewall"
"github.com/evilsocket/opensnitch/daemon/log"
"github.com/evilsocket/opensnitch/daemon/procmon"
"github.com/evilsocket/opensnitch/daemon/procmon/monitor"
"github.com/evilsocket/opensnitch/daemon/rule"
"github.com/evilsocket/opensnitch/daemon/ui/config"
"github.com/evilsocket/opensnitch/daemon/ui/protocol"
Expand Down Expand Up @@ -109,23 +108,12 @@ func (c *Client) handleActionChangeConfig(stream protocol.UI_NotificationsClient
return
}

if c.GetFirewallType() != newConf.Firewall {
firewall.Reload(
newConf.Firewall,
newConf.FwOptions.ConfigPath,
newConf.FwOptions.MonitorInterval,
)
}

if err := monitor.ReconfigureMonitorMethod(
newConf.ProcMonitorMethod,
clientConfig.Ebpf.ModulesPath,
); err != nil {
if err := c.reloadConfiguration(true, newConf); err != nil {
c.sendNotificationReply(stream, notification.Id, "", err.Msg)
return
}

// this save operation triggers a re-loadConfiguration()
// this save operation triggers a regular re-loadConfiguration()
err = config.Save(configFile, notification.Data)
if err != nil {
log.Warning("[notification] CHANGE_CONFIG not applied %s", err)
Expand Down

0 comments on commit bde5d34

Please sign in to comment.