diff --git a/daemon/main.go b/daemon/main.go index 358777b46d..22c11f21ed 100644 --- a/daemon/main.go +++ b/daemon/main.go @@ -50,6 +50,7 @@ import ( "github.com/evilsocket/opensnitch/daemon/rule" "github.com/evilsocket/opensnitch/daemon/statistics" "github.com/evilsocket/opensnitch/daemon/ui" + "github.com/evilsocket/opensnitch/daemon/ui/config" "github.com/evilsocket/opensnitch/daemon/ui/protocol" ) @@ -60,7 +61,8 @@ var ( logFile = "" logUTC = true logMicro = false - rulesPath = "rules" + rulesPath = "/etc/opensnitchd/rules/" + configFile = "/etc/opensnitchd/default-config.json" noLiveReload = false queueNum = 0 repeatQueueNum int //will be set later to queueNum + 1 @@ -102,6 +104,7 @@ func init() { flag.IntVar(&workers, "workers", workers, "Number of concurrent workers.") flag.BoolVar(&noLiveReload, "no-live-reload", debug, "Disable rules live reloading.") + flag.StringVar(&configFile, "config-file", configFile, "Path to the daemon configuration file.") flag.StringVar(&logFile, "log-file", logFile, "Write logs to this file instead of the standard output.") flag.BoolVar(&logUTC, "log-utc", logUTC, "Write logs output with UTC timezone (enabled by default).") flag.BoolVar(&logMicro, "log-micro", logMicro, "Write logs output with microsecond timestamp (disabled by default).") @@ -114,6 +117,27 @@ func init() { flag.StringVar(&memProfile, "mem-profile", memProfile, "Write memory profile to this file.") } +// Load configuration file from disk, by default from /etc/opensnitchd/default-config.json, +// or from the path specified by configFile. +// This configuration will be loaded again by uiClient(), in order to monitor it for changes. +func loadDiskConfiguration() (*config.Config, error) { + if configFile == "" { + return nil, fmt.Errorf("Configuration file cannot be empty") + } + + raw, err := config.Load(configFile) + if err != nil || len(raw) == 0 { + return nil, fmt.Errorf("Error loading configuration %s: %s", configFile, err) + } + clientConfig, err := config.Parse(raw) + if err != nil { + return nil, fmt.Errorf("Error parsing configuration %s: %s", configFile, err) + } + + log.Info("Loading configuration file %s ...", configFile) + return &clientConfig, nil +} + func overwriteLogging() bool { return debug || warning || important || errorlog || logFile != "" || logMicro } @@ -482,6 +506,17 @@ func main() { log.Important("Starting %s v%s", core.Name, core.Version) + cfg, err := loadDiskConfiguration() + if err != nil { + 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) @@ -490,14 +525,15 @@ func main() { setupSignals() log.Info("Loading rules from %s ...", rulesPath) - if rules, err = rule.NewLoader(!noLiveReload); err != nil { + 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() - uiClient = ui.NewClient(uiSocket, stats, rules, loggerMgr) + uiClient = ui.NewClient(uiSocket, configFile, stats, rules, loggerMgr) // prepare the queue setupWorkers() diff --git a/daemon/rule/loader.go b/daemon/rule/loader.go index 5071a88dce..c07b660e4c 100644 --- a/daemon/rule/loader.go +++ b/daemon/rule/loader.go @@ -64,11 +64,11 @@ func (l *Loader) GetAll() map[string]*Rule { // Load loads rules files from disk. func (l *Loader) Load(path string) error { if core.Exists(path) == false { - return fmt.Errorf("Path '%s' does not exist\nCreate it in if you want to save rules to disk", path) + return fmt.Errorf("Path '%s' does not exist\nCreate it if you want to save rules to disk", path) } path, err := core.ExpandPath(path) if err != nil { - return fmt.Errorf("Error accessing rules path: %s.\nCreate it in if you want to save rules to disk", err) + return fmt.Errorf("Error accessing rules path: %s.\nCreate it if you want to save rules to disk", err) } expr := filepath.Join(path, "*.json") diff --git a/daemon/ui/client.go b/daemon/ui/client.go index 541cc0d777..2de0e4b4f3 100644 --- a/daemon/ui/client.go +++ b/daemon/ui/client.go @@ -59,7 +59,10 @@ type Client struct { } // NewClient creates and configures a new client. -func NewClient(socketPath string, stats *statistics.Statistics, rules *rule.Loader, loggers *loggers.LoggerManager) *Client { +func NewClient(socketPath, localConfigFile string, stats *statistics.Statistics, rules *rule.Loader, loggers *loggers.LoggerManager) *Client { + if localConfigFile != "" { + configFile = localConfigFile + } c := &Client{ stats: stats, rules: rules, diff --git a/daemon/ui/config/config.go b/daemon/ui/config/config.go index 23d30fa323..4638ce65e2 100644 --- a/daemon/ui/config/config.go +++ b/daemon/ui/config/config.go @@ -44,19 +44,25 @@ type serverConfig struct { Loggers []loggers.LoggerConfig `json:"Loggers"` } +type rulesOptions struct { + Path string `json:"Path"` + EnableChecksums bool `json:"EnableChecksums"` +} + // Config holds the values loaded from configFile type Config struct { sync.RWMutex Server serverConfig `json:"Server"` + Stats statistics.StatsConfig `json:"Stats"` + Rules rulesOptions `json:"Rules"` DefaultAction string `json:"DefaultAction"` DefaultDuration string `json:"DefaultDuration"` - InterceptUnknown bool `json:"InterceptUnknown"` ProcMonitorMethod string `json:"ProcMonitorMethod"` + Firewall string `json:"Firewall"` LogLevel *uint32 `json:"LogLevel"` + InterceptUnknown bool `json:"InterceptUnknown"` LogUTC bool `json:"LogUTC"` LogMicro bool `json:"LogMicro"` - Firewall string `json:"Firewall"` - Stats statistics.StatsConfig `json:"Stats"` } // Parse determines if the given configuration is ok.