mirror of
https://github.com/evilsocket/opensnitch.git
synced 2025-03-04 08:34:40 +01:00
allow to configure what firewall to use
Before this change, we tried to determine what firewall to use based on the version of iptables (if -V legacy -> nftables, otherwise iptables). This caused problems (#455), and as there's no support yet for nftables system firewall rules, it can't be configured to workaround these errors. Now the default firewall to use will be iptables. If it's not available (installed), can't be used or the configuration option is empty/missing, we'll use nftables.
This commit is contained in:
parent
db18b0cc63
commit
ba7c4e1878
10 changed files with 127 additions and 54 deletions
|
@ -8,5 +8,6 @@
|
|||
"DefaultDuration": "once",
|
||||
"InterceptUnknown": false,
|
||||
"ProcMonitorMethod": "proc",
|
||||
"LogLevel": 2
|
||||
"LogLevel": 2,
|
||||
"Firewall": "iptables"
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ type (
|
|||
// Common holds common fields and functionality of both firewalls,
|
||||
// iptables and nftables.
|
||||
Common struct {
|
||||
sync.Mutex
|
||||
sync.RWMutex
|
||||
QueueNum uint16
|
||||
Running bool
|
||||
RulesChecker *time.Ticker
|
||||
|
@ -58,6 +58,9 @@ func (c *Common) SetQueueNum(qNum *int) {
|
|||
|
||||
// IsRunning returns if the firewall is running or not.
|
||||
func (c *Common) IsRunning() bool {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
return c != nil && c.Running
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
package iptables
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/evilsocket/opensnitch/daemon/core"
|
||||
"github.com/evilsocket/opensnitch/daemon/firewall/common"
|
||||
"github.com/evilsocket/opensnitch/daemon/firewall/config"
|
||||
"github.com/evilsocket/opensnitch/daemon/log"
|
||||
|
@ -16,6 +13,13 @@ import (
|
|||
// Action is the modifier we apply to a rule.
|
||||
type Action string
|
||||
|
||||
const (
|
||||
// Name is the name that identifies this firewall
|
||||
Name = "iptables"
|
||||
// SystemRulePrefix prefix added to each system rule
|
||||
SystemRulePrefix = "opensnitch-filter"
|
||||
)
|
||||
|
||||
// Actions we apply to the firewall.
|
||||
const (
|
||||
ADD = Action("-A")
|
||||
|
@ -24,8 +28,6 @@ const (
|
|||
FLUSH = Action("-F")
|
||||
NEWCHAIN = Action("-N")
|
||||
DELCHAIN = Action("-X")
|
||||
|
||||
SystemRulePrefix = "opensnitch-filter"
|
||||
)
|
||||
|
||||
// SystemChains holds the fw rules defined by the user
|
||||
|
@ -51,7 +53,7 @@ type Iptables struct {
|
|||
|
||||
// Fw initializes a new Iptables object
|
||||
func Fw() (*Iptables, error) {
|
||||
if err := IsSupported(); err != nil {
|
||||
if err := IsAvailable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -70,7 +72,7 @@ func Fw() (*Iptables, error) {
|
|||
|
||||
// Name returns the firewall name
|
||||
func (ipt *Iptables) Name() string {
|
||||
return "iptables"
|
||||
return Name
|
||||
}
|
||||
|
||||
// Init inserts the firewall rules and starts monitoring for firewall
|
||||
|
@ -110,20 +112,12 @@ func (ipt *Iptables) Stop() {
|
|||
ipt.Running = false
|
||||
}
|
||||
|
||||
// IsSupported checks if iptables is installed in the system and what version it is.
|
||||
// If it's not installed or if it's nftables, nftables will be used instead.
|
||||
func IsSupported() error {
|
||||
raw, err := exec.Command("iptables", []string{"-V"}...).CombinedOutput()
|
||||
// IsAvailable checks if iptables is installed in the system.
|
||||
func IsAvailable() error {
|
||||
_, err := exec.Command("iptables", []string{"-V"}...).CombinedOutput()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
version := strings.Split(string(raw), " ")
|
||||
log.Info("iptables version: %s", version)
|
||||
// format: iptables v1.8.7 (nf_tables), iptables v1.8.7 (legacy), iptables v1.8.7
|
||||
if len(version) > 2 && core.Trim(version[2]) == "(nf_tables)" {
|
||||
return fmt.Errorf("fw, using nftables instead of iptables (%v)", version)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ func (n *Nft) AreRulesLoaded() bool {
|
|||
return false
|
||||
}
|
||||
for _, r := range rules {
|
||||
if string(r.UserData) == connsRuleKey {
|
||||
if string(r.UserData) == fwKey {
|
||||
nRules++
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ func (n *Nft) AreRulesLoaded() bool {
|
|||
return false
|
||||
}
|
||||
for _, r := range rules {
|
||||
if string(r.UserData) == dnsRuleKey {
|
||||
if string(r.UserData) == fwKey {
|
||||
nRules++
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ func (n *Nft) AreRulesLoaded() bool {
|
|||
}
|
||||
|
||||
func (n *Nft) reloadRulesCallback() {
|
||||
log.Important("firewall rules changed, reloading")
|
||||
log.Important("nftables firewall rules changed, reloading")
|
||||
n.AddSystemRules()
|
||||
n.InsertRules()
|
||||
}
|
||||
|
|
|
@ -11,16 +11,17 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
mangleTableName = "opensnitch-mangle"
|
||||
filterTableName = "opensnitch-filter"
|
||||
// Name is the name that identifies this firewall
|
||||
Name = "nftables"
|
||||
|
||||
mangleTableName = "mangle"
|
||||
filterTableName = "filter"
|
||||
// The following chains will be under our own mangle or filter tables.
|
||||
// There shouldn't be other chains with the same name here.
|
||||
outputChain = "output"
|
||||
inputChain = "input"
|
||||
// key assigned to every fw rule we add, in order to get rules by this key.
|
||||
fwKey = "opesnitch-key"
|
||||
dnsRuleKey = fwKey + "-dns"
|
||||
connsRuleKey = fwKey + "-conns"
|
||||
fwKey = "opensnitch-key"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -75,7 +76,7 @@ func Fw() (*Nft, error) {
|
|||
|
||||
// Name returns the name of the firewall
|
||||
func (n *Nft) Name() string {
|
||||
return "nftables"
|
||||
return Name
|
||||
}
|
||||
|
||||
// Init inserts the firewall rules and starts monitoring for firewall
|
||||
|
@ -118,6 +119,7 @@ func (n *Nft) Stop() {
|
|||
|
||||
// InsertRules adds fw rules to intercept connections
|
||||
func (n *Nft) InsertRules() {
|
||||
n.delInterceptionRules()
|
||||
n.addGlobalTables()
|
||||
n.addGlobalChains()
|
||||
|
||||
|
@ -130,10 +132,7 @@ func (n *Nft) InsertRules() {
|
|||
|
||||
// CleanRules deletes the rules we added.
|
||||
func (n *Nft) CleanRules(logErrors bool) {
|
||||
n.conn.DelTable(mangleTable)
|
||||
n.conn.DelTable(mangleTable6)
|
||||
n.conn.DelTable(filterTable)
|
||||
n.conn.DelTable(filterTable6)
|
||||
n.delInterceptionRules()
|
||||
err := n.conn.Flush()
|
||||
if err != nil && logErrors {
|
||||
log.Error("Error cleaning nftables tables: %s", err)
|
||||
|
|
|
@ -92,7 +92,7 @@ func (n *Nft) QueueDNSResponses(enable bool, logError bool) (error, error) {
|
|||
},
|
||||
},
|
||||
// rule key, to allow get it later by key
|
||||
UserData: []byte(dnsRuleKey),
|
||||
UserData: []byte(fwKey),
|
||||
})
|
||||
}
|
||||
// apply changes
|
||||
|
@ -139,7 +139,7 @@ func (n *Nft) QueueConnections(enable bool, logError bool) (error, error) {
|
|||
},
|
||||
},
|
||||
// rule key, to allow get it later by key
|
||||
UserData: []byte(connsRuleKey),
|
||||
UserData: []byte(fwKey),
|
||||
})
|
||||
}
|
||||
// apply changes
|
||||
|
@ -149,3 +149,50 @@ func (n *Nft) QueueConnections(enable bool, logError bool) (error, error) {
|
|||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *Nft) delInterceptionRules() {
|
||||
n.delRulesByKey(fwKey)
|
||||
}
|
||||
|
||||
func (n *Nft) delRulesByKey(key string) {
|
||||
chains, err := n.conn.ListChains()
|
||||
if err != nil {
|
||||
log.Warning("nftables, error listing chains: %s", err)
|
||||
return
|
||||
}
|
||||
commit := false
|
||||
for _, c := range chains {
|
||||
deletedRules := 0
|
||||
rules, err := n.conn.GetRule(c.Table, c)
|
||||
if err != nil {
|
||||
log.Warning("nftables, error listing rules: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, r := range rules {
|
||||
if string(r.UserData) == key {
|
||||
// just passing the rule object doesn't work.
|
||||
if err := n.conn.DelRule(&nftables.Rule{
|
||||
Table: c.Table,
|
||||
Chain: c,
|
||||
Handle: r.Handle,
|
||||
}); err != nil {
|
||||
log.Warning("nftables, error deleting interception rule: %s", err)
|
||||
continue
|
||||
}
|
||||
deletedRules++
|
||||
commit = true
|
||||
}
|
||||
}
|
||||
if deletedRules == len(rules) {
|
||||
n.conn.DelTable(c.Table)
|
||||
}
|
||||
}
|
||||
if commit {
|
||||
if err := n.conn.Flush(); err != nil {
|
||||
log.Warning("nftables, error applying interception rules: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -28,20 +28,6 @@ type Firewall interface {
|
|||
|
||||
var fw Firewall
|
||||
|
||||
// Determine as soon as possible what firewall to use.
|
||||
func init() {
|
||||
var err error
|
||||
fw, err = iptables.Fw()
|
||||
if err != nil {
|
||||
log.Warning("iptables not available: %s", err)
|
||||
fw, err = nftables.Fw()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Warning("firewall error: %s, not iptables nor nftables are available or are usable. Please, report it on github.", err)
|
||||
}
|
||||
}
|
||||
|
||||
// IsRunning returns if the firewall is running or not.
|
||||
func IsRunning() bool {
|
||||
return fw != nil && fw.IsRunning()
|
||||
|
@ -49,20 +35,50 @@ func IsRunning() bool {
|
|||
|
||||
// CleanRules deletes the rules we added.
|
||||
func CleanRules(logErrors bool) {
|
||||
if fw == nil {
|
||||
return
|
||||
}
|
||||
fw.CleanRules(logErrors)
|
||||
}
|
||||
|
||||
// Stop deletes the firewall rules, allowing network traffic.
|
||||
func Stop() {
|
||||
if fw == nil {
|
||||
return
|
||||
}
|
||||
fw.Stop()
|
||||
}
|
||||
|
||||
// Init initializes the firewall and loads firewall rules.
|
||||
func Init(qNum *int) {
|
||||
func Init(fwType string, qNum *int) {
|
||||
var err error
|
||||
|
||||
if fwType == iptables.Name {
|
||||
fw, err = iptables.Fw()
|
||||
if err != nil {
|
||||
log.Warning("iptables not available: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// if iptables is not installed, we can add nftables rules directly to the kernel,
|
||||
// without relying on any binaries.
|
||||
if fwType == nftables.Name || err != nil {
|
||||
fw, err = nftables.Fw()
|
||||
if err != nil {
|
||||
log.Warning("nftables not available: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Warning("firewall error: %s, not iptables nor nftables are available or are usable. Please, report it on github.", err)
|
||||
return
|
||||
}
|
||||
|
||||
if fw == nil {
|
||||
log.Error("firewall not initialized.")
|
||||
return
|
||||
}
|
||||
fw.Stop()
|
||||
fw.Init(qNum)
|
||||
|
||||
log.Info("Using %s firewall", fw.Name())
|
||||
|
|
|
@ -376,10 +376,11 @@ func main() {
|
|||
}
|
||||
repeatPktChan = repeatQueue.Packets()
|
||||
|
||||
// queue is ready, run firewall rules
|
||||
firewall.Init(&queueNum)
|
||||
|
||||
uiClient = ui.NewClient(uiSocket, stats, rules)
|
||||
|
||||
// queue is ready, run firewall rules
|
||||
firewall.Init(uiClient.GetFirewallType(), &queueNum)
|
||||
|
||||
if overwriteLogging() {
|
||||
setupLogging()
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/evilsocket/opensnitch/daemon/conman"
|
||||
"github.com/evilsocket/opensnitch/daemon/firewall/iptables"
|
||||
"github.com/evilsocket/opensnitch/daemon/log"
|
||||
"github.com/evilsocket/opensnitch/daemon/rule"
|
||||
"github.com/evilsocket/opensnitch/daemon/statistics"
|
||||
|
@ -43,6 +44,7 @@ type Config struct {
|
|||
InterceptUnknown bool `json:"InterceptUnknown"`
|
||||
ProcMonitorMethod string `json:"ProcMonitorMethod"`
|
||||
LogLevel *uint32 `json:"LogLevel"`
|
||||
Firewall string `json:"Firewall"`
|
||||
}
|
||||
|
||||
// Client holds the connection information of a client.
|
||||
|
@ -105,6 +107,16 @@ func (c *Client) InterceptUnknown() bool {
|
|||
return config.InterceptUnknown
|
||||
}
|
||||
|
||||
// GetFirewallType returns the firewall to use
|
||||
func (c *Client) GetFirewallType() string {
|
||||
config.RLock()
|
||||
defer config.RUnlock()
|
||||
if config.Firewall == "" {
|
||||
return iptables.Name
|
||||
}
|
||||
return config.Firewall
|
||||
}
|
||||
|
||||
// DefaultAction returns the default configured action for
|
||||
func (c *Client) DefaultAction() rule.Action {
|
||||
isConnected := c.Connected()
|
||||
|
|
|
@ -208,7 +208,7 @@ func (c *Client) handleNotification(stream protocol.UI_NotificationsClient, noti
|
|||
|
||||
case notification.Type == protocol.Action_LOAD_FIREWALL:
|
||||
log.Info("[notification] starting firewall")
|
||||
firewall.Init(nil)
|
||||
firewall.Init(c.GetFirewallType(), nil)
|
||||
c.sendNotificationReply(stream, notification.Id, "", nil)
|
||||
|
||||
case notification.Type == protocol.Action_UNLOAD_FIREWALL:
|
||||
|
|
Loading…
Add table
Reference in a new issue