opensnitch/daemon/firewall/nftables/system.go
Gustavo Iñiguez Goia 8740755f64
sys fw: report errors to the GUI after reloading
- Send errors to the server (GUI) if there's any error when reloading
   the system fw rules (far from being perfect/optimal, needs a
   rewrite).
 - Don't load the configuration after saving it, let the watcher reload
   it on write change to avoid double reload/duplicated errors.
2023-07-15 20:32:42 +02:00

180 lines
4.8 KiB
Go

package nftables
import (
"fmt"
"strings"
"sync"
"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/google/nftables"
"github.com/google/nftables/expr"
"github.com/google/uuid"
)
// store of tables added to the system
type sysTablesT struct {
tables map[string]*nftables.Table
sync.RWMutex
}
func (t *sysTablesT) Add(name string, tbl *nftables.Table) {
t.Lock()
defer t.Unlock()
t.tables[name] = tbl
}
func (t *sysTablesT) Get(name string) *nftables.Table {
t.RLock()
defer t.RUnlock()
return t.tables[name]
}
func (t *sysTablesT) List() map[string]*nftables.Table {
t.RLock()
defer t.RUnlock()
return t.tables
}
func (t *sysTablesT) Del(name string) {
t.Lock()
defer t.Unlock()
delete(t.tables, name)
}
var (
logTag = "nftables:"
sysTables *sysTablesT
sysChains *sync.Map
origSysChains map[string]*nftables.Chain
sysSets []*nftables.Set
)
// InitMapsStore initializes internal stores of chains and maps.
func InitMapsStore() {
sysTables = &sysTablesT{
tables: make(map[string]*nftables.Table),
}
sysChains = &sync.Map{}
origSysChains = make(map[string]*nftables.Chain)
}
// CreateSystemRule create the custom firewall chains and adds them to system.
// nft insert rule ip opensnitch-filter opensnitch-input udp dport 1153
func (n *Nft) CreateSystemRule(chain *config.FwChain, logErrors bool) bool {
if chain.IsInvalid() {
log.Warning("%s CreateSystemRule(), Chain's field Name and Family cannot be empty", logTag)
return false
}
tableName := chain.Table
n.AddTable(chain.Table, chain.Family)
// regular chains doesn't have a hook, nor a type
if chain.Hook == "" && chain.Type == "" {
n.addRegularChain(chain.Name, tableName, chain.Family)
return n.Commit()
}
chainPolicy := nftables.ChainPolicyAccept
if iptables.Action(strings.ToLower(chain.Policy)) == exprs.VERDICT_DROP {
chainPolicy = nftables.ChainPolicyDrop
}
chainHook := GetHook(chain.Hook)
chainPrio, chainType := GetChainPriority(chain.Family, chain.Type, chain.Hook)
if chainPrio == nil {
log.Warning("%s Invalid system firewall combination: %s, %s", logTag, chain.Type, chain.Hook)
return false
}
if ret := n.AddChain(chain.Name, chain.Table, chain.Family, chainPrio,
chainType, chainHook, chainPolicy); ret == nil {
log.Warning("%s error adding chain: %s, table: %s", logTag, chain.Name, chain.Table)
return false
}
return n.Commit()
}
// AddSystemRules creates the system firewall from configuration.
func (n *Nft) AddSystemRules(reload, backupExistingChains bool) {
n.SysConfig.RLock()
defer n.SysConfig.RUnlock()
if n.SysConfig.Enabled == false {
log.Important("[nftables] AddSystemRules() fw disabled")
return
}
if backupExistingChains {
n.backupExistingChains()
}
for _, fwCfg := range n.SysConfig.SystemRules {
for _, chain := range fwCfg.Chains {
if !n.CreateSystemRule(chain, true) {
log.Info("createSystem failed: %s %s", chain.Name, chain.Table)
continue
}
for i := len(chain.Rules) - 1; i >= 0; i-- {
if chain.Rules[i].UUID == "" {
uuid := uuid.New()
chain.Rules[i].UUID = uuid.String()
}
if chain.Rules[i].Enabled {
if err4, _ := n.AddSystemRule(chain.Rules[i], chain); err4 != nil {
n.SendError(fmt.Sprintf("%s (%s)", err4, chain.Rules[i].UUID))
}
}
}
}
}
}
// DeleteSystemRules deletes the system rules.
// If force is false and the rule has not been previously added,
// it won't try to delete the tables and chains. Otherwise it'll try to delete them.
func (n *Nft) DeleteSystemRules(force, restoreExistingChains, logErrors bool) {
n.Lock()
defer n.Unlock()
if err := n.delRulesByKey(SystemRuleKey); err != nil {
log.Warning("error deleting interception rules: %s", err)
}
if restoreExistingChains {
n.restoreBackupChains()
}
if force {
n.DelSystemTables()
}
}
// AddSystemRule inserts a new rule.
func (n *Nft) AddSystemRule(rule *config.FwRule, chain *config.FwChain) (err4, err6 error) {
n.Lock()
defer n.Unlock()
exprList := []expr.Any{}
for _, expression := range rule.Expressions {
exprsOfRule := n.parseExpression(chain.Table, chain.Name, chain.Family, expression)
if exprsOfRule == nil {
return fmt.Errorf("%s invalid rule parameters: %v", rule.UUID, expression), nil
}
exprList = append(exprList, *exprsOfRule...)
}
if len(exprList) > 0 {
exprVerdict := exprs.NewExprVerdict(rule.Target, rule.TargetParameters)
if exprVerdict == nil {
return fmt.Errorf("%s invalid verdict %s %s", rule.UUID, rule.Target, rule.TargetParameters), nil
}
exprList = append(exprList, *exprVerdict...)
if err := n.InsertRule(chain.Name, chain.Table, chain.Family, rule.Position, &exprList); err != nil {
return err, nil
}
}
return nil, nil
}