Skip to content

Commit bde5d34

Browse files
reload more config options without restarting the daemon
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.
1 parent f5f30b1 commit bde5d34

File tree

6 files changed

+149
-69
lines changed

6 files changed

+149
-69
lines changed

daemon/log/loggers/logger.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package loggers
22

3-
import "fmt"
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/evilsocket/opensnitch/daemon/log"
8+
)
49

510
const logTag = "opensnitch"
611

@@ -31,14 +36,19 @@ type LoggerConfig struct {
3136

3237
// LoggerManager represents the LoggerManager.
3338
type LoggerManager struct {
39+
ctx context.Context
40+
cancel context.CancelFunc
3441
loggers map[string]Logger
3542
msgs chan []interface{}
3643
count int
3744
}
3845

3946
// NewLoggerManager instantiates all the configured loggers.
4047
func NewLoggerManager() *LoggerManager {
48+
ctx, cancel := context.WithCancel(context.Background())
4149
lm := &LoggerManager{
50+
ctx: ctx,
51+
cancel: cancel,
4252
loggers: make(map[string]Logger),
4353
}
4454

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

8292
}
8393

94+
func (l *LoggerManager) Stop() {
95+
l.cancel()
96+
l.count = 0
97+
l.loggers = make(map[string]Logger)
98+
}
99+
84100
func (l *LoggerManager) write(args ...interface{}) {
85101
for _, logger := range l.loggers {
86102
logger.Write(logger.Transform(args...))
@@ -89,10 +105,15 @@ func (l *LoggerManager) write(args ...interface{}) {
89105

90106
func newWorker(id int, l *LoggerManager) {
91107
for {
92-
for msg := range l.msgs {
108+
select {
109+
case <-l.ctx.Done():
110+
goto Exit
111+
case msg := <-l.msgs:
93112
l.write(msg)
94113
}
95114
}
115+
Exit:
116+
log.Debug("logger worker %d exited", id)
96117
}
97118

98119
// Log sends data to the loggers.

daemon/procmon/monitor/init.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,6 @@ func stopProcMonitors() {
5151

5252
// ReconfigureMonitorMethod configures a new method for parsing connections.
5353
func ReconfigureMonitorMethod(newMonitorMethod, ebpfModulesPath string) *Error {
54-
if procmon.GetMonitorMethod() == newMonitorMethod {
55-
return nil
56-
}
57-
5854
oldMethod := procmon.GetMonitorMethod()
5955
if oldMethod == "" {
6056
oldMethod = procmon.MethodProc

daemon/rule/loader.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ func (l *Loader) HasChecksums(op Operand) {
8383
}
8484
}
8585

86+
// Reload loads rules from the specified path, deleting existing loaded
87+
// rules from memory.
88+
func (l *Loader) Reload(path string) error {
89+
l.rulesKeys = make([]string, 0)
90+
l.rules = make(map[string]*Rule)
91+
return l.Load(path)
92+
}
93+
8694
// Load loads rules files from disk.
8795
func (l *Loader) Load(path string) error {
8896
if core.Exists(path) == false {

daemon/ui/client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type Client struct {
4343
clientCtx context.Context
4444
clientCancel context.CancelFunc
4545

46+
loggers *loggers.LoggerManager
4647
stats *statistics.Statistics
4748
rules *rule.Loader
4849
con *grpc.ClientConn
@@ -67,6 +68,7 @@ func NewClient(socketPath, localConfigFile string, stats *statistics.Statistics,
6768
configFile = localConfigFile
6869
}
6970
c := &Client{
71+
loggers: loggers,
7072
stats: stats,
7173
rules: rules,
7274
isUnixSocket: false,
@@ -88,9 +90,7 @@ func NewClient(socketPath, localConfigFile string, stats *statistics.Statistics,
8890
}
8991
procmon.EventsCache.SetComputeChecksums(clientConfig.Rules.EnableChecksums)
9092
rules.EnableChecksums(clientConfig.Rules.EnableChecksums)
91-
loggers.Load(clientConfig.Server.Loggers, clientConfig.Stats.Workers)
9293
stats.SetLimits(clientConfig.Stats)
93-
stats.SetLoggers(loggers)
9494

9595
return c
9696
}

daemon/ui/config_utils.go

Lines changed: 114 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/evilsocket/opensnitch/daemon/firewall"
1010
"github.com/evilsocket/opensnitch/daemon/log"
11+
"github.com/evilsocket/opensnitch/daemon/procmon"
1112
"github.com/evilsocket/opensnitch/daemon/procmon/monitor"
1213
"github.com/evilsocket/opensnitch/daemon/rule"
1314
"github.com/evilsocket/opensnitch/daemon/ui/config"
@@ -55,86 +56,152 @@ func (c *Client) loadDiskConfiguration(reload bool) {
5556
return
5657
}
5758

58-
if ok := c.loadConfiguration(raw); ok {
59+
err = c.loadConfiguration(reload, raw)
60+
if err == nil {
5961
if err := c.configWatcher.Add(configFile); err != nil {
6062
log.Error("Could not watch path: %s", err)
6163
return
6264
}
65+
} else {
66+
log.Error("[client] error loading config file: %s", err.Error())
67+
c.SendWarningAlert(err.Error())
6368
}
6469

6570
if reload {
66-
firewall.Reload(
67-
clientConfig.Firewall,
68-
clientConfig.FwOptions.ConfigPath,
69-
clientConfig.FwOptions.MonitorInterval,
70-
)
7171
return
7272
}
73-
7473
go c.monitorConfigWorker()
7574
}
7675

77-
func (c *Client) loadConfiguration(rawConfig []byte) bool {
76+
func (c *Client) loadConfiguration(reload bool, rawConfig []byte) error {
7877
var err error
79-
clientConfig, err = config.Parse(rawConfig)
78+
newConfig, err := config.Parse(rawConfig)
8079
if err != nil {
81-
msg := fmt.Sprintf("Error parsing configuration %s: %s", configFile, err)
82-
log.Error(msg)
83-
c.SendWarningAlert(msg)
84-
return false
80+
return fmt.Errorf("parsing configuration %s: %s", configFile, err)
8581
}
8682

87-
clientConfig.Lock()
88-
defer clientConfig.Unlock()
83+
if err := c.reloadConfiguration(reload, newConfig); err != nil {
84+
return fmt.Errorf("reloading configuration: %s", err.Msg)
85+
}
86+
clientConfig = newConfig
87+
return nil
88+
}
89+
90+
func (c *Client) reloadConfiguration(reload bool, newConfig config.Config) *monitor.Error {
8991

9092
// firstly load config level, to detect further errors if any
91-
if clientConfig.LogLevel != nil {
92-
log.SetLogLevel(int(*clientConfig.LogLevel))
93+
if newConfig.LogLevel != nil {
94+
log.SetLogLevel(int(*newConfig.LogLevel))
9395
}
94-
log.SetLogUTC(clientConfig.LogUTC)
95-
log.SetLogMicro(clientConfig.LogMicro)
96-
if clientConfig.Server.LogFile != "" {
96+
log.SetLogUTC(newConfig.LogUTC)
97+
log.SetLogMicro(newConfig.LogMicro)
98+
if newConfig.Server.LogFile != "" {
99+
log.Debug("[config] using config.server.logfile: %s", newConfig.Server.LogFile)
97100
log.Close()
98-
log.OpenFile(clientConfig.Server.LogFile)
101+
log.OpenFile(newConfig.Server.LogFile)
99102
}
100103

101-
if clientConfig.Server.Address != "" {
102-
tempSocketPath := c.getSocketPath(clientConfig.Server.Address)
104+
reconnect := newConfig.Server.Authentication.Type != clientConfig.Server.Authentication.Type ||
105+
newConfig.Server.Authentication.TLSOptions.CACert != clientConfig.Server.Authentication.TLSOptions.CACert ||
106+
newConfig.Server.Authentication.TLSOptions.ServerCert != clientConfig.Server.Authentication.TLSOptions.ServerCert ||
107+
newConfig.Server.Authentication.TLSOptions.ServerKey != clientConfig.Server.Authentication.TLSOptions.ServerKey ||
108+
newConfig.Server.Authentication.TLSOptions.ClientCert != clientConfig.Server.Authentication.TLSOptions.ClientCert ||
109+
newConfig.Server.Authentication.TLSOptions.ClientKey != clientConfig.Server.Authentication.TLSOptions.ClientKey ||
110+
newConfig.Server.Authentication.TLSOptions.ClientAuthType != clientConfig.Server.Authentication.TLSOptions.ClientAuthType ||
111+
newConfig.Server.Authentication.TLSOptions.SkipVerify != clientConfig.Server.Authentication.TLSOptions.SkipVerify
112+
113+
if newConfig.Server.Address != "" {
114+
tempSocketPath := c.getSocketPath(newConfig.Server.Address)
115+
log.Debug("[config] using config.server.address: %s", newConfig.Server.Address)
103116
if tempSocketPath != c.socketPath {
104117
// disconnect, and let the connection poller reconnect to the new address
105-
c.disconnect()
118+
reconnect = true
106119
}
107120
c.setSocketPath(tempSocketPath)
108121
}
109-
if clientConfig.DefaultAction != "" {
110-
clientDisconnectedRule.Action = rule.Action(clientConfig.DefaultAction)
111-
clientErrorRule.Action = rule.Action(clientConfig.DefaultAction)
122+
123+
if reconnect {
124+
log.Debug("[config] config.server.address.* changed, reconnecting")
125+
c.disconnect()
126+
}
127+
128+
if newConfig.DefaultAction != "" {
129+
clientDisconnectedRule.Action = rule.Action(newConfig.DefaultAction)
130+
clientErrorRule.Action = rule.Action(newConfig.DefaultAction)
112131
// TODO: reconfigure connected rule if changed, but not save it to disk.
113-
//clientConnectedRule.Action = rule.Action(clientConfig.DefaultAction)
114-
}
115-
if clientConfig.DefaultDuration != "" {
116-
clientDisconnectedRule.Duration = rule.Duration(clientConfig.DefaultDuration)
117-
clientErrorRule.Duration = rule.Duration(clientConfig.DefaultDuration)
118-
}
119-
if clientConfig.ProcMonitorMethod != "" {
120-
err := monitor.ReconfigureMonitorMethod(clientConfig.ProcMonitorMethod, clientConfig.Ebpf.ModulesPath)
121-
if err != nil {
122-
msg := fmt.Sprintf("Unable to set new process monitor (%s) method from disk: %v", clientConfig.ProcMonitorMethod, err.Msg)
123-
log.Warning(msg)
124-
c.SendWarningAlert(msg)
125-
}
132+
//clientConnectedRule.Action = rule.Action(newConfig.DefaultAction)
126133
}
127134

128-
if clientConfig.Internal.GCPercent > 0 {
129-
oldgcpercent := debug.SetGCPercent(clientConfig.Internal.GCPercent)
130-
log.Info("GC percent set to %d, previously was %d", clientConfig.Internal.GCPercent, oldgcpercent)
135+
if newConfig.DefaultDuration != "" {
136+
clientDisconnectedRule.Duration = rule.Duration(newConfig.DefaultDuration)
137+
clientErrorRule.Duration = rule.Duration(newConfig.DefaultDuration)
131138
}
132139

133-
c.rules.EnableChecksums(clientConfig.Rules.EnableChecksums)
140+
if newConfig.Internal.GCPercent > 0 && newConfig.Internal.GCPercent != clientConfig.Internal.GCPercent {
141+
oldgcpercent := debug.SetGCPercent(newConfig.Internal.GCPercent)
142+
log.Debug("[config] GC percent set to %d, previously was %d", newConfig.Internal.GCPercent, oldgcpercent)
143+
} else {
144+
log.Debug("[config] config.internal.gcpercent not changed")
145+
}
146+
147+
c.rules.EnableChecksums(newConfig.Rules.EnableChecksums)
148+
if clientConfig.Rules.Path != newConfig.Rules.Path {
149+
c.rules.Reload(newConfig.Rules.Path)
150+
log.Debug("[config] reloading config.rules.path: %s", newConfig.Rules.Path)
151+
} else {
152+
log.Debug("[config] config.rules.path not changed")
153+
}
134154
// TODO:
135155
//c.stats.SetLimits(clientConfig.Stats)
136-
//loggers.Load(clientConfig.Server.Loggers, clientConfig.Stats.Workers)
137-
//stats.SetLoggers(loggers)
156+
if reload {
157+
c.loggers.Stop()
158+
}
159+
c.loggers.Load(clientConfig.Server.Loggers, clientConfig.Stats.Workers)
160+
c.stats.SetLoggers(c.loggers)
161+
162+
if reload && c.GetFirewallType() != newConfig.Firewall ||
163+
newConfig.FwOptions.ConfigPath != clientConfig.FwOptions.ConfigPath ||
164+
newConfig.FwOptions.MonitorInterval != clientConfig.FwOptions.MonitorInterval {
165+
log.Debug("[config] reloading config.firewall")
166+
167+
firewall.Reload(
168+
newConfig.Firewall,
169+
newConfig.FwOptions.ConfigPath,
170+
newConfig.FwOptions.MonitorInterval,
171+
)
172+
} else {
173+
log.Debug("[config] config.firewall not changed")
174+
}
175+
176+
reloadProc := false
177+
if clientConfig.ProcMonitorMethod == "" ||
178+
newConfig.ProcMonitorMethod != clientConfig.ProcMonitorMethod {
179+
log.Debug("[config] reloading config.ProcMonMethod, old: %s -> new: %s", clientConfig.ProcMonitorMethod, newConfig.ProcMonitorMethod)
180+
reloadProc = true
181+
} else {
182+
log.Debug("[config] config.ProcMonMethod not changed")
183+
}
184+
185+
if reload && procmon.MethodIsEbpf() && newConfig.Ebpf.ModulesPath != "" && clientConfig.Ebpf.ModulesPath != newConfig.Ebpf.ModulesPath {
186+
log.Debug("[config] reloading config.Ebpf.ModulesPath: %s", newConfig.Ebpf.ModulesPath)
187+
reloadProc = true
188+
} else {
189+
log.Debug("[config] config.Ebpf.ModulesPath not changed")
190+
}
191+
if reloadProc {
192+
monitor.End()
193+
procmon.SetMonitorMethod(newConfig.ProcMonitorMethod)
194+
clientConfig.ProcMonitorMethod = newConfig.ProcMonitorMethod
195+
err := monitor.Init(newConfig.Ebpf.ModulesPath)
196+
if err.What > monitor.NoError {
197+
log.Error("[config] config.procmon error: %s", err.Msg)
198+
procmon.SetMonitorMethod(clientConfig.ProcMonitorMethod)
199+
monitor.Init(clientConfig.Ebpf.ModulesPath)
200+
return err
201+
}
202+
} else {
203+
log.Debug("[config] config.procmon not changed")
204+
}
138205

139-
return true
206+
return nil
140207
}

daemon/ui/notifications.go

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"github.com/evilsocket/opensnitch/daemon/firewall"
1414
"github.com/evilsocket/opensnitch/daemon/log"
1515
"github.com/evilsocket/opensnitch/daemon/procmon"
16-
"github.com/evilsocket/opensnitch/daemon/procmon/monitor"
1716
"github.com/evilsocket/opensnitch/daemon/rule"
1817
"github.com/evilsocket/opensnitch/daemon/ui/config"
1918
"github.com/evilsocket/opensnitch/daemon/ui/protocol"
@@ -109,23 +108,12 @@ func (c *Client) handleActionChangeConfig(stream protocol.UI_NotificationsClient
109108
return
110109
}
111110

112-
if c.GetFirewallType() != newConf.Firewall {
113-
firewall.Reload(
114-
newConf.Firewall,
115-
newConf.FwOptions.ConfigPath,
116-
newConf.FwOptions.MonitorInterval,
117-
)
118-
}
119-
120-
if err := monitor.ReconfigureMonitorMethod(
121-
newConf.ProcMonitorMethod,
122-
clientConfig.Ebpf.ModulesPath,
123-
); err != nil {
111+
if err := c.reloadConfiguration(true, newConf); err != nil {
124112
c.sendNotificationReply(stream, notification.Id, "", err.Msg)
125113
return
126114
}
127115

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

0 commit comments

Comments
 (0)