Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion client/anonymize/anonymize.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (a *Anonymizer) AnonymizeIP(ip netip.Addr) netip.Addr {
ip.IsLinkLocalUnicast() ||
ip.IsLinkLocalMulticast() ||
ip.IsInterfaceLocalMulticast() ||
ip.IsPrivate() ||
(ip.Is4() && ip.IsPrivate()) ||
ip.IsUnspecified() ||
ip.IsMulticast() ||
isWellKnown(ip) ||
Expand Down
6 changes: 4 additions & 2 deletions client/iface/device/device_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,13 @@ func (t *TunDevice) Create() (WGConfigurer, error) {
if t.address.HasIPv6() {
nbiface6, err := luid.IPInterface(windows.AF_INET6)
if err != nil {
log.Warnf("failed to get IPv6 interface for MTU: %v", err)
log.Warnf("failed to get IPv6 interface for MTU, continuing v4-only: %v", err)
t.address.ClearIPv6()
} else {
nbiface6.NLMTU = uint32(t.mtu)
if err := nbiface6.Set(); err != nil {
log.Warnf("failed to set IPv6 interface MTU: %v", err)
log.Warnf("failed to set IPv6 interface MTU, continuing v4-only: %v", err)
t.address.ClearIPv6()
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions client/internal/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/netbirdio/netbird/client/internal/updater/installer"
nbstatus "github.com/netbirdio/netbird/client/status"
mgmProto "github.com/netbirdio/netbird/shared/management/proto"
"github.com/netbirdio/netbird/shared/netiputil"
"github.com/netbirdio/netbird/util"
)

Expand Down Expand Up @@ -1259,6 +1260,11 @@ func anonymizePeerConfig(config *mgmProto.PeerConfig, anonymizer *anonymize.Anon
config.Address = anonymizer.AnonymizeIP(addr).String()
}

if v6Prefix, err := netiputil.DecodePrefix(config.GetAddressV6()); err == nil {
anonV6 := anonymizer.AnonymizeIP(v6Prefix.Addr())
config.AddressV6 = netiputil.EncodePrefix(netip.PrefixFrom(anonV6, v6Prefix.Bits()))
}

anonymizeSSHConfig(config.SshConfig)

config.Dns = anonymizer.AnonymizeString(config.Dns)
Expand Down
26 changes: 18 additions & 8 deletions client/internal/debug/debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"bytes"
"encoding/json"
"net"
"net/netip"
"os"
"path/filepath"
"strings"
Expand All @@ -16,6 +17,7 @@ import (
"github.com/netbirdio/netbird/client/anonymize"
"github.com/netbirdio/netbird/client/configs"
mgmProto "github.com/netbirdio/netbird/shared/management/proto"
"github.com/netbirdio/netbird/shared/netiputil"
)

func TestAnonymizeStateFile(t *testing.T) {
Expand Down Expand Up @@ -168,7 +170,7 @@ func TestAnonymizeStateFile(t *testing.T) {
assert.Equal(t, "100.64.0.1", state["protected_ip"]) // Protected IP unchanged
assert.Equal(t, "8.8.8.8", state["well_known_ip"]) // Well-known IP unchanged
assert.NotEqual(t, "2001:db8::1", state["ipv6_addr"])
assert.Equal(t, "fd00::1", state["private_ipv6"]) // Private IPv6 unchanged
assert.NotEqual(t, "fd00::1", state["private_ipv6"]) // ULA IPv6 anonymized (global ID is a fingerprint)
assert.NotEqual(t, "test.example.com", state["domain"])
assert.True(t, strings.HasSuffix(state["domain"].(string), ".domain"))
assert.Equal(t, "device.netbird.cloud", state["netbird_domain"]) // Netbird domain unchanged
Expand Down Expand Up @@ -272,11 +274,13 @@ func mustMarshal(v any) json.RawMessage {
}

func TestAnonymizeNetworkMap(t *testing.T) {
origV6Prefix := netip.MustParsePrefix("2001:db8:abcd::5/64")
networkMap := &mgmProto.NetworkMap{
PeerConfig: &mgmProto.PeerConfig{
Address: "203.0.113.5",
Dns: "1.2.3.4",
Fqdn: "peer1.corp.example.com",
Address: "203.0.113.5",
AddressV6: netiputil.EncodePrefix(origV6Prefix),
Dns: "1.2.3.4",
Fqdn: "peer1.corp.example.com",
SshConfig: &mgmProto.SSHConfig{
SshPubKey: []byte("ssh-rsa AAAAB3NzaC1..."),
},
Expand Down Expand Up @@ -350,6 +354,12 @@ func TestAnonymizeNetworkMap(t *testing.T) {
require.NotEqual(t, "peer1.corp.example.com", peerCfg.Fqdn)
require.True(t, strings.HasSuffix(peerCfg.Fqdn, ".domain"))

// Verify AddressV6 is anonymized but preserves prefix length
anonV6Prefix, err := netiputil.DecodePrefix(peerCfg.AddressV6)
require.NoError(t, err)
assert.Equal(t, origV6Prefix.Bits(), anonV6Prefix.Bits(), "prefix length must be preserved")
assert.NotEqual(t, origV6Prefix.Addr(), anonV6Prefix.Addr(), "IPv6 address must be anonymized")

// Verify SSH key is replaced
require.Equal(t, []byte("ssh-placeholder-key"), peerCfg.SshConfig.SshPubKey)

Expand Down Expand Up @@ -784,8 +794,8 @@ COMMIT`
assert.NotContains(t, anonNftables, "2001:db8::")
assert.Contains(t, anonNftables, "2001:db8:ffff::") // Default anonymous v6 range

// ULA addresses in nftables should remain unchanged (private)
assert.Contains(t, anonNftables, "fd00:1234::1")
// ULA addresses in nftables should be anonymized (global ID is a fingerprint)
assert.NotContains(t, anonNftables, "fd00:1234::1")

// IPv6 nftables structure preserved
assert.Contains(t, anonNftables, "ip6 saddr")
Expand All @@ -794,8 +804,8 @@ COMMIT`
// Test ip6tables-save anonymization
anonIp6tablesSave := anonymizer.AnonymizeString(ip6tablesSave)

// ULA (private) IPv6 should remain unchanged
assert.Contains(t, anonIp6tablesSave, "fd00:1234::1/128")
// ULA IPv6 should be anonymized (global ID is a fingerprint)
assert.NotContains(t, anonIp6tablesSave, "fd00:1234::1/128")

// Public IPv6 addresses should be anonymized
assert.NotContains(t, anonIp6tablesSave, "2607:f8b0:4005::1")
Expand Down
10 changes: 10 additions & 0 deletions client/internal/dns/network_manager_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,24 @@ func (n *networkManagerDbusConfigurator) applyDNSConfig(config HostDNSConfig, st
connSettings.cleanDeprecatedSettings()

ipKey := networkManagerDbusIPv4Key
staleKey := networkManagerDbusIPv6Key
if config.ServerIP.Is6() {
ipKey = networkManagerDbusIPv6Key
staleKey = networkManagerDbusIPv4Key
raw := config.ServerIP.As16()
connSettings[ipKey][networkManagerDbusDNSKey] = dbus.MakeVariant([][]byte{raw[:]})
} else {
convDNSIP := binary.LittleEndian.Uint32(config.ServerIP.AsSlice())
connSettings[ipKey][networkManagerDbusDNSKey] = dbus.MakeVariant([]uint32{convDNSIP})
}

// Clear stale DNS settings from the opposite address family to avoid
// leftover entries if the server IP family changed.
if staleSettings, ok := connSettings[staleKey]; ok {
delete(staleSettings, networkManagerDbusDNSKey)
delete(staleSettings, networkManagerDbusDNSPriorityKey)
delete(staleSettings, networkManagerDbusDNSSearchKey)
}
var (
searchDomains []string
matchDomains []string
Expand Down
3 changes: 2 additions & 1 deletion client/internal/peer/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestAddPeer(t *testing.T) {
Expand Down Expand Up @@ -46,7 +47,7 @@ func TestUpdatePeerState(t *testing.T) {
ip := "10.10.10.10"
fqdn := "peer-a.netbird.local"
status := NewRecorder("https://mgm")
_ = status.AddPeer(key, fqdn, ip, "")
require.NoError(t, status.AddPeer(key, fqdn, ip, ""))

peerState := State{
PubKey: key,
Expand Down
8 changes: 3 additions & 5 deletions client/ssh/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,22 +321,20 @@ func (s *Server) Stop() error {
}
s.sshServer = nil
s.listener = nil
extraListeners := s.extraListeners
s.extraListeners = nil
s.mu.Unlock()

// Close outside the lock: session handlers need s.mu for unregisterSession.
if err := sshServer.Close(); err != nil {
log.Debugf("close SSH server: %v", err)
}

for _, ln := range s.extraListeners {
for _, ln := range extraListeners {
if err := ln.Close(); err != nil {
log.Debugf("close extra SSH listener: %v", err)
}
}
s.extraListeners = nil

s.sshServer = nil
s.listener = nil

s.mu.Lock()
maps.Clear(s.sessions)
Expand Down
Loading