From 609654eee7d441846021959bc386338b4696815a Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Wed, 4 Jun 2025 18:12:48 +0200 Subject: [PATCH] [client] Allow userspace local forwarding to internal interfaces if requested (#3884) --- client/firewall/uspfilter/forwarder/tcp.go | 4 ++-- client/firewall/uspfilter/forwarder/udp.go | 4 ++-- client/firewall/uspfilter/uspfilter.go | 23 +++++++++++++++------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/client/firewall/uspfilter/forwarder/tcp.go b/client/firewall/uspfilter/forwarder/tcp.go index 04b3ae233..64e54e293 100644 --- a/client/firewall/uspfilter/forwarder/tcp.go +++ b/client/firewall/uspfilter/forwarder/tcp.go @@ -111,12 +111,12 @@ func (f *Forwarder) proxyTCP(id stack.TransportEndpointID, inConn *gonet.TCPConn if errInToOut != nil { if !isClosedError(errInToOut) { - f.logger.Error("proxyTCP: copy error (in -> out): %v", errInToOut) + f.logger.Error("proxyTCP: copy error (in -> out) for %s: %v", epID(id), errInToOut) } } if errOutToIn != nil { if !isClosedError(errOutToIn) { - f.logger.Error("proxyTCP: copy error (out -> in): %v", errOutToIn) + f.logger.Error("proxyTCP: copy error (out -> in) for %s: %v", epID(id), errOutToIn) } } diff --git a/client/firewall/uspfilter/forwarder/udp.go b/client/firewall/uspfilter/forwarder/udp.go index cb88aa59a..f237a313d 100644 --- a/client/firewall/uspfilter/forwarder/udp.go +++ b/client/firewall/uspfilter/forwarder/udp.go @@ -250,10 +250,10 @@ func (f *Forwarder) proxyUDP(ctx context.Context, pConn *udpPacketConn, id stack wg.Wait() if outboundErr != nil && !isClosedError(outboundErr) { - f.logger.Error("proxyUDP: copy error (outbound->inbound): %v", outboundErr) + f.logger.Error("proxyUDP: copy error (outbound->inbound) for %s: %v", epID(id), outboundErr) } if inboundErr != nil && !isClosedError(inboundErr) { - f.logger.Error("proxyUDP: copy error (inbound->outbound): %v", inboundErr) + f.logger.Error("proxyUDP: copy error (inbound->outbound) for %s: %v", epID(id), inboundErr) } var rxPackets, txPackets uint64 diff --git a/client/firewall/uspfilter/uspfilter.go b/client/firewall/uspfilter/uspfilter.go index eede1ab13..c216bc302 100644 --- a/client/firewall/uspfilter/uspfilter.go +++ b/client/firewall/uspfilter/uspfilter.go @@ -39,8 +39,12 @@ const ( // EnvForceUserspaceRouter forces userspace routing even if native routing is available. EnvForceUserspaceRouter = "NB_FORCE_USERSPACE_ROUTER" - // EnvEnableNetstackLocalForwarding enables forwarding of local traffic to the native stack when running netstack - // Leaving this on by default introduces a security risk as sockets on listening on localhost only will be accessible + // EnvEnableLocalForwarding enables forwarding of local traffic to the native stack for internal (non-NetBird) interfaces. + // Default off as it might be security risk because sockets listening on localhost only will become accessible. + EnvEnableLocalForwarding = "NB_ENABLE_LOCAL_FORWARDING" + + // EnvEnableNetstackLocalForwarding is an alias for EnvEnableLocalForwarding. + // In netstack mode, it enables forwarding of local traffic to the native stack for all interfaces. EnvEnableNetstackLocalForwarding = "NB_ENABLE_NETSTACK_LOCAL_FORWARDING" ) @@ -147,6 +151,11 @@ func parseCreateEnv() (bool, bool) { if err != nil { log.Warnf("failed to parse %s: %v", EnvEnableNetstackLocalForwarding, err) } + } else if val := os.Getenv(EnvEnableLocalForwarding); val != "" { + enableLocalForwarding, err = strconv.ParseBool(val) + if err != nil { + log.Warnf("failed to parse %s: %v", EnvEnableLocalForwarding, err) + } } return disableConntrack, enableLocalForwarding @@ -779,9 +788,10 @@ func (m *Manager) handleLocalTraffic(d *decoder, srcIP, dstIP netip.Addr, packet return true } - // if running in netstack mode we need to pass this to the forwarder - if m.netstack && m.localForwarding { - return m.handleNetstackLocalTraffic(packetData) + // If requested we pass local traffic to internal interfaces to the forwarder. + // netstack doesn't have an interface to forward packets to the native stack so we always need to use the forwarder. + if m.localForwarding && (m.netstack || dstIP != m.wgIface.Address().IP) { + return m.handleForwardedLocalTraffic(packetData) } // track inbound packets to get the correct direction and session id for flows @@ -791,8 +801,7 @@ func (m *Manager) handleLocalTraffic(d *decoder, srcIP, dstIP netip.Addr, packet return false } -func (m *Manager) handleNetstackLocalTraffic(packetData []byte) bool { - +func (m *Manager) handleForwardedLocalTraffic(packetData []byte) bool { fwd := m.forwarder.Load() if fwd == nil { m.logger.Trace("Dropping local packet (forwarder not initialized)")