mirror of
https://github.com/evilsocket/opensnitch.git
synced 2025-03-04 00:24:40 +01:00
fw: allow to configure config file/ check interval
- Allow to configure system firewall configuration file path: * via cli (-fw-config-file). * via global configuration file. - Allow to configure fw rules check interval. The system fw config file contains regular iptables/nftables rules. Previously it was hardcoded to /etc/opensnitchd/system-fw.json The interval to check if the interception rules were added was also hardcoded to 10 seconds. Now it's possible to configure it. A value of "0s" disables the interval, while "" defaults to 10 seconds.
This commit is contained in:
parent
e905163a03
commit
54ac5a3549
10 changed files with 96 additions and 39 deletions
|
@ -16,6 +16,9 @@ var (
|
|||
RestoreChains = true
|
||||
BackupChains = true
|
||||
ReloadConf = true
|
||||
|
||||
DefaultCheckInterval = 10 * time.Second
|
||||
RulesCheckerDisabled = "0s"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -25,13 +28,14 @@ type (
|
|||
// Common holds common fields and functionality of both firewalls,
|
||||
// iptables and nftables.
|
||||
Common struct {
|
||||
RulesChecker *time.Ticker
|
||||
ErrChan chan string
|
||||
QueueNum uint16
|
||||
stopChecker chan bool
|
||||
Running bool
|
||||
Intercepting bool
|
||||
FwEnabled bool
|
||||
RulesChecker *time.Ticker
|
||||
RulesCheckInterval time.Duration
|
||||
ErrChan chan string
|
||||
stopChecker chan bool
|
||||
QueueNum uint16
|
||||
Running bool
|
||||
Intercepting bool
|
||||
FwEnabled bool
|
||||
sync.RWMutex
|
||||
}
|
||||
)
|
||||
|
@ -67,6 +71,17 @@ func (c *Common) SendError(err string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Common) SetRulesCheckerInterval(interval string) {
|
||||
dur, err := time.ParseDuration(interval)
|
||||
if err != nil {
|
||||
log.Warning("Invalid rules checker interval (falling back to %s): %s", DefaultCheckInterval, err)
|
||||
c.RulesCheckInterval = DefaultCheckInterval
|
||||
return
|
||||
}
|
||||
|
||||
c.RulesCheckInterval = dur
|
||||
}
|
||||
|
||||
// SetQueueNum sets the queue number used by the firewall.
|
||||
// It's the queue where all intercepted connections will be sent.
|
||||
func (c *Common) SetQueueNum(qNum *int) {
|
||||
|
@ -109,6 +124,10 @@ func (c *Common) IsIntercepting() bool {
|
|||
func (c *Common) NewRulesChecker(areRulesLoaded callbackBool, reloadRules callback) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.RulesCheckInterval.String() == RulesCheckerDisabled {
|
||||
log.Info("Fw rules checker disabled ...")
|
||||
return
|
||||
}
|
||||
|
||||
if c.RulesChecker != nil {
|
||||
c.RulesChecker.Stop()
|
||||
|
@ -119,7 +138,8 @@ func (c *Common) NewRulesChecker(areRulesLoaded callbackBool, reloadRules callba
|
|||
}
|
||||
}
|
||||
c.stopChecker = make(chan bool, 1)
|
||||
c.RulesChecker = time.NewTicker(time.Second * 10)
|
||||
log.Info("Starting new fw checker every %s ...", DefaultCheckInterval)
|
||||
c.RulesChecker = time.NewTicker(c.RulesCheckInterval)
|
||||
|
||||
go startCheckingRules(c.stopChecker, c.RulesChecker, areRulesLoaded, reloadRules)
|
||||
}
|
||||
|
|
|
@ -118,10 +118,10 @@ type SystemConfig struct {
|
|||
// This is the configuration to manage the system firewall (iptables, nftables).
|
||||
type Config struct {
|
||||
sync.Mutex
|
||||
file string
|
||||
watcher *fsnotify.Watcher
|
||||
monitorExitChan chan bool
|
||||
SysConfig SystemConfig
|
||||
monitorExitChan chan bool
|
||||
file string
|
||||
|
||||
// preloadCallback is called before reloading the configuration,
|
||||
// in order to delete old fw rules.
|
||||
|
@ -132,7 +132,7 @@ type Config struct {
|
|||
}
|
||||
|
||||
// NewSystemFwConfig initializes config fields
|
||||
func (c *Config) NewSystemFwConfig(preLoadCb, reLoadCb func()) (*Config, error) {
|
||||
func (c *Config) NewSystemFwConfig(configPath string, preLoadCb, reLoadCb func()) (*Config, error) {
|
||||
var err error
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
|
@ -143,7 +143,7 @@ func (c *Config) NewSystemFwConfig(preLoadCb, reLoadCb func()) (*Config, error)
|
|||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.file = "/etc/opensnitchd/system-fw.json"
|
||||
c.file = configPath
|
||||
c.monitorExitChan = make(chan bool, 1)
|
||||
c.preloadCallback = preLoadCb
|
||||
c.reloadCallback = reLoadCb
|
||||
|
@ -151,7 +151,13 @@ func (c *Config) NewSystemFwConfig(preLoadCb, reLoadCb func()) (*Config, error)
|
|||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Config) SetFile(file string) {
|
||||
// SetConfigFile sets the absolute path to the configuration file to use.
|
||||
// If it's empty, it'll be ignored (when changing the fw type for example).
|
||||
func (c *Config) SetConfigFile(file string) {
|
||||
if file == "" {
|
||||
log.Debug("Firewall configuration file not provided, ignoring")
|
||||
return
|
||||
}
|
||||
c.file = file
|
||||
}
|
||||
|
||||
|
|
|
@ -96,17 +96,18 @@ func (ipt *Iptables) Name() string {
|
|||
|
||||
// Init inserts the firewall rules and starts monitoring for firewall
|
||||
// changes.
|
||||
func (ipt *Iptables) Init(qNum *int) {
|
||||
func (ipt *Iptables) Init(qNum *int, configPath, monitorInterval string) {
|
||||
if ipt.IsRunning() {
|
||||
return
|
||||
}
|
||||
ipt.SetQueueNum(qNum)
|
||||
ipt.SetRulesCheckerInterval(monitorInterval)
|
||||
ipt.ErrChan = make(chan string, 100)
|
||||
|
||||
// In order to clean up any existing firewall rule before start,
|
||||
// we need to load the fw configuration first to know what rules
|
||||
// were configured.
|
||||
ipt.NewSystemFwConfig(ipt.preloadConfCallback, ipt.reloadRulesCallback)
|
||||
ipt.NewSystemFwConfig(configPath, ipt.preloadConfCallback, ipt.reloadRulesCallback)
|
||||
ipt.LoadDiskConfiguration(!common.ReloadConf)
|
||||
|
||||
// start from a clean state
|
||||
|
@ -119,6 +120,7 @@ func (ipt *Iptables) Init(qNum *int) {
|
|||
|
||||
// Stop deletes the firewall rules, allowing network traffic.
|
||||
func (ipt *Iptables) Stop() {
|
||||
ipt.ErrChan = make(chan string, 100)
|
||||
if ipt.Running == false {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -71,19 +71,20 @@ func (n *Nft) Name() string {
|
|||
|
||||
// Init inserts the firewall rules and starts monitoring for firewall
|
||||
// changes.
|
||||
func (n *Nft) Init(qNum *int) {
|
||||
func (n *Nft) Init(qNum *int, configPath, monitorInterval string) {
|
||||
if n.IsRunning() {
|
||||
return
|
||||
}
|
||||
n.Conn = NewNft()
|
||||
n.ErrChan = make(chan string, 100)
|
||||
InitMapsStore()
|
||||
n.SetQueueNum(qNum)
|
||||
n.Conn = NewNft()
|
||||
n.SetRulesCheckerInterval(monitorInterval)
|
||||
|
||||
// In order to clean up any existing firewall rule before start,
|
||||
// we need to load the fw configuration first to know what rules
|
||||
// were configured.
|
||||
n.NewSystemFwConfig(n.PreloadConfCallback, n.ReloadConfCallback)
|
||||
n.NewSystemFwConfig(configPath, n.PreloadConfCallback, n.ReloadConfCallback)
|
||||
n.LoadDiskConfiguration(!common.ReloadConf)
|
||||
|
||||
// start from a clean state
|
||||
|
@ -98,6 +99,7 @@ func (n *Nft) Init(qNum *int) {
|
|||
|
||||
// Stop deletes the firewall rules, allowing network traffic.
|
||||
func (n *Nft) Stop() {
|
||||
n.ErrChan = make(chan string, 100)
|
||||
if n.IsRunning() == false {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -14,6 +14,10 @@ type sysChainsListT struct {
|
|||
expectedRules int
|
||||
}
|
||||
|
||||
var (
|
||||
configFile = "./testdata/test-sysfw-conf.json"
|
||||
)
|
||||
|
||||
func TestAddSystemRules(t *testing.T) {
|
||||
nftest.SkipIfNotPrivileged(t)
|
||||
|
||||
|
@ -21,12 +25,12 @@ func TestAddSystemRules(t *testing.T) {
|
|||
defer nftest.CleanupSystemConn(t, newNS)
|
||||
nftest.Fw.Conn = conn
|
||||
|
||||
cfg, err := nftest.Fw.NewSystemFwConfig(nftest.Fw.PreloadConfCallback, nftest.Fw.ReloadConfCallback)
|
||||
cfg, err := nftest.Fw.NewSystemFwConfig(configFile, nftest.Fw.PreloadConfCallback, nftest.Fw.ReloadConfCallback)
|
||||
if err != nil {
|
||||
t.Logf("Error creating fw config: %s", err)
|
||||
}
|
||||
|
||||
cfg.SetFile("./testdata/test-sysfw-conf.json")
|
||||
cfg.SetConfigFile("./testdata/test-sysfw-conf.json")
|
||||
if err := cfg.LoadDiskConfiguration(false); err != nil {
|
||||
t.Errorf("Error loading config from disk: %s", err)
|
||||
}
|
||||
|
@ -69,12 +73,12 @@ func TestFwConfDisabled(t *testing.T) {
|
|||
defer nftest.CleanupSystemConn(t, newNS)
|
||||
nftest.Fw.Conn = conn
|
||||
|
||||
cfg, err := nftest.Fw.NewSystemFwConfig(nftest.Fw.PreloadConfCallback, nftest.Fw.ReloadConfCallback)
|
||||
cfg, err := nftest.Fw.NewSystemFwConfig(configFile, nftest.Fw.PreloadConfCallback, nftest.Fw.ReloadConfCallback)
|
||||
if err != nil {
|
||||
t.Logf("Error creating fw config: %s", err)
|
||||
}
|
||||
|
||||
cfg.SetFile("./testdata/test-sysfw-conf.json")
|
||||
cfg.SetConfigFile("./testdata/test-sysfw-conf.json")
|
||||
if err := cfg.LoadDiskConfiguration(false); err != nil {
|
||||
t.Errorf("Error loading config from disk: %s", err)
|
||||
}
|
||||
|
@ -108,12 +112,12 @@ func TestDeleteSystemRules(t *testing.T) {
|
|||
defer nftest.CleanupSystemConn(t, newNS)
|
||||
nftest.Fw.Conn = conn
|
||||
|
||||
cfg, err := nftest.Fw.NewSystemFwConfig(nftest.Fw.PreloadConfCallback, nftest.Fw.ReloadConfCallback)
|
||||
cfg, err := nftest.Fw.NewSystemFwConfig(configFile, nftest.Fw.PreloadConfCallback, nftest.Fw.ReloadConfCallback)
|
||||
if err != nil {
|
||||
t.Logf("Error creating fw config: %s", err)
|
||||
}
|
||||
|
||||
cfg.SetFile("./testdata/test-sysfw-conf.json")
|
||||
cfg.SetConfigFile("./testdata/test-sysfw-conf.json")
|
||||
if err := cfg.LoadDiskConfiguration(false); err != nil {
|
||||
t.Errorf("Error loading config from disk: %s", err)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
// Firewall is the interface that all firewalls (iptables, nftables) must implement.
|
||||
type Firewall interface {
|
||||
Init(*int)
|
||||
Init(*int, string, string)
|
||||
Stop()
|
||||
Name() string
|
||||
IsRunning() bool
|
||||
|
@ -45,7 +45,7 @@ var (
|
|||
// We'll try to use the firewall configured in the configuration (iptables/nftables).
|
||||
// If iptables is not installed, we can add nftables rules directly to the kernel,
|
||||
// without relying on any binaries.
|
||||
func Init(fwType string, qNum *int) (err error) {
|
||||
func Init(fwType, configPath, monitorInterval string, qNum *int) (err error) {
|
||||
if fwType == iptables.Name {
|
||||
fw, err = iptables.Fw()
|
||||
if err != nil {
|
||||
|
@ -68,7 +68,7 @@ func Init(fwType string, qNum *int) (err error) {
|
|||
return fmt.Errorf("Firewall not initialized")
|
||||
}
|
||||
fw.Stop()
|
||||
fw.Init(qNum)
|
||||
fw.Init(qNum, configPath, monitorInterval)
|
||||
queueNum = *qNum
|
||||
|
||||
log.Info("Using %s firewall", fw.Name())
|
||||
|
@ -99,19 +99,13 @@ func CleanRules(logErrors bool) {
|
|||
fw.CleanRules(logErrors)
|
||||
}
|
||||
|
||||
// ChangeFw stops current firewall and initializes a new one.
|
||||
func ChangeFw(fwtype string) (err error) {
|
||||
// Reload stops current firewall and initializes a new one.
|
||||
func Reload(fwtype, configPath, monitorInterval string) (err error) {
|
||||
Stop()
|
||||
err = Init(fwtype, &queueNum)
|
||||
err = Init(fwtype, configPath, monitorInterval, &queueNum)
|
||||
return
|
||||
}
|
||||
|
||||
// Reload deletes existing firewall rules and readds them.
|
||||
func Reload() {
|
||||
fw.Stop()
|
||||
fw.Init(&queueNum)
|
||||
}
|
||||
|
||||
// ReloadSystemRules deletes existing rules, and add them again
|
||||
func ReloadSystemRules() {
|
||||
fw.DeleteSystemRules(!common.ForcedDelRules, common.RestoreChains, true)
|
||||
|
|
|
@ -63,6 +63,7 @@ var (
|
|||
logMicro = false
|
||||
rulesPath = "/etc/opensnitchd/rules/"
|
||||
configFile = "/etc/opensnitchd/default-config.json"
|
||||
fwConfigFile = "/etc/opensnitchd/system-fw.json"
|
||||
noLiveReload = false
|
||||
queueNum = 0
|
||||
repeatQueueNum int //will be set later to queueNum + 1
|
||||
|
@ -105,6 +106,7 @@ func init() {
|
|||
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(&fwConfigFile, "fw-config-file", fwConfigFile, "Path to the system fw 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).")
|
||||
|
@ -556,8 +558,17 @@ func main() {
|
|||
}
|
||||
repeatPktChan = repeatQueue.Packets()
|
||||
|
||||
fwConfigPath := fwConfigFile
|
||||
if cfg.FwOptions.ConfigPath != "" {
|
||||
fwConfigPath = cfg.FwOptions.ConfigPath
|
||||
}
|
||||
log.Info("Using system fw configuration %s ...", fwConfigPath)
|
||||
// queue is ready, run firewall rules and start intercepting connections
|
||||
if err = firewall.Init(uiClient.GetFirewallType(), &queueNum); err != nil {
|
||||
if err = firewall.Init(
|
||||
uiClient.GetFirewallType(),
|
||||
fwConfigPath,
|
||||
cfg.FwOptions.MonitorInterval,
|
||||
&queueNum); err != nil {
|
||||
log.Warning("%s", err)
|
||||
uiClient.SendWarningAlert(err)
|
||||
}
|
||||
|
|
|
@ -49,16 +49,24 @@ type rulesOptions struct {
|
|||
EnableChecksums bool `json:"EnableChecksums"`
|
||||
}
|
||||
|
||||
type fwOptions struct {
|
||||
Firewall string `json:"Firewall"`
|
||||
ConfigPath string `json:"ConfigPath"`
|
||||
ActionOnOverflow string `json:"ActionOnOverflow"`
|
||||
MonitorInterval string `json:"MonitorInterval"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
ProcMonitorMethod string `json:"ProcMonitorMethod"`
|
||||
Rules rulesOptions `json:"Rules"`
|
||||
Firewall string `json:"Firewall"`
|
||||
FwOptions fwOptions `json:"FwOptions"`
|
||||
LogLevel *int32 `json:"LogLevel"`
|
||||
InterceptUnknown bool `json:"InterceptUnknown"`
|
||||
LogUTC bool `json:"LogUTC"`
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/evilsocket/opensnitch/daemon/firewall"
|
||||
"github.com/evilsocket/opensnitch/daemon/log"
|
||||
"github.com/evilsocket/opensnitch/daemon/procmon/monitor"
|
||||
"github.com/evilsocket/opensnitch/daemon/rule"
|
||||
|
@ -60,6 +61,11 @@ func (c *Client) loadDiskConfiguration(reload bool) {
|
|||
}
|
||||
|
||||
if reload {
|
||||
firewall.Reload(
|
||||
clientConfig.Firewall,
|
||||
clientConfig.FwOptions.ConfigPath,
|
||||
clientConfig.FwOptions.MonitorInterval,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,11 @@ func (c *Client) handleActionChangeConfig(stream protocol.UI_NotificationsClient
|
|||
}
|
||||
|
||||
if c.GetFirewallType() != newConf.Firewall {
|
||||
firewall.ChangeFw(newConf.Firewall)
|
||||
firewall.Reload(
|
||||
newConf.Firewall,
|
||||
newConf.FwOptions.ConfigPath,
|
||||
newConf.FwOptions.MonitorInterval,
|
||||
)
|
||||
}
|
||||
|
||||
if err := monitor.ReconfigureMonitorMethod(newConf.ProcMonitorMethod); err != nil {
|
||||
|
|
Loading…
Add table
Reference in a new issue