Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[client] Cleanup firewall state on startup #2768

Merged
merged 33 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2a1ec9b
Add state manager, migrate dns cleanup and add route cleanup
lixmal Oct 16, 2024
d4ef622
Add mobile dummies
lixmal Oct 18, 2024
c0adf17
Remove broken state files
lixmal Oct 18, 2024
a6cc9f2
Remove obsolete return
lixmal Oct 18, 2024
c4ac044
Fix log msg
lixmal Oct 18, 2024
631b8dc
Fix android build
lixmal Oct 18, 2024
57b350c
Fix some tests
lixmal Oct 18, 2024
db9e805
Fix linter
lixmal Oct 18, 2024
d1240fd
Fix dns test
lixmal Oct 18, 2024
9f6eb39
Remove obsolete return
lixmal Oct 18, 2024
79c7a83
Ignore go lint for permissions
lixmal Oct 18, 2024
17a2b04
Fix freebsd
lixmal Oct 18, 2024
21e224a
Remove route state on stop
lixmal Oct 18, 2024
3c1a1bc
Cleanup firewall rules on unclean shutdown
lixmal Oct 21, 2024
ecdd1f7
Fix tests
lixmal Oct 22, 2024
7c3dbb6
Persist nftables early as well
lixmal Oct 22, 2024
84d5e0f
Remove obsolete marshal methods
lixmal Oct 22, 2024
301979d
Exclude android
lixmal Oct 22, 2024
ab81b60
Remove ref.go
lixmal Oct 22, 2024
3302be5
Remove obsolete SetFunctions
lixmal Oct 22, 2024
c86a8de
Move build flag to correct place
lixmal Oct 22, 2024
f4e4eec
Fix copied mutex issue
lixmal Oct 22, 2024
d4cb34d
Add android flag to generic state
lixmal Oct 22, 2024
d3c1084
Fix removed import
lixmal Oct 22, 2024
80a0b72
Fix windows Reset method
lixmal Oct 22, 2024
e34e91b
Make state files Linux only
lixmal Oct 22, 2024
a80ad7f
Remove unused context
lixmal Oct 23, 2024
236c74f
Fix test
lixmal Oct 23, 2024
f2e48d3
Simplify route state
lixmal Oct 23, 2024
248f5e1
Reduce cognitive complexity
lixmal Oct 23, 2024
6fe6a90
Merge branch 'main' into cleanup-firewall
lixmal Oct 24, 2024
5a53cef
Fix regressions
lixmal Oct 24, 2024
f45ae2e
Fix more regressions
lixmal Oct 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading