mirror of
https://github.com/evilsocket/opensnitch.git
synced 2025-03-04 08:34:40 +01:00
improved rules reloading, cli parameters
- When reloading rules from a path: stop existing (domains,ips,regexp) lists monitors, stop rules watcher and start watching the new dir for changes, delete existing rules from memory, etc. - Previously, cli parameters (queue number, log file, etc) were taking into account before loading the configuration. Now the configuration file is loaded first (default-config.json), and if any of the cli parameter has been specified, it'll overwrite the loaded configuration from file. This means for example that if you use "-process-monitor-method proc", and "ebpf" is configured in default-config.json, firstly "ebpf" will be configured, and later "proc". (-queue-num option for now requires to match config option cfg.FwOptions.QueueNumber)
This commit is contained in:
parent
661e3da48f
commit
c0d1da20d2
5 changed files with 109 additions and 60 deletions
|
@ -62,9 +62,9 @@ var (
|
|||
logFile = ""
|
||||
logUTC = true
|
||||
logMicro = false
|
||||
rulesPath = "/etc/opensnitchd/rules/"
|
||||
rulesPath = ""
|
||||
configFile = "/etc/opensnitchd/default-config.json"
|
||||
fwConfigFile = "/etc/opensnitchd/system-fw.json"
|
||||
fwConfigFile = ""
|
||||
ebpfModPath = "" // /usr/lib/opensnitchd/ebpf
|
||||
noLiveReload = false
|
||||
queueNum = 0
|
||||
|
@ -103,7 +103,7 @@ func init() {
|
|||
flag.BoolVar(&showVersion, "version", debug, "Show daemon version of this executable and exit.")
|
||||
flag.BoolVar(&checkRequirements, "check-requirements", debug, "Check system requirements for incompatibilities.")
|
||||
|
||||
flag.StringVar(&procmonMethod, "process-monitor-method", procmonMethod, "How to search for processes path. Options: ftrace, audit (experimental), ebpf (experimental), proc (default)")
|
||||
flag.StringVar(&procmonMethod, "process-monitor-method", procmonMethod, "Options: audit, ebpf, proc (default)")
|
||||
flag.StringVar(&uiSocket, "ui-socket", uiSocket, "Path the UI gRPC service listener (https://github.com/grpc/grpc/blob/master/doc/naming.md).")
|
||||
flag.IntVar(&queueNum, "queue-num", queueNum, "Netfilter queue number.")
|
||||
flag.IntVar(&workers, "workers", workers, "Number of concurrent workers.")
|
||||
|
@ -151,6 +151,20 @@ func overwriteLogging() bool {
|
|||
return debug || warning || important || errorlog || logFile != "" || logMicro
|
||||
}
|
||||
|
||||
// overwriteFw reloads the fw with the configuration file specified via cli.
|
||||
func overwriteFw(cfg *config.Config, qNum uint16, fwCfg string) {
|
||||
firewall.Reload(
|
||||
cfg.Firewall,
|
||||
fwCfg,
|
||||
cfg.FwOptions.MonitorInterval,
|
||||
qNum,
|
||||
)
|
||||
// TODO: Close() closes the daemon if closing the queue timeouts
|
||||
//queue.Close()
|
||||
//repeatQueue.Close()
|
||||
//setupQueues(qNum)
|
||||
}
|
||||
|
||||
func setupQueues(qNum uint16) {
|
||||
// prepare the queue
|
||||
var err error
|
||||
|
@ -557,6 +571,7 @@ func main() {
|
|||
|
||||
setupLogging()
|
||||
setupProfiling()
|
||||
setupSignals()
|
||||
|
||||
log.Important("Starting %s v%s", core.Name, core.Version)
|
||||
|
||||
|
@ -565,55 +580,55 @@ func main() {
|
|||
log.Fatal("%s", err)
|
||||
}
|
||||
|
||||
if err == nil && cfg.Rules.Path != "" {
|
||||
rulesPath = cfg.Rules.Path
|
||||
}
|
||||
if rulesPath == "" {
|
||||
log.Fatal("rules path cannot be empty")
|
||||
}
|
||||
|
||||
rulesPath, err := core.ExpandPath(rulesPath)
|
||||
if err != nil {
|
||||
log.Fatal("Error accessing rules path (does it exist?): %s", err)
|
||||
}
|
||||
|
||||
if cfg.FwOptions.ConfigPath == "" {
|
||||
cfg.FwOptions.ConfigPath = fwConfigFile
|
||||
}
|
||||
log.Info("Using system fw configuration %s ...", fwConfigFile)
|
||||
|
||||
if uint16(queueNum) != cfg.FwOptions.QueueNum && queueNum > 0 {
|
||||
cfg.FwOptions.QueueNum = uint16(queueNum)
|
||||
}
|
||||
|
||||
setupSignals()
|
||||
|
||||
log.Info("Loading rules from %s ...", rulesPath)
|
||||
log.Info("Loading rules from %s ...", cfg.Rules.Path)
|
||||
rules, err = rule.NewLoader(!noLiveReload)
|
||||
if err != nil {
|
||||
log.Fatal("%s", err)
|
||||
} else if err = rules.Load(rulesPath); err != nil {
|
||||
log.Fatal("%s", err)
|
||||
}
|
||||
stats = statistics.New(rules)
|
||||
loggerMgr = loggers.NewLoggerManager()
|
||||
stats.SetLoggers(loggerMgr)
|
||||
uiClient = ui.NewClient(uiSocket, configFile, stats, rules, loggerMgr)
|
||||
|
||||
// default expected queue from the cli is 0. If it's greater than 0
|
||||
// overwrite config value (which by default is also 0)
|
||||
qNum := cfg.FwOptions.QueueNum
|
||||
if uint16(queueNum) != cfg.FwOptions.QueueNum && queueNum > 0 {
|
||||
qNum = uint16(queueNum)
|
||||
}
|
||||
log.Info("Using queue number %d ...", qNum)
|
||||
|
||||
setupWorkers()
|
||||
setupQueues(cfg.FwOptions.QueueNum)
|
||||
setupQueues(qNum)
|
||||
|
||||
// queue and firewall rules should be ready by now
|
||||
|
||||
uiClient.Connect()
|
||||
listenToEvents()
|
||||
|
||||
// overwrite configuration options with the ones specified from the cli
|
||||
|
||||
if overwriteLogging() {
|
||||
setupLogging()
|
||||
}
|
||||
|
||||
if fwConfigFile != "" {
|
||||
log.Info("Reloading fw rules from %s, queue %d ...", fwConfigFile, qNum)
|
||||
overwriteFw(cfg, qNum, fwConfigFile)
|
||||
}
|
||||
log.Info("Using system fw configuration %s ...", fwConfigFile)
|
||||
|
||||
if rulesPath != "" {
|
||||
log.Info("Reloading rules from %s ...", rulesPath)
|
||||
if err := rules.Reload(rulesPath); err != nil {
|
||||
log.Fatal("Error loading rules path %s", rulesPath)
|
||||
}
|
||||
}
|
||||
|
||||
// overwrite monitor method from configuration if the user has passed
|
||||
// the option via command line.
|
||||
if procmonMethod != "" {
|
||||
if procmonMethod != "" || (ebpfModPath != "" && ebpfModPath != cfg.Ebpf.ModulesPath) {
|
||||
log.Info("Reloading proc monitor (%s) (ebpf mods path: %s)...", procmonMethod, cfg.Ebpf.ModulesPath)
|
||||
if err := monitor.ReconfigureMonitorMethod(procmonMethod, cfg.Ebpf); err != nil {
|
||||
msg := fmt.Sprintf("Unable to set process monitor method via parameter: %v", err)
|
||||
uiClient.SendWarningAlert(msg)
|
||||
|
|
|
@ -25,11 +25,12 @@ import (
|
|||
type Loader struct {
|
||||
watcher *fsnotify.Watcher
|
||||
rules map[string]*Rule
|
||||
path string
|
||||
rulesKeys []string
|
||||
Path string
|
||||
liveReload bool
|
||||
liveReloadRunning bool
|
||||
checkSums bool
|
||||
stopLiveReload chan struct{}
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
@ -42,11 +43,12 @@ func NewLoader(liveReload bool) (*Loader, error) {
|
|||
return nil, err
|
||||
}
|
||||
return &Loader{
|
||||
path: "",
|
||||
Path: "",
|
||||
rules: make(map[string]*Rule),
|
||||
liveReload: liveReload,
|
||||
watcher: watcher,
|
||||
liveReloadRunning: false,
|
||||
stopLiveReload: make(chan struct{}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -86,13 +88,35 @@ 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 {
|
||||
log.Info("rules.Loader.Reload(): %s", path)
|
||||
|
||||
// check that the new path exists before reloading
|
||||
if core.Exists(path) == false {
|
||||
return fmt.Errorf("The new path '%s' does not exist", path)
|
||||
}
|
||||
|
||||
// stop monitors
|
||||
if l.liveReloadRunning {
|
||||
l.stopLiveReload <- struct{}{}
|
||||
}
|
||||
if l.watcher != nil {
|
||||
l.watcher.Remove(l.Path)
|
||||
}
|
||||
for _, r := range l.rules {
|
||||
l.cleanListsRule(r)
|
||||
}
|
||||
|
||||
// then delete the rules, and reload everything
|
||||
l.Lock()
|
||||
l.rulesKeys = make([]string, 0)
|
||||
l.rules = make(map[string]*Rule)
|
||||
l.Unlock()
|
||||
return l.Load(path)
|
||||
}
|
||||
|
||||
// Load loads rules files from disk.
|
||||
func (l *Loader) Load(path string) error {
|
||||
log.Debug("rules.Loader.Load(): %s", path)
|
||||
if core.Exists(path) == false {
|
||||
return fmt.Errorf("Path '%s' does not exist\nCreate it if you want to save rules to disk", path)
|
||||
}
|
||||
|
@ -107,7 +131,7 @@ func (l *Loader) Load(path string) error {
|
|||
return fmt.Errorf("Error globbing '%s': %s", expr, err)
|
||||
}
|
||||
|
||||
l.path = path
|
||||
l.Path = path
|
||||
if len(l.rules) == 0 {
|
||||
l.rules = make(map[string]*Rule)
|
||||
}
|
||||
|
@ -132,7 +156,7 @@ func (l *Loader) Load(path string) error {
|
|||
func (l *Loader) Add(rule *Rule, saveToDisk bool) error {
|
||||
l.addUserRule(rule)
|
||||
if saveToDisk {
|
||||
fileName := filepath.Join(l.path, fmt.Sprintf("%s.json", rule.Name))
|
||||
fileName := filepath.Join(l.Path, fmt.Sprintf("%s.json", rule.Name))
|
||||
return l.Save(rule, fileName)
|
||||
}
|
||||
return nil
|
||||
|
@ -147,7 +171,7 @@ func (l *Loader) Replace(rule *Rule, saveToDisk bool) error {
|
|||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
fileName := filepath.Join(l.path, fmt.Sprintf("%s.json", rule.Name))
|
||||
fileName := filepath.Join(l.Path, fmt.Sprintf("%s.json", rule.Name))
|
||||
return l.Save(rule, fileName)
|
||||
}
|
||||
return nil
|
||||
|
@ -263,7 +287,7 @@ func (l *Loader) deleteRule(filePath string) {
|
|||
}
|
||||
|
||||
func (l *Loader) deleteRuleFromDisk(ruleName string) error {
|
||||
path := fmt.Sprint(l.path, "/", ruleName, ".json")
|
||||
path := fmt.Sprint(l.Path, "/", ruleName, ".json")
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
||||
|
@ -277,7 +301,8 @@ func (l *Loader) deleteOldRuleFromDisk(oldRule, newRule *Rule) {
|
|||
}
|
||||
}
|
||||
|
||||
// cleanListsRule erases the list of domains of an Operator of type Lists
|
||||
// cleanListsRule erases the lists loaded of an Operator of type Lists,
|
||||
// and stops the workers monitoring the lists.
|
||||
func (l *Loader) cleanListsRule(oldRule *Rule) {
|
||||
if oldRule.Operator.Type == Lists {
|
||||
oldRule.Operator.StopMonitoringLists()
|
||||
|
@ -412,8 +437,8 @@ func (l *Loader) scheduleTemporaryRule(rule Rule) error {
|
|||
func (l *Loader) liveReloadWorker() {
|
||||
l.liveReloadRunning = true
|
||||
|
||||
log.Debug("Rules watcher started on path %s ...", l.path)
|
||||
if err := l.watcher.Add(l.path); err != nil {
|
||||
log.Debug("Rules watcher started on path %s ...", l.Path)
|
||||
if err := l.watcher.Add(l.Path); err != nil {
|
||||
log.Error("Could not watch path: %s", err)
|
||||
l.liveReloadRunning = false
|
||||
return
|
||||
|
@ -421,6 +446,8 @@ func (l *Loader) liveReloadWorker() {
|
|||
|
||||
for {
|
||||
select {
|
||||
case <-l.stopLiveReload:
|
||||
goto Exit
|
||||
case event := <-l.watcher.Events:
|
||||
// a new rule json file has been created or updated
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
|
@ -442,6 +469,9 @@ func (l *Loader) liveReloadWorker() {
|
|||
log.Error("File system watcher error: %s", err)
|
||||
}
|
||||
}
|
||||
Exit:
|
||||
log.Debug("[rules] liveReloadWorker() exited")
|
||||
l.liveReloadRunning = false
|
||||
}
|
||||
|
||||
// FindFirstMatch will try match the connection against the existing rule set.
|
||||
|
|
|
@ -80,7 +80,7 @@ func (o *Operator) monitorLists() {
|
|||
Exit:
|
||||
modTimes = nil
|
||||
o.ClearLists()
|
||||
log.Info("lists monitor stopped")
|
||||
log.Info("lists monitor stopped: %s", o.Data)
|
||||
}
|
||||
|
||||
// ClearLists deletes all the entries of a list
|
||||
|
|
|
@ -76,6 +76,7 @@ func NewClient(socketPath, localConfigFile string, stats *statistics.Statistics,
|
|||
isConnected: make(chan bool),
|
||||
alertsChan: make(chan protocol.Alert, maxQueuedAlerts),
|
||||
}
|
||||
c.config.Rules.Path = rules.Path
|
||||
//for i := 0; i < 4; i++ {
|
||||
go c.alertsDispatcher()
|
||||
|
||||
|
|
|
@ -157,32 +157,16 @@ func (c *Client) reloadConfiguration(reload bool, newConfig config.Config) *moni
|
|||
log.Debug("[config] config.internal.gcpercent not changed")
|
||||
}
|
||||
|
||||
// 1. load rules
|
||||
c.rules.EnableChecksums(newConfig.Rules.EnableChecksums)
|
||||
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)
|
||||
log.Debug("[config] reloading config.rules.path, old: <%s> new: <%s>", c.config.Rules.Path, newConfig.Rules.Path)
|
||||
} else {
|
||||
log.Debug("[config] config.rules.path not changed")
|
||||
}
|
||||
|
||||
reloadFw := false
|
||||
if c.GetFirewallType() != newConfig.Firewall ||
|
||||
newConfig.FwOptions.ConfigPath != c.config.FwOptions.ConfigPath ||
|
||||
newConfig.FwOptions.QueueNum != c.config.FwOptions.QueueNum ||
|
||||
newConfig.FwOptions.MonitorInterval != c.config.FwOptions.MonitorInterval {
|
||||
log.Debug("[config] reloading config.firewall")
|
||||
reloadFw = true
|
||||
|
||||
firewall.Reload(
|
||||
newConfig.Firewall,
|
||||
newConfig.FwOptions.ConfigPath,
|
||||
newConfig.FwOptions.MonitorInterval,
|
||||
newConfig.FwOptions.QueueNum,
|
||||
)
|
||||
} else {
|
||||
log.Debug("[config] config.firewall not changed")
|
||||
}
|
||||
|
||||
// 2. load proc mon method
|
||||
reloadProc := false
|
||||
if c.config.ProcMonitorMethod == "" ||
|
||||
newConfig.ProcMonitorMethod != c.config.ProcMonitorMethod {
|
||||
|
@ -208,6 +192,25 @@ func (c *Client) reloadConfiguration(reload bool, newConfig config.Config) *moni
|
|||
log.Debug("[config] config.procmon not changed")
|
||||
}
|
||||
|
||||
// 3. load fw
|
||||
reloadFw := false
|
||||
if c.GetFirewallType() != newConfig.Firewall ||
|
||||
newConfig.FwOptions.ConfigPath != c.config.FwOptions.ConfigPath ||
|
||||
newConfig.FwOptions.QueueNum != c.config.FwOptions.QueueNum ||
|
||||
newConfig.FwOptions.MonitorInterval != c.config.FwOptions.MonitorInterval {
|
||||
log.Debug("[config] reloading config.firewall")
|
||||
reloadFw = true
|
||||
|
||||
firewall.Reload(
|
||||
newConfig.Firewall,
|
||||
newConfig.FwOptions.ConfigPath,
|
||||
newConfig.FwOptions.MonitorInterval,
|
||||
newConfig.FwOptions.QueueNum,
|
||||
)
|
||||
} else {
|
||||
log.Debug("[config] config.firewall not changed")
|
||||
}
|
||||
|
||||
if (reloadProc || reloadFw) && newConfig.Internal.FlushConnsOnStart {
|
||||
log.Debug("[config] flushing established connections")
|
||||
netlink.FlushConnections()
|
||||
|
|
Loading…
Add table
Reference in a new issue