-
-
Notifications
You must be signed in to change notification settings - Fork 539
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
201 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
package server | ||
|
||
import ( | ||
"math/bits" | ||
"net" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/netbirdio/netbird/client/anonymize" | ||
mgmProto "github.com/netbirdio/netbird/management/proto" | ||
) | ||
|
||
func TestAnonymizeNetworkMap(t *testing.T) { | ||
// Create test network map with sensitive data | ||
networkMap := &mgmProto.NetworkMap{ | ||
PeerConfig: &mgmProto.PeerConfig{ | ||
Address: "203.0.113.5", // Public IP that should be anonymized | ||
Dns: "dns.corp.example.com", | ||
Fqdn: "peer1.corp.example.com", | ||
SshConfig: &mgmProto.SSHConfig{ | ||
SshPubKey: []byte("ssh-rsa AAAAB3NzaC1..."), | ||
}, | ||
}, | ||
RemotePeers: []*mgmProto.RemotePeerConfig{ | ||
{ | ||
AllowedIps: []string{ | ||
"203.0.113.1/32", // Public IP that should be anonymized | ||
"2001:db8:1234::1/128", // Public IPv6 that should be anonymized | ||
"192.168.1.1/32", // Private IP that should be preserved | ||
"100.64.0.1/32", // CGNAT IP that should be preserved | ||
"10.0.0.1/32", // Private IP that should be preserved | ||
}, | ||
Fqdn: "peer2.corp.example.com", | ||
SshConfig: &mgmProto.SSHConfig{ | ||
SshPubKey: []byte("ssh-rsa AAAAB3NzaC2..."), | ||
}, | ||
}, | ||
}, | ||
Routes: []*mgmProto.Route{ | ||
{ | ||
Network: "198.51.100.0/24", // Public IP that should be anonymized | ||
Domains: []string{"prod.example.com", "staging.example.com"}, | ||
NetID: "net-123abc", | ||
}, | ||
}, | ||
DNSConfig: &mgmProto.DNSConfig{ | ||
NameServerGroups: []*mgmProto.NameServerGroup{ | ||
{ | ||
NameServers: []*mgmProto.NameServer{ | ||
{IP: "8.8.8.8"}, // Google DNS that should be preserved | ||
{IP: "1.1.1.1"}, // Cloudflare DNS that should be preserved | ||
{IP: "203.0.113.53"}, // Public DNS that should be anonymized | ||
}, | ||
Domains: []string{"example.com", "internal.example.com"}, | ||
}, | ||
}, | ||
CustomZones: []*mgmProto.CustomZone{ | ||
{ | ||
Domain: "custom.example.com", | ||
Records: []*mgmProto.SimpleRecord{ | ||
{ | ||
Name: "www.custom.example.com", | ||
Type: 1, // A record | ||
RData: "203.0.113.10", // Public IP that should be anonymized | ||
}, | ||
{ | ||
Name: "internal.custom.example.com", | ||
Type: 1, // A record | ||
RData: "192.168.1.10", // Private IP that should be preserved | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
FirewallRules: []*mgmProto.FirewallRule{ | ||
{ | ||
PeerIP: "203.0.113.100", // Public IP that should be anonymized | ||
}, | ||
{ | ||
PeerIP: "192.168.1.100", // Private IP that should be preserved | ||
}, | ||
}, | ||
RoutesFirewallRules: []*mgmProto.RouteFirewallRule{ | ||
{ | ||
SourceRanges: []string{"203.0.113.0/24"}, // Public IP that should be anonymized | ||
Destination: "198.51.100.0/24", // Public IP that should be anonymized | ||
}, | ||
{ | ||
SourceRanges: []string{"192.168.0.0/16"}, // Private IP that should be preserved | ||
Destination: "10.0.0.0/8", // Private IP that should be preserved | ||
}, | ||
}, | ||
} | ||
|
||
// Create anonymizer with test addresses | ||
anonymizer := anonymize.NewAnonymizer(anonymize.DefaultAddresses()) | ||
|
||
// Anonymize the network map | ||
err := anonymizeNetworkMap(networkMap, anonymizer) | ||
require.NoError(t, err) | ||
|
||
// Test PeerConfig anonymization | ||
peerCfg := networkMap.PeerConfig | ||
require.NotEqual(t, "203.0.113.5", peerCfg.Address, "Public IP should be anonymized") | ||
t.Logf("dns: %s, fqdn: %s", peerCfg.Dns, peerCfg.Fqdn) | ||
require.NotEqual(t, "dns.corp.example.com", peerCfg.Dns) | ||
require.NotEqual(t, "peer1.corp.example.com", peerCfg.Fqdn) | ||
require.True(t, strings.HasSuffix(peerCfg.Dns, ".domain")) | ||
require.True(t, strings.HasSuffix(peerCfg.Fqdn, ".domain")) | ||
require.Equal(t, []byte("ssh-placeholder-key"), peerCfg.SshConfig.SshPubKey) | ||
|
||
// Test RemotePeers anonymization | ||
remotePeer := networkMap.RemotePeers[0] | ||
|
||
// Check that public IPs are anonymized but private IPs are preserved | ||
for _, allowedIP := range remotePeer.AllowedIps { | ||
ip, _, err := net.ParseCIDR(allowedIP) | ||
require.NoError(t, err) | ||
|
||
if ip.IsPrivate() || isInCGNATRange(ip) { | ||
require.Contains(t, []string{ | ||
"192.168.1.1/32", | ||
"100.64.0.1/32", | ||
"10.0.0.1/32", | ||
}, allowedIP, "Private/CGNAT IP should be preserved") | ||
} else { | ||
require.NotContains(t, []string{ | ||
"203.0.113.1/32", | ||
"2001:db8:1234::1/128", | ||
}, allowedIP, "Public IP should be anonymized") | ||
} | ||
} | ||
|
||
// Test DNS config anonymization | ||
dnsConfig := networkMap.DNSConfig | ||
nameServers := dnsConfig.NameServerGroups[0].NameServers | ||
|
||
// Well-known DNS servers should be preserved | ||
require.Equal(t, "8.8.8.8", nameServers[0].IP) | ||
require.Equal(t, "1.1.1.1", nameServers[1].IP) | ||
|
||
// Public DNS server should be anonymized | ||
require.NotEqual(t, "203.0.113.53", nameServers[2].IP) | ||
|
||
// Test CustomZones anonymization | ||
customZone := dnsConfig.CustomZones[0] | ||
require.True(t, strings.HasSuffix(customZone.Domain, ".domain")) | ||
|
||
// Test records anonymization | ||
for _, record := range customZone.Records { | ||
require.True(t, strings.HasSuffix(record.Name, ".domain")) | ||
|
||
ip := net.ParseIP(record.RData) | ||
if ip != nil && !ip.IsPrivate() { | ||
require.NotEqual(t, "203.0.113.10", record.RData, "Public IP in record should be anonymized") | ||
} else if ip != nil && ip.IsPrivate() { | ||
require.Equal(t, "192.168.1.10", record.RData, "Private IP in record should be preserved") | ||
} | ||
} | ||
|
||
// Test FirewallRules anonymization | ||
require.NotEqual(t, "203.0.113.100", networkMap.FirewallRules[0].PeerIP, "Public IP should be anonymized") | ||
require.Equal(t, "192.168.1.100", networkMap.FirewallRules[1].PeerIP, "Private IP should be preserved") | ||
|
||
// Test RouteFirewallRules anonymization | ||
routeRule := networkMap.RoutesFirewallRules[0] | ||
require.NotEqual(t, "203.0.113.0/24", routeRule.SourceRanges[0], "Public IP range should be anonymized") | ||
require.NotEqual(t, "198.51.100.0/24", routeRule.Destination, "Public IP range should be anonymized") | ||
|
||
privateRouteRule := networkMap.RoutesFirewallRules[1] | ||
require.Equal(t, "192.168.0.0/16", privateRouteRule.SourceRanges[0], "Private IP range should be preserved") | ||
require.Equal(t, "10.0.0.0/8", privateRouteRule.Destination, "Private IP range should be preserved") | ||
|
||
} | ||
|
||
// Helper function to check if IP is in CGNAT range | ||
func isInCGNATRange(ip net.IP) bool { | ||
cgnat := net.IPNet{ | ||
IP: net.ParseIP("100.64.0.0"), | ||
Mask: net.CIDRMask(10, 32), | ||
} | ||
return cgnat.Contains(ip) | ||
} | ||
|
||
// Helper function to count netmask bits | ||
func countNetmaskBits(mask net.IPMask) int { | ||
Check failure on line 188 in client/server/debug_test.go GitHub Actions / lint (ubuntu-latest)
|
||
bitss := 0 | ||
for _, b := range mask { | ||
bitss += bits.OnesCount8(b) | ||
} | ||
return bitss | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters