mirror of
https://github.com/evilsocket/opensnitch.git
synced 2025-03-04 08:34:40 +01:00

- Added new configuration field to allow configure fw interception number queue (default to 0): "FwOptions": { "QueueNum": 0 } (we still need to reconfigure nfqueue queues in order for this to take effect). - If the fw configuration path is not supplied, default to /etc/opensnitchd/system-fw.json
186 lines
4.3 KiB
Go
186 lines
4.3 KiB
Go
package common
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/evilsocket/opensnitch/daemon/log"
|
|
)
|
|
|
|
// default arguments for various functions
|
|
var (
|
|
EnableRule = true
|
|
DoLogErrors = true
|
|
ForcedDelRules = true
|
|
ReloadRules = true
|
|
RestoreChains = true
|
|
BackupChains = true
|
|
ReloadConf = true
|
|
|
|
DefaultCheckInterval = 10 * time.Second
|
|
RulesCheckerDisabled = "0s"
|
|
)
|
|
|
|
type (
|
|
callback func()
|
|
callbackBool func() bool
|
|
|
|
// Common holds common fields and functionality of both firewalls,
|
|
// iptables and nftables.
|
|
Common struct {
|
|
RulesChecker *time.Ticker
|
|
ErrChan chan string
|
|
stopChecker chan struct{}
|
|
RulesCheckInterval time.Duration
|
|
QueueNum uint16
|
|
Running bool
|
|
Intercepting bool
|
|
FwEnabled bool
|
|
sync.RWMutex
|
|
}
|
|
)
|
|
|
|
// ErrorsChan returns the channel where the errors are sent to.
|
|
func (c *Common) ErrorsChan() <-chan string {
|
|
return c.ErrChan
|
|
}
|
|
|
|
// ErrChanEmpty checks if the errors channel is empty.
|
|
func (c *Common) ErrChanEmpty() bool {
|
|
return len(c.ErrChan) == 0
|
|
}
|
|
|
|
// SendError sends an error to the channel of errors.
|
|
func (c *Common) SendError(err string) {
|
|
log.Warning("%s", err)
|
|
|
|
if len(c.ErrChan) >= cap(c.ErrChan) {
|
|
log.Debug("fw errors channel full, emptying errChan")
|
|
for e := range c.ErrChan {
|
|
log.Warning("%s", e)
|
|
if c.ErrChanEmpty() {
|
|
break
|
|
}
|
|
}
|
|
return
|
|
}
|
|
select {
|
|
case c.ErrChan <- err:
|
|
case <-time.After(100 * time.Millisecond):
|
|
log.Warning("SendError() channel locked? REVIEW")
|
|
}
|
|
}
|
|
|
|
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 uint16) {
|
|
c.Lock()
|
|
defer c.Unlock()
|
|
c.QueueNum = qNum
|
|
}
|
|
|
|
// IsRunning returns if the firewall is running or not.
|
|
func (c *Common) IsRunning() bool {
|
|
c.RLock()
|
|
defer c.RUnlock()
|
|
|
|
return c != nil && c.Running
|
|
}
|
|
|
|
// IsFirewallEnabled returns if the firewall is running or not.
|
|
func (c *Common) IsFirewallEnabled() bool {
|
|
c.RLock()
|
|
defer c.RUnlock()
|
|
|
|
return c != nil && c.FwEnabled
|
|
}
|
|
|
|
// IsIntercepting returns if the firewall is running or not.
|
|
func (c *Common) IsIntercepting() bool {
|
|
c.RLock()
|
|
defer c.RUnlock()
|
|
|
|
return c != nil && c.Intercepting
|
|
}
|
|
|
|
// NewRulesChecker starts monitoring interception rules.
|
|
// We expect to have 2 rules loaded: one to intercept DNS responses and another one
|
|
// to intercept network traffic.
|
|
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()
|
|
select {
|
|
case c.stopChecker <- struct{}{}:
|
|
case <-time.After(5 * time.Millisecond):
|
|
log.Error("NewRulesChecker: timed out stopping monitor rules")
|
|
}
|
|
}
|
|
c.stopChecker = make(chan struct{}, 1)
|
|
log.Info("Starting new fw checker every %s ...", c.RulesCheckInterval)
|
|
c.RulesChecker = time.NewTicker(c.RulesCheckInterval)
|
|
|
|
go startCheckingRules(c.stopChecker, c.RulesChecker, areRulesLoaded, reloadRules)
|
|
}
|
|
|
|
// StartCheckingRules monitors if our rules are loaded.
|
|
// If the rules to intercept traffic are not loaded, we'll try to insert them again.
|
|
func startCheckingRules(exitChan <-chan struct{}, rulesChecker *time.Ticker, areRulesLoaded callbackBool, reloadRules callback) {
|
|
for {
|
|
select {
|
|
case <-exitChan:
|
|
goto Exit
|
|
case _, active := <-rulesChecker.C:
|
|
if !active {
|
|
goto Exit
|
|
}
|
|
|
|
if areRulesLoaded() == false {
|
|
reloadRules()
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
log.Info("exit checking firewall rules")
|
|
}
|
|
|
|
// StopCheckingRules stops checking if firewall rules are loaded.
|
|
func (c *Common) StopCheckingRules() {
|
|
c.Lock()
|
|
defer c.Unlock()
|
|
|
|
if c.RulesChecker != nil {
|
|
select {
|
|
case c.stopChecker <- struct{}{}:
|
|
close(c.stopChecker)
|
|
case <-time.After(5 * time.Millisecond):
|
|
// We should not arrive here
|
|
log.Error("StopCheckingRules: timed out stopping monitor rules")
|
|
}
|
|
|
|
c.RulesChecker.Stop()
|
|
c.RulesChecker = nil
|
|
}
|
|
}
|
|
|
|
func (c *Common) reloadCallback(callback func()) {
|
|
callback()
|
|
}
|