mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-12 17:59:06 +02:00
Keep the DNS interception hooks installed when the firewall is disabled
This commit is contained in:
@@ -68,11 +68,11 @@ func NewFirewall(iface IFaceMapper, stateManager *statemanager.Manager, flowLogg
|
||||
case err == nil && !iface.IsUserspaceBind():
|
||||
// Nothing to do, fall through
|
||||
case err == nil && iface.IsUserspaceBind():
|
||||
// Native firewall handles packet filtering, but the userspace WireGuard bind
|
||||
// Native firewall handles packet filtering, but the userspace bind
|
||||
// needs a device filter for DNS interception hooks. Install a minimal
|
||||
// hooks-only filter that passes all traffic through to the kernel firewall.
|
||||
if err := iface.SetFilter(&uspfilter.HooksFilter{}); err != nil {
|
||||
log.Warnf("failed to set hooks filter, DNS via memory hooks will not work: %v", err)
|
||||
if err := InstallDNSHooksFilter(iface); err != nil {
|
||||
log.Errorf("failed to set hooks filter, DNS via memory hooks will not work: %v", err)
|
||||
}
|
||||
case err != nil && !iface.IsUserspaceBind():
|
||||
// Kernel cannot fall back to anything else, need to return error
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/client/firewall/uspfilter"
|
||||
)
|
||||
|
||||
// InstallDNSHooksFilter installs a device filter that carries nothing but the
|
||||
// DNS interception hooks, for setups that run no firewall manager. The
|
||||
// in-process resolver receives queries through hooks on the interface's device
|
||||
// filter, so without a filter it never sees a query; the filter passes all
|
||||
// other traffic through untouched.
|
||||
//
|
||||
// It is a no-op when the interface has no device filter to install on, which
|
||||
// is the case for a kernel bind.
|
||||
func InstallDNSHooksFilter(iface IFaceMapper) error {
|
||||
if !iface.IsUserspaceBind() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return iface.SetFilter(&uspfilter.HooksFilter{})
|
||||
}
|
||||
@@ -21,9 +21,10 @@ const (
|
||||
)
|
||||
|
||||
// HooksFilter is a minimal packet filter that only handles outbound DNS hooks.
|
||||
// It is installed on the WireGuard interface when the userspace bind is active
|
||||
// but a full firewall filter (Manager) is not needed because a native kernel
|
||||
// firewall (nftables/iptables) handles packet filtering.
|
||||
// It is installed on the interface when the userspace bind is active but a full
|
||||
// filter (Manager) is not: either because a native kernel firewall
|
||||
// (nftables/iptables) handles packet filtering, or because no firewall manager
|
||||
// runs at all.
|
||||
type HooksFilter struct {
|
||||
udpHook atomic.Pointer[common.PacketHook]
|
||||
tcpHook atomic.Pointer[common.PacketHook]
|
||||
|
||||
@@ -732,6 +732,14 @@ func (e *Engine) Start(netbirdConfig *mgmProto.NetbirdConfig, mgmtURL *url.URL)
|
||||
func (e *Engine) createFirewall() error {
|
||||
if e.config.DisableFirewall {
|
||||
log.Infof("firewall is disabled")
|
||||
|
||||
// The DNS hooks are not firewall rules. Without the filter that carries
|
||||
// them the resolver never receives a query, while the system is still
|
||||
// pointed at it.
|
||||
if err := firewall.InstallDNSHooksFilter(e.wgInterface); err != nil {
|
||||
log.Errorf("failed to install DNS hooks filter, DNS will not work: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/netbirdio/netbird/client/firewall/uspfilter"
|
||||
"github.com/netbirdio/netbird/client/iface/device"
|
||||
)
|
||||
|
||||
// A disabled firewall must still leave the DNS interception hooks in place on a
|
||||
// userspace bind: the in-process resolver receives queries through them, and the
|
||||
// system is pointed at that resolver either way.
|
||||
func TestCreateFirewallDisabledInstallsDNSHooksOnUserspaceBind(t *testing.T) {
|
||||
var installed device.PacketFilter
|
||||
iface := &MockWGIface{
|
||||
IsUserspaceBindFunc: func() bool { return true },
|
||||
SetFilterFunc: func(filter device.PacketFilter) error {
|
||||
installed = filter
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
engine := &Engine{
|
||||
config: &EngineConfig{DisableFirewall: true},
|
||||
wgInterface: iface,
|
||||
}
|
||||
|
||||
require.NoError(t, engine.createFirewall())
|
||||
assert.Nil(t, engine.firewall, "no firewall manager should be created")
|
||||
assert.IsType(t, &uspfilter.HooksFilter{}, installed)
|
||||
}
|
||||
|
||||
// A kernel bind has no device filter, so nothing should be installed on it.
|
||||
func TestCreateFirewallDisabledSkipsDNSHooksOnKernelBind(t *testing.T) {
|
||||
iface := &MockWGIface{
|
||||
IsUserspaceBindFunc: func() bool { return false },
|
||||
SetFilterFunc: func(device.PacketFilter) error {
|
||||
t.Error("SetFilter called for a kernel bind")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
engine := &Engine{
|
||||
config: &EngineConfig{DisableFirewall: true},
|
||||
wgInterface: iface,
|
||||
}
|
||||
|
||||
require.NoError(t, engine.createFirewall())
|
||||
assert.Nil(t, engine.firewall, "no firewall manager should be created")
|
||||
}
|
||||
Reference in New Issue
Block a user