Skip to content

Commit

Permalink
[client] Cleanup firewall state on startup (#2768)
Browse files Browse the repository at this point in the history
  • Loading branch information
lixmal authored Oct 24, 2024
1 parent 4e918e5 commit 8016710
Show file tree
Hide file tree
Showing 32 changed files with 728 additions and 307 deletions.
4 changes: 2 additions & 2 deletions client/firewall/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
package firewall

import (
"context"
"fmt"
"runtime"

log "github.com/sirupsen/logrus"

firewall "github.com/netbirdio/netbird/client/firewall/manager"
"github.com/netbirdio/netbird/client/firewall/uspfilter"
"github.com/netbirdio/netbird/client/internal/statemanager"
)

// NewFirewall creates a firewall manager instance
func NewFirewall(context context.Context, iface IFaceMapper) (firewall.Manager, error) {
func NewFirewall(iface IFaceMapper, _ *statemanager.Manager) (firewall.Manager, error) {
if !iface.IsUserspaceBind() {
return nil, fmt.Errorf("not implemented for this OS: %s", runtime.GOOS)
}
Expand Down
82 changes: 50 additions & 32 deletions client/firewall/create_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package firewall

import (
"context"
"fmt"
"os"

Expand All @@ -15,6 +14,7 @@ import (
firewall "github.com/netbirdio/netbird/client/firewall/manager"
nbnftables "github.com/netbirdio/netbird/client/firewall/nftables"
"github.com/netbirdio/netbird/client/firewall/uspfilter"
"github.com/netbirdio/netbird/client/internal/statemanager"
)

const (
Expand All @@ -32,54 +32,72 @@ const SKIP_NFTABLES_ENV = "NB_SKIP_NFTABLES_CHECK"
// FWType is the type for the firewall type
type FWType int

func NewFirewall(context context.Context, iface IFaceMapper) (firewall.Manager, error) {
func NewFirewall(iface IFaceMapper, stateManager *statemanager.Manager) (firewall.Manager, error) {
// on the linux system we try to user nftables or iptables
// in any case, because we need to allow netbird interface traffic
// so we use AllowNetbird traffic from these firewall managers
// for the userspace packet filtering firewall
var fm firewall.Manager
var errFw error
fm, errFw := createNativeFirewall(iface)

if fm != nil {
if err := fm.Init(stateManager); err != nil {
log.Errorf("failed to init nftables manager: %s", err)
}
}

if iface.IsUserspaceBind() {
return createUserspaceFirewall(iface, fm, errFw)
}

return fm, errFw
}

func createNativeFirewall(iface IFaceMapper) (firewall.Manager, error) {
switch check() {
case IPTABLES:
log.Info("creating an iptables firewall manager")
fm, errFw = nbiptables.Create(context, iface)
if errFw != nil {
log.Errorf("failed to create iptables manager: %s", errFw)
}
return createIptablesFirewall(iface)
case NFTABLES:
log.Info("creating an nftables firewall manager")
fm, errFw = nbnftables.Create(context, iface)
if errFw != nil {
log.Errorf("failed to create nftables manager: %s", errFw)
}
return createNftablesFirewall(iface)
default:
errFw = fmt.Errorf("no firewall manager found")
log.Info("no firewall manager found, trying to use userspace packet filtering firewall")
return nil, fmt.Errorf("no firewall manager found")
}
}

if iface.IsUserspaceBind() {
var errUsp error
if errFw == nil {
fm, errUsp = uspfilter.CreateWithNativeFirewall(iface, fm)
} else {
fm, errUsp = uspfilter.Create(iface)
}
if errUsp != nil {
log.Debugf("failed to create userspace filtering firewall: %s", errUsp)
return nil, errUsp
}
func createIptablesFirewall(iface IFaceMapper) (firewall.Manager, error) {
log.Info("creating an iptables firewall manager")
fm, err := nbiptables.Create(iface)
if err != nil {
log.Errorf("failed to create iptables manager: %s", err)
}
return fm, err
}

if err := fm.AllowNetbird(); err != nil {
log.Errorf("failed to allow netbird interface traffic: %v", err)
}
return fm, nil
func createNftablesFirewall(iface IFaceMapper) (firewall.Manager, error) {
log.Info("creating an nftables firewall manager")
fm, err := nbnftables.Create(iface)
if err != nil {
log.Errorf("failed to create nftables manager: %s", err)
}
return fm, err
}

if errFw != nil {
return nil, errFw
func createUserspaceFirewall(iface IFaceMapper, fm firewall.Manager, errFw error) (firewall.Manager, error) {
var errUsp error
if errFw == nil {
fm, errUsp = uspfilter.CreateWithNativeFirewall(iface, fm)
} else {
fm, errUsp = uspfilter.Create(iface)
}

if errUsp != nil {
log.Debugf("failed to create userspace filtering firewall: %s", errUsp)
return nil, errUsp
}

if err := fm.AllowNetbird(); err != nil {
log.Errorf("failed to allow netbird interface traffic: %v", err)
}
return fm, nil
}

Expand Down
79 changes: 63 additions & 16 deletions client/firewall/iptables/acl_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
log "github.com/sirupsen/logrus"

firewall "github.com/netbirdio/netbird/client/firewall/manager"
"github.com/netbirdio/netbird/client/internal/statemanager"
nbnet "github.com/netbirdio/netbird/util/net"
)

Expand All @@ -22,6 +23,8 @@ const (
chainNameOutputRules = "NETBIRD-ACL-OUTPUT"
)

type aclEntries map[string][][]string

type entry struct {
spec []string
position int
Expand All @@ -32,9 +35,11 @@ type aclManager struct {
wgIface iFaceMapper
routingFwChainName string

entries map[string][][]string
entries aclEntries
optionalEntries map[string][]entry
ipsetStore *ipsetStore

stateManager *statemanager.Manager
}

func newAclManager(iptablesClient *iptables.IPTables, wgIface iFaceMapper, routingFwChainName string) (*aclManager, error) {
Expand All @@ -48,24 +53,30 @@ func newAclManager(iptablesClient *iptables.IPTables, wgIface iFaceMapper, routi
ipsetStore: newIpsetStore(),
}

err := ipset.Init()
if err != nil {
return nil, fmt.Errorf("failed to init ipset: %w", err)
if err := ipset.Init(); err != nil {
return nil, fmt.Errorf("init ipset: %w", err)
}

return m, nil
}

func (m *aclManager) init(stateManager *statemanager.Manager) error {
m.stateManager = stateManager

m.seedInitialEntries()
m.seedInitialOptionalEntries()

err = m.cleanChains()
if err != nil {
return nil, err
if err := m.cleanChains(); err != nil {
return fmt.Errorf("clean chains: %w", err)
}

err = m.createDefaultChains()
if err != nil {
return nil, err
if err := m.createDefaultChains(); err != nil {
return fmt.Errorf("create default chains: %w", err)
}
return m, nil

m.updateState()

return nil
}

func (m *aclManager) AddPeerFiltering(
Expand Down Expand Up @@ -146,6 +157,8 @@ func (m *aclManager) AddPeerFiltering(
chain: chain,
}

m.updateState()

return []firewall.Rule{rule}, nil
}

Expand Down Expand Up @@ -180,15 +193,23 @@ func (m *aclManager) DeletePeerRule(rule firewall.Rule) error {
}
}

err := m.iptablesClient.Delete(tableName, r.chain, r.specs...)
if err != nil {
log.Debugf("failed to delete rule, %s, %v: %s", r.chain, r.specs, err)
if err := m.iptablesClient.Delete(tableName, r.chain, r.specs...); err != nil {
return fmt.Errorf("failed to delete rule: %s, %v: %w", r.chain, r.specs, err)
}
return err

m.updateState()

return nil
}

func (m *aclManager) Reset() error {
return m.cleanChains()
if err := m.cleanChains(); err != nil {
return fmt.Errorf("clean chains: %w", err)
}

m.updateState()

return nil
}

// todo write less destructive cleanup mechanism
Expand Down Expand Up @@ -348,6 +369,32 @@ func (m *aclManager) appendToEntries(chainName string, spec []string) {
m.entries[chainName] = append(m.entries[chainName], spec)
}

func (m *aclManager) updateState() {
if m.stateManager == nil {
return
}

var currentState *ShutdownState
if existing := m.stateManager.GetState(currentState); existing != nil {
if existingState, ok := existing.(*ShutdownState); ok {
currentState = existingState
}
}
if currentState == nil {
currentState = &ShutdownState{}
}

currentState.Lock()
defer currentState.Unlock()

currentState.ACLEntries = m.entries
currentState.ACLIPsetStore = m.ipsetStore

if err := m.stateManager.UpdateState(currentState); err != nil {
log.Errorf("failed to update state: %v", err)
}
}

// filterRuleSpecs returns the specs of a filtering rule
func filterRuleSpecs(
ip net.IP, protocol string, sPort, dPort string, direction firewall.RuleDirection, action firewall.Action, ipsetName string,
Expand Down
Loading

0 comments on commit 8016710

Please sign in to comment.