Skip to content

Commit

Permalink
allow to configure nfqueue bypass flag
Browse files Browse the repository at this point in the history
Nfqueue bypass option skips the enqueue of packets to userspace
if no application is listening to the queue.
https://wiki.nftables.org/wiki-nftables/index.php/Queueing_to_userspace

If this flag is not specified, and for example the daemon dies
unexpectedly, all the outbound traffic will be blocked.

Up until now we've been using this flag by default not to block network
traffic if the daemon dies or is killed for some reason. But some users
want to use precisely this behaviour (#884, #1183, #1201).

Now you can configure it, to block connections if the daemon
unexpectedly dies.

The option is on by default in the configuration (QueueBypass: true).
If this item is not present in the daemon config file, then it'll be
false.
  • Loading branch information
gustavo-iniguez-goya committed Oct 19, 2024
1 parent ebac200 commit 6622df9
Show file tree
Hide file tree
Showing 10 changed files with 50 additions and 18 deletions.
3 changes: 2 additions & 1 deletion daemon/default-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"Firewall": "nftables",
"FwOptions": {
"ConfigPath": "/etc/opensnitchd/system-fw.json",
"MonitorInterval": "15s"
"MonitorInterval": "15s",
"BypassQueue": true
},
"Rules": {
"Path": "/etc/opensnitchd/rules/",
Expand Down
6 changes: 4 additions & 2 deletions daemon/firewall/iptables/iptables.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type Iptables struct {
bin string
bin6 string
chains SystemChains
bypassQueue bool
common.Common
config.Config

Expand All @@ -71,7 +72,7 @@ func Fw() (*Iptables, error) {
return nil, err
}

reRulesQuery, _ := regexp.Compile(`NFQUEUE.*ctstate NEW,RELATED.*NFQUEUE num.*bypass`)
reRulesQuery, _ := regexp.Compile(`NFQUEUE.*ctstate NEW,RELATED.*NFQUEUE num.*`)
reSystemRulesQuery, _ := regexp.Compile(SystemRulePrefix + ".*")

ipt := &Iptables{
Expand All @@ -93,10 +94,11 @@ func (ipt *Iptables) Name() string {

// Init inserts the firewall rules and starts monitoring for firewall
// changes.
func (ipt *Iptables) Init(qNum uint16, configPath, monitorInterval string) {
func (ipt *Iptables) Init(qNum uint16, configPath, monitorInterval string, bypassQueue bool) {
if ipt.IsRunning() {
return
}
ipt.bypassQueue = bypassQueue
ipt.SetQueueNum(qNum)
ipt.SetRulesCheckerInterval(monitorInterval)
ipt.ErrChan = make(chan string, 100)
Expand Down
19 changes: 17 additions & 2 deletions daemon/firewall/iptables/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,27 @@ import (
"github.com/vishvananda/netlink"
)

func (ipt *Iptables) getBypassQueue() string {
if !ipt.bypassQueue {
return ""
}

return "--queue-bypass"
}

// RunRule inserts or deletes a firewall rule.
func (ipt *Iptables) RunRule(action Action, enable bool, logError bool, rule []string) (err4, err6 error) {
if enable == false {
action = "-D"
}

// If the last argument of the rule is "", the iptables command fails.
// So if the user selects "QueueBypass: false", delete the last token of the rule,
// which will be "".
args := len(rule)
if rule[args-1] == "" {
rule = append(rule[:args-1], rule[args:]...)
}
rule = append([]string{string(action)}, rule...)

ipt.Lock()
Expand Down Expand Up @@ -49,7 +64,7 @@ func (ipt *Iptables) QueueDNSResponses(enable bool, logError bool) (err4, err6 e
"--sport", "53",
"-j", "NFQUEUE",
"--queue-num", fmt.Sprintf("%d", ipt.QueueNum),
"--queue-bypass",
ipt.getBypassQueue(),
})
}

Expand All @@ -64,7 +79,7 @@ func (ipt *Iptables) QueueConnections(enable bool, logError bool) (error, error)
"--ctstate", "NEW,RELATED",
"-j", "NFQUEUE",
"--queue-num", fmt.Sprintf("%d", ipt.QueueNum),
"--queue-bypass",
ipt.getBypassQueue(),
})
if enable {
// flush conntrack as soon as netfilter rule is set. This ensures that already-established
Expand Down
8 changes: 5 additions & 3 deletions daemon/firewall/nftables/nftables.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ var (

// Nft holds the fields of our nftables firewall
type Nft struct {
Conn *nftables.Conn
chains iptables.SystemChains
Conn *nftables.Conn
chains iptables.SystemChains
bypassQueue bool

common.Common
config.Config
Expand Down Expand Up @@ -71,10 +72,11 @@ func (n *Nft) Name() string {

// Init inserts the firewall rules and starts monitoring for firewall
// changes.
func (n *Nft) Init(qNum uint16, configPath, monitorInterval string) {
func (n *Nft) Init(qNum uint16, configPath, monitorInterval string, bypassQueue bool) {
if n.IsRunning() {
return
}
n.bypassQueue = bypassQueue
n.Conn = NewNft()
n.ErrChan = make(chan string, 100)
InitMapsStore()
Expand Down
6 changes: 3 additions & 3 deletions daemon/firewall/nftables/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (n *Nft) QueueDNSResponses(enable, logError bool) (error, error) {
},
&expr.Queue{
Num: n.QueueNum,
Flag: expr.QueueFlagBypass,
Flag: n.getBypassFlag(),
},
},
// rule key, to allow get it later by key
Expand Down Expand Up @@ -112,7 +112,7 @@ func (n *Nft) QueueConnections(enable, logError bool) (error, error) {
&expr.Cmp{Op: expr.CmpOpNeq, Register: 1, Data: []byte{0, 0, 0, 0}},
&expr.Queue{
Num: n.QueueNum,
Flag: expr.QueueFlagBypass,
Flag: n.getBypassFlag(),
},
},
// rule key, to allow get it later by key
Expand Down Expand Up @@ -163,7 +163,7 @@ func (n *Nft) QueueConnections(enable, logError bool) (error, error) {
},
&expr.Queue{
Num: n.QueueNum,
Flag: expr.QueueFlagBypass,
Flag: n.getBypassFlag(),
},
},
// rule key, to allow get it later by key
Expand Down
9 changes: 9 additions & 0 deletions daemon/firewall/nftables/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ import (
"github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs"
"github.com/evilsocket/opensnitch/daemon/log"
"github.com/google/nftables"
"github.com/google/nftables/expr"
)

func (n *Nft) getBypassFlag() expr.QueueFlag {
if n.bypassQueue {
return expr.QueueFlagBypass
}

return 0x0
}

func GetFamilyCode(family string) nftables.TableFamily {
famCode := nftables.TableFamilyINet
switch family {
Expand Down
10 changes: 5 additions & 5 deletions daemon/firewall/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

// Firewall is the interface that all firewalls (iptables, nftables) must implement.
type Firewall interface {
Init(uint16, string, string)
Init(uint16, string, string, bool)
Stop()
Name() string
IsRunning() bool
Expand Down Expand Up @@ -46,7 +46,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, configPath, monitorInterval string, qNum uint16) (err error) {
func Init(fwType, configPath, monitorInterval string, bypassQueue bool, qNum uint16) (err error) {
if fwType == iptables.Name {
fw, err = iptables.Fw()
if err != nil {
Expand All @@ -72,7 +72,7 @@ func Init(fwType, configPath, monitorInterval string, qNum uint16) (err error) {
configPath = config.DefaultConfigFile
}
fw.Stop()
fw.Init(qNum, configPath, monitorInterval)
fw.Init(qNum, configPath, monitorInterval, bypassQueue)
queueNum = qNum

log.Info("Using %s firewall", fw.Name())
Expand Down Expand Up @@ -104,9 +104,9 @@ func CleanRules(logErrors bool) {
}

// Reload stops current firewall and initializes a new one.
func Reload(fwtype, configPath, monitorInterval string, queueNum uint16) (err error) {
func Reload(fwtype, configPath, monitorInterval string, bypassQueue bool, queueNum uint16) (err error) {
Stop()
err = Init(fwtype, configPath, monitorInterval, queueNum)
err = Init(fwtype, configPath, monitorInterval, bypassQueue, queueNum)
return
}

Expand Down
1 change: 1 addition & 0 deletions daemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ func overwriteFw(cfg *config.Config, qNum uint16, fwCfg string) {
cfg.Firewall,
fwCfg,
cfg.FwOptions.MonitorInterval,
cfg.FwOptions.QueueBypass,
qNum,
)
// TODO: Close() closes the daemon if closing the queue timeouts
Expand Down
2 changes: 1 addition & 1 deletion daemon/ui/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ type (
FwOptions struct {
Firewall string `json:"Firewall"`
ConfigPath string `json:"ConfigPath"`
BypassQueue string `json:"BypassQueue"`
MonitorInterval string `json:"MonitorInterval"`
QueueNum uint16 `json:"QueueNum"`
QueueBypass bool `json:"QueueBypass"`
}

// InternalOptions struct
Expand Down
4 changes: 3 additions & 1 deletion daemon/ui/config_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,14 +204,16 @@ func (c *Client) reloadConfiguration(reload bool, newConfig config.Config) *moni
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 {
newConfig.FwOptions.MonitorInterval != c.config.FwOptions.MonitorInterval ||
newConfig.FwOptions.QueueBypass != c.config.FwOptions.QueueBypass {
log.Debug("[config] reloading config.firewall")
reloadFw = true

firewall.Reload(
newConfig.Firewall,
newConfig.FwOptions.ConfigPath,
newConfig.FwOptions.MonitorInterval,
newConfig.FwOptions.QueueBypass,
newConfig.FwOptions.QueueNum,
)
} else {
Expand Down

0 comments on commit 6622df9

Please sign in to comment.