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

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.
199 lines
5.4 KiB
Go
199 lines
5.4 KiB
Go
package nftables
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/evilsocket/opensnitch/daemon/firewall/common"
|
|
"github.com/evilsocket/opensnitch/daemon/firewall/config"
|
|
"github.com/evilsocket/opensnitch/daemon/firewall/iptables"
|
|
"github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs"
|
|
"github.com/evilsocket/opensnitch/daemon/log"
|
|
"github.com/evilsocket/opensnitch/daemon/ui/protocol"
|
|
"github.com/golang/protobuf/jsonpb"
|
|
"github.com/google/nftables"
|
|
)
|
|
|
|
// Action is the modifier we apply to a rule.
|
|
type Action string
|
|
|
|
// Actions we apply to the firewall.
|
|
const (
|
|
fwKey = "opensnitch-key"
|
|
InterceptionRuleKey = fwKey + "-interception"
|
|
SystemRuleKey = fwKey + "-system"
|
|
Name = "nftables"
|
|
)
|
|
|
|
var (
|
|
filterTable = &nftables.Table{
|
|
Family: nftables.TableFamilyINet,
|
|
Name: exprs.NFT_CHAIN_FILTER,
|
|
}
|
|
|
|
mangleTable = &nftables.Table{
|
|
Family: nftables.TableFamilyINet,
|
|
Name: exprs.NFT_CHAIN_FILTER,
|
|
}
|
|
)
|
|
|
|
// Nft holds the fields of our nftables firewall
|
|
type Nft struct {
|
|
Conn *nftables.Conn
|
|
chains iptables.SystemChains
|
|
bypassQueue bool
|
|
|
|
common.Common
|
|
config.Config
|
|
sync.Mutex
|
|
}
|
|
|
|
// NewNft creates a new nftables object
|
|
func NewNft() *nftables.Conn {
|
|
return &nftables.Conn{}
|
|
}
|
|
|
|
// Fw initializes a new nftables object
|
|
func Fw() (*Nft, error) {
|
|
n := &Nft{
|
|
chains: iptables.SystemChains{
|
|
Rules: make(map[string]*iptables.SystemRule),
|
|
},
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// Name returns the name of the firewall
|
|
func (n *Nft) Name() string {
|
|
return Name
|
|
}
|
|
|
|
// Init inserts the firewall rules and starts monitoring for firewall
|
|
// changes.
|
|
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()
|
|
n.SetQueueNum(qNum)
|
|
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(configPath, n.PreloadConfCallback, n.ReloadConfCallback)
|
|
n.LoadDiskConfiguration(!common.ReloadConf)
|
|
|
|
// start from a clean state
|
|
// The daemon may have exited unexpectedly, leaving residual fw rules, so we
|
|
// need to clean them up to avoid duplicated rules.
|
|
n.DelInterceptionRules()
|
|
n.AddSystemRules(!common.ReloadRules, common.BackupChains)
|
|
n.EnableInterception()
|
|
|
|
n.Running = true
|
|
}
|
|
|
|
// Stop deletes the firewall rules, allowing network traffic.
|
|
func (n *Nft) Stop() {
|
|
n.ErrChan = make(chan string, 100)
|
|
if n.IsRunning() == false {
|
|
return
|
|
}
|
|
n.StopConfigWatcher()
|
|
n.StopCheckingRules()
|
|
n.CleanRules(log.GetLogLevel() == log.DEBUG)
|
|
|
|
n.Lock()
|
|
n.Running = false
|
|
n.Unlock()
|
|
}
|
|
|
|
// EnableInterception adds firewall rules to intercept connections
|
|
func (n *Nft) EnableInterception() {
|
|
if err := n.AddInterceptionTables(); err != nil {
|
|
log.Error("Error while adding interception tables: %s", err)
|
|
return
|
|
}
|
|
if err := n.AddInterceptionChains(); err != nil {
|
|
log.Error("Error while adding interception chains: %s", err)
|
|
return
|
|
}
|
|
|
|
if err, _ := n.QueueDNSResponses(common.EnableRule, common.EnableRule); err != nil {
|
|
log.Error("Error while running DNS nftables rule: %s", err)
|
|
}
|
|
if err, _ := n.QueueConnections(common.EnableRule, common.EnableRule); err != nil {
|
|
log.Error("Error while running conntrack nftables rule: %s", err)
|
|
}
|
|
// start monitoring firewall rules to intercept network traffic.
|
|
n.NewRulesChecker(n.AreRulesLoaded, n.ReloadRulesCallback)
|
|
}
|
|
|
|
// DisableInterception removes firewall rules to intercept outbound connections.
|
|
func (n *Nft) DisableInterception(logErrors bool) {
|
|
n.StopCheckingRules()
|
|
n.DelInterceptionRules()
|
|
}
|
|
|
|
// CleanRules deletes the rules we added.
|
|
func (n *Nft) CleanRules(logErrors bool) {
|
|
n.DisableInterception(logErrors)
|
|
n.DeleteSystemRules(common.ForcedDelRules, common.RestoreChains, logErrors)
|
|
}
|
|
|
|
// Commit applies the queued changes, creating new objects (tables, chains, etc).
|
|
// You add rules, chains or tables, and after calling to Flush() they're added to the system.
|
|
// NOTE: it's very important not to call Flush() without queued tasks.
|
|
func (n *Nft) Commit() bool {
|
|
if err := n.Conn.Flush(); err != nil {
|
|
log.Warning("%s error applying changes: %s", logTag, err)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Serialize converts the configuration from json to protobuf
|
|
func (n *Nft) Serialize() (*protocol.SysFirewall, error) {
|
|
sysfw := &protocol.SysFirewall{}
|
|
jun := jsonpb.Unmarshaler{
|
|
AllowUnknownFields: true,
|
|
}
|
|
rawConfig, err := json.Marshal(&n.SysConfig)
|
|
if err != nil {
|
|
log.Error("nftables.Serialize() struct to string error: %s", err)
|
|
return nil, err
|
|
}
|
|
// string to proto
|
|
if err := jun.Unmarshal(strings.NewReader(string(rawConfig)), sysfw); err != nil {
|
|
log.Error("nftables.Serialize() string to protobuf error: %s", err)
|
|
return nil, err
|
|
}
|
|
|
|
return sysfw, nil
|
|
}
|
|
|
|
// Deserialize converts a protocolbuffer structure to byte array.
|
|
func (n *Nft) Deserialize(sysfw *protocol.SysFirewall) ([]byte, error) {
|
|
jun := jsonpb.Marshaler{
|
|
OrigName: true,
|
|
EmitDefaults: true,
|
|
Indent: " ",
|
|
}
|
|
|
|
// NOTE: '<' and '>' characters are encoded to unicode (\u003c).
|
|
// This has no effect on adding rules to nftables.
|
|
// Users can still write "<" if they want to, rules are added ok.
|
|
|
|
var b bytes.Buffer
|
|
if err := jun.Marshal(&b, sysfw); err != nil {
|
|
log.Error("nfables.Deserialize() error 2: %s", err)
|
|
return nil, err
|
|
}
|
|
return b.Bytes(), nil
|
|
}
|