2023-01-17 17:34:40 +01:00
package server
import (
2024-07-03 11:33:02 +02:00
"context"
2024-08-07 10:52:31 +02:00
"fmt"
2023-06-28 17:29:02 +02:00
"net/netip"
2024-08-07 10:52:31 +02:00
"reflect"
2023-05-19 11:42:25 +02:00
"testing"
2024-08-07 10:52:31 +02:00
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server/telemetry"
2023-05-19 11:42:25 +02:00
"github.com/stretchr/testify/require"
2023-06-28 17:29:02 +02:00
"github.com/netbirdio/netbird/dns"
2023-01-17 17:34:40 +01:00
"github.com/netbirdio/netbird/management/server/activity"
2024-03-27 18:48:48 +01:00
"github.com/netbirdio/netbird/management/server/group"
2023-11-28 13:45:26 +01:00
nbpeer "github.com/netbirdio/netbird/management/server/peer"
2023-01-17 17:34:40 +01:00
"github.com/netbirdio/netbird/management/server/status"
)
const (
dnsGroup1ID = "group1"
dnsGroup2ID = "group2"
dnsPeer1Key = "BhRPtynAAYRDy08+q4HTMsos8fs4plTP4NOSh7C1ry8="
dnsPeer2Key = "/yF0+vCfv+mRR5k0dca0TrGdO/oiNeAI58gToZm5NyI="
dnsAccountID = "testingAcc"
dnsAdminUserID = "testingAdminUser"
dnsRegularUserID = "testingRegularUser"
2023-06-28 17:29:02 +02:00
dnsNSGroup1 = "ns1"
2023-01-17 17:34:40 +01:00
)
func TestGetDNSSettings ( t * testing . T ) {
am , err := createDNSManager ( t )
if err != nil {
t . Error ( "failed to create account manager" )
}
account , err := initTestDNSAccount ( t , am )
if err != nil {
2024-05-16 18:28:37 +02:00
t . Fatal ( "failed to init testing account" )
2023-01-17 17:34:40 +01:00
}
2024-07-03 11:33:02 +02:00
dnsSettings , err := am . GetDNSSettings ( context . Background ( ) , account . Id , dnsAdminUserID )
2023-01-17 17:34:40 +01:00
if err != nil {
t . Fatalf ( "Got an error when trying to retrieve the DNS settings with an admin user, err: %s" , err )
}
if dnsSettings == nil {
t . Fatal ( "DNS settings for new accounts shouldn't return nil" )
}
2023-10-11 23:00:56 +02:00
account . DNSSettings = DNSSettings {
2023-01-17 17:34:40 +01:00
DisabledManagementGroups : [ ] string { group1ID } ,
}
2024-07-03 11:33:02 +02:00
err = am . Store . SaveAccount ( context . Background ( ) , account )
2023-01-17 17:34:40 +01:00
if err != nil {
t . Error ( "failed to save testing account with new DNS settings" )
}
2024-07-03 11:33:02 +02:00
dnsSettings , err = am . GetDNSSettings ( context . Background ( ) , account . Id , dnsAdminUserID )
2023-01-17 17:34:40 +01:00
if err != nil {
t . Errorf ( "Got an error when trying to retrieve the DNS settings with an admin user, err: %s" , err )
}
if len ( dnsSettings . DisabledManagementGroups ) != 1 {
t . Errorf ( "DNS settings should have one disabled mgmt group, groups: %s" , dnsSettings . DisabledManagementGroups )
}
2024-07-03 11:33:02 +02:00
_ , err = am . GetDNSSettings ( context . Background ( ) , account . Id , dnsRegularUserID )
2023-01-17 17:34:40 +01:00
if err == nil {
t . Errorf ( "An error should be returned when getting the DNS settings with a regular user" )
}
s , ok := status . FromError ( err )
if ! ok && s . Type ( ) != status . PermissionDenied {
t . Errorf ( "returned error should be Permission Denied, got err: %s" , err )
}
}
func TestSaveDNSSettings ( t * testing . T ) {
testCases := [ ] struct {
name string
userID string
inputSettings * DNSSettings
shouldFail bool
} {
{
name : "Saving As Admin Should Be OK" ,
userID : dnsAdminUserID ,
inputSettings : & DNSSettings {
DisabledManagementGroups : [ ] string { dnsGroup1ID } ,
} ,
} ,
{
name : "Should Not Update Settings As Regular User" ,
userID : dnsRegularUserID ,
inputSettings : & DNSSettings {
DisabledManagementGroups : [ ] string { dnsGroup1ID } ,
} ,
shouldFail : true ,
} ,
{
name : "Should Not Update Settings If Input is Nil" ,
userID : dnsAdminUserID ,
inputSettings : nil ,
shouldFail : true ,
} ,
{
name : "Should Not Update Settings If Group Is Invalid" ,
userID : dnsAdminUserID ,
inputSettings : & DNSSettings {
DisabledManagementGroups : [ ] string { "non-existing-group" } ,
} ,
shouldFail : true ,
} ,
}
for _ , testCase := range testCases {
t . Run ( testCase . name , func ( t * testing . T ) {
am , err := createDNSManager ( t )
if err != nil {
t . Error ( "failed to create account manager" )
}
account , err := initTestDNSAccount ( t , am )
if err != nil {
t . Error ( "failed to init testing account" )
}
2024-07-03 11:33:02 +02:00
err = am . SaveDNSSettings ( context . Background ( ) , account . Id , testCase . userID , testCase . inputSettings )
2023-01-17 17:34:40 +01:00
if err != nil {
if testCase . shouldFail {
return
}
t . Error ( err )
}
2024-07-03 11:33:02 +02:00
updatedAccount , err := am . Store . GetAccount ( context . Background ( ) , account . Id )
2023-01-17 17:34:40 +01:00
if err != nil {
t . Errorf ( "should be able to retrieve updated account, got err: %s" , err )
}
require . ElementsMatchf ( t , testCase . inputSettings . DisabledManagementGroups , updatedAccount . DNSSettings . DisabledManagementGroups ,
"resulting DNS settings should match input" )
} )
}
}
func TestGetNetworkMap_DNSConfigSync ( t * testing . T ) {
am , err := createDNSManager ( t )
if err != nil {
t . Error ( "failed to create account manager" )
}
account , err := initTestDNSAccount ( t , am )
if err != nil {
t . Error ( "failed to init testing account" )
}
2023-02-03 10:33:28 +01:00
peer1 , err := account . FindPeerByPubKey ( dnsPeer1Key )
if err != nil {
t . Error ( "failed to init testing account" )
}
peer2 , err := account . FindPeerByPubKey ( dnsPeer2Key )
if err != nil {
t . Error ( "failed to init testing account" )
}
2024-07-03 11:33:02 +02:00
newAccountDNSConfig , err := am . GetNetworkMap ( context . Background ( ) , peer1 . ID )
2023-01-17 17:34:40 +01:00
require . NoError ( t , err )
require . Len ( t , newAccountDNSConfig . DNSConfig . CustomZones , 1 , "default DNS config should have one custom zone for peers" )
require . True ( t , newAccountDNSConfig . DNSConfig . ServiceEnable , "default DNS config should have local DNS service enabled" )
2023-06-28 17:29:02 +02:00
require . Len ( t , newAccountDNSConfig . DNSConfig . NameServerGroups , 0 , "updated DNS config should have no nameserver groups since peer 1 is NS for the only existing NS group" )
2023-01-17 17:34:40 +01:00
dnsSettings := account . DNSSettings . Copy ( )
dnsSettings . DisabledManagementGroups = append ( dnsSettings . DisabledManagementGroups , dnsGroup1ID )
account . DNSSettings = dnsSettings
2024-07-03 11:33:02 +02:00
err = am . Store . SaveAccount ( context . Background ( ) , account )
2023-01-17 17:34:40 +01:00
require . NoError ( t , err )
2024-07-03 11:33:02 +02:00
updatedAccountDNSConfig , err := am . GetNetworkMap ( context . Background ( ) , peer1 . ID )
2023-01-17 17:34:40 +01:00
require . NoError ( t , err )
require . Len ( t , updatedAccountDNSConfig . DNSConfig . CustomZones , 0 , "updated DNS config should have no custom zone when peer belongs to a disabled group" )
require . False ( t , updatedAccountDNSConfig . DNSConfig . ServiceEnable , "updated DNS config should have local DNS service disabled when peer belongs to a disabled group" )
2024-07-03 11:33:02 +02:00
peer2AccountDNSConfig , err := am . GetNetworkMap ( context . Background ( ) , peer2 . ID )
2023-01-17 17:34:40 +01:00
require . NoError ( t , err )
require . Len ( t , peer2AccountDNSConfig . DNSConfig . CustomZones , 1 , "DNS config should have one custom zone for peers not in the disabled group" )
require . True ( t , peer2AccountDNSConfig . DNSConfig . ServiceEnable , "DNS config should have DNS service enabled for peers not in the disabled group" )
2023-06-28 17:29:02 +02:00
require . Len ( t , peer2AccountDNSConfig . DNSConfig . NameServerGroups , 1 , "updated DNS config should have 1 nameserver groups since peer 2 is part of the group All" )
2023-01-17 17:34:40 +01:00
}
func createDNSManager ( t * testing . T ) ( * DefaultAccountManager , error ) {
2023-11-10 16:33:13 +01:00
t . Helper ( )
2023-01-17 17:34:40 +01:00
store , err := createDNSStore ( t )
if err != nil {
return nil , err
}
eventStore := & activity . InMemoryEventStore { }
2024-08-07 10:52:31 +02:00
metrics , err := telemetry . NewDefaultAppMetrics ( context . Background ( ) )
require . NoError ( t , err )
return BuildManager ( context . Background ( ) , store , NewPeersUpdateManager ( nil ) , nil , "" , "netbird.test" , eventStore , nil , false , MocIntegratedValidator { } , metrics )
2023-01-17 17:34:40 +01:00
}
func createDNSStore ( t * testing . T ) ( Store , error ) {
2023-11-10 16:33:13 +01:00
t . Helper ( )
2023-01-17 17:34:40 +01:00
dataDir := t . TempDir ( )
2024-10-03 15:50:35 +02:00
store , cleanUp , err := NewTestStoreFromSqlite ( context . Background ( ) , "" , dataDir )
2023-01-17 17:34:40 +01:00
if err != nil {
return nil , err
}
2024-05-16 18:28:37 +02:00
t . Cleanup ( cleanUp )
2023-01-17 17:34:40 +01:00
return store , nil
}
func initTestDNSAccount ( t * testing . T , am * DefaultAccountManager ) ( * Account , error ) {
2023-11-10 16:33:13 +01:00
t . Helper ( )
2023-11-28 13:45:26 +01:00
peer1 := & nbpeer . Peer {
2023-01-17 17:34:40 +01:00
Key : dnsPeer1Key ,
Name : "test-host1@netbird.io" ,
2023-11-28 13:45:26 +01:00
Meta : nbpeer . PeerSystemMeta {
2023-01-17 17:34:40 +01:00
Hostname : "test-host1@netbird.io" ,
GoOS : "linux" ,
Kernel : "Linux" ,
Core : "21.04" ,
Platform : "x86_64" ,
OS : "Ubuntu" ,
WtVersion : "development" ,
UIVersion : "development" ,
} ,
DNSLabel : dnsPeer1Key ,
}
2023-11-28 13:45:26 +01:00
peer2 := & nbpeer . Peer {
2023-01-17 17:34:40 +01:00
Key : dnsPeer2Key ,
Name : "test-host2@netbird.io" ,
2023-11-28 13:45:26 +01:00
Meta : nbpeer . PeerSystemMeta {
2023-01-17 17:34:40 +01:00
Hostname : "test-host2@netbird.io" ,
GoOS : "linux" ,
Kernel : "Linux" ,
Core : "21.04" ,
Platform : "x86_64" ,
OS : "Ubuntu" ,
WtVersion : "development" ,
UIVersion : "development" ,
} ,
DNSLabel : dnsPeer2Key ,
}
domain := "example.com"
2024-07-03 11:33:02 +02:00
account := newAccountWithId ( context . Background ( ) , dnsAccountID , dnsAdminUserID , domain )
2023-01-17 17:34:40 +01:00
account . Users [ dnsRegularUserID ] = & User {
Id : dnsRegularUserID ,
Role : UserRoleUser ,
}
2024-07-03 11:33:02 +02:00
err := am . Store . SaveAccount ( context . Background ( ) , account )
2023-01-17 17:34:40 +01:00
if err != nil {
return nil , err
}
2024-07-03 11:33:02 +02:00
savedPeer1 , _ , _ , err := am . AddPeer ( context . Background ( ) , "" , dnsAdminUserID , peer1 )
2023-02-03 10:33:28 +01:00
if err != nil {
return nil , err
}
2024-07-03 11:33:02 +02:00
_ , _ , _ , err = am . AddPeer ( context . Background ( ) , "" , dnsAdminUserID , peer2 )
2023-02-03 10:33:28 +01:00
if err != nil {
return nil , err
}
2024-07-03 11:33:02 +02:00
account , err = am . Store . GetAccount ( context . Background ( ) , account . Id )
2023-02-03 10:33:28 +01:00
if err != nil {
return nil , err
}
peer1 , err = account . FindPeerByPubKey ( peer1 . Key )
if err != nil {
return nil , err
}
_ , err = account . FindPeerByPubKey ( peer2 . Key )
if err != nil {
return nil , err
}
2024-03-27 18:48:48 +01:00
newGroup1 := & group . Group {
2023-01-17 17:34:40 +01:00
ID : dnsGroup1ID ,
2023-02-03 10:33:28 +01:00
Peers : [ ] string { peer1 . ID } ,
2023-01-17 17:34:40 +01:00
Name : dnsGroup1ID ,
}
2024-03-27 18:48:48 +01:00
newGroup2 := & group . Group {
2023-01-17 17:34:40 +01:00
ID : dnsGroup2ID ,
Name : dnsGroup2ID ,
}
account . Groups [ newGroup1 . ID ] = newGroup1
account . Groups [ newGroup2 . ID ] = newGroup2
2023-06-28 17:29:02 +02:00
allGroup , err := account . GetGroupAll ( )
if err != nil {
return nil , err
}
account . NameServerGroups [ dnsNSGroup1 ] = & dns . NameServerGroup {
ID : dnsNSGroup1 ,
Name : "ns-group-1" ,
NameServers : [ ] dns . NameServer { {
IP : netip . MustParseAddr ( savedPeer1 . IP . String ( ) ) ,
NSType : dns . UDPNameServerType ,
Port : dns . DefaultDNSPort ,
} } ,
Primary : true ,
Enabled : true ,
Groups : [ ] string { allGroup . ID } ,
}
2024-07-03 11:33:02 +02:00
err = am . Store . SaveAccount ( context . Background ( ) , account )
2023-01-17 17:34:40 +01:00
if err != nil {
return nil , err
}
2024-07-03 11:33:02 +02:00
return am . Store . GetAccount ( context . Background ( ) , account . Id )
2023-01-17 17:34:40 +01:00
}
2024-08-07 10:52:31 +02:00
func generateTestData ( size int ) nbdns . Config {
config := nbdns . Config {
ServiceEnable : true ,
CustomZones : make ( [ ] nbdns . CustomZone , size ) ,
NameServerGroups : make ( [ ] * nbdns . NameServerGroup , size ) ,
}
for i := 0 ; i < size ; i ++ {
config . CustomZones [ i ] = nbdns . CustomZone {
Domain : fmt . Sprintf ( "domain%d.com" , i ) ,
Records : [ ] nbdns . SimpleRecord {
{
Name : fmt . Sprintf ( "record%d" , i ) ,
Type : 1 ,
Class : "IN" ,
TTL : 3600 ,
RData : "192.168.1.1" ,
} ,
} ,
}
config . NameServerGroups [ i ] = & nbdns . NameServerGroup {
ID : fmt . Sprintf ( "group%d" , i ) ,
Primary : i == 0 ,
Domains : [ ] string { fmt . Sprintf ( "domain%d.com" , i ) } ,
SearchDomainsEnabled : true ,
NameServers : [ ] nbdns . NameServer {
{
IP : netip . MustParseAddr ( "8.8.8.8" ) ,
Port : 53 ,
NSType : 1 ,
} ,
} ,
}
}
return config
}
func BenchmarkToProtocolDNSConfig ( b * testing . B ) {
sizes := [ ] int { 10 , 100 , 1000 }
for _ , size := range sizes {
testData := generateTestData ( size )
b . Run ( fmt . Sprintf ( "WithCache-Size%d" , size ) , func ( b * testing . B ) {
cache := & DNSConfigCache { }
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
toProtocolDNSConfig ( testData , cache )
}
} )
b . Run ( fmt . Sprintf ( "WithoutCache-Size%d" , size ) , func ( b * testing . B ) {
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
cache := & DNSConfigCache { }
toProtocolDNSConfig ( testData , cache )
}
} )
}
}
func TestToProtocolDNSConfigWithCache ( t * testing . T ) {
var cache DNSConfigCache
// Create two different configs
config1 := nbdns . Config {
ServiceEnable : true ,
CustomZones : [ ] nbdns . CustomZone {
{
Domain : "example.com" ,
Records : [ ] nbdns . SimpleRecord {
{ Name : "www" , Type : 1 , Class : "IN" , TTL : 300 , RData : "192.168.1.1" } ,
} ,
} ,
} ,
NameServerGroups : [ ] * nbdns . NameServerGroup {
{
ID : "group1" ,
Name : "Group 1" ,
NameServers : [ ] nbdns . NameServer {
{ IP : netip . MustParseAddr ( "8.8.8.8" ) , Port : 53 } ,
} ,
} ,
} ,
}
config2 := nbdns . Config {
ServiceEnable : true ,
CustomZones : [ ] nbdns . CustomZone {
{
Domain : "example.org" ,
Records : [ ] nbdns . SimpleRecord {
{ Name : "mail" , Type : 1 , Class : "IN" , TTL : 300 , RData : "192.168.1.2" } ,
} ,
} ,
} ,
NameServerGroups : [ ] * nbdns . NameServerGroup {
{
ID : "group2" ,
Name : "Group 2" ,
NameServers : [ ] nbdns . NameServer {
{ IP : netip . MustParseAddr ( "8.8.4.4" ) , Port : 53 } ,
} ,
} ,
} ,
}
// First run with config1
result1 := toProtocolDNSConfig ( config1 , & cache )
// Second run with config2
result2 := toProtocolDNSConfig ( config2 , & cache )
// Third run with config1 again
result3 := toProtocolDNSConfig ( config1 , & cache )
// Verify that result1 and result3 are identical
if ! reflect . DeepEqual ( result1 , result3 ) {
t . Errorf ( "Results are not identical when run with the same input. Expected %v, got %v" , result1 , result3 )
}
// Verify that result2 is different from result1 and result3
if reflect . DeepEqual ( result1 , result2 ) || reflect . DeepEqual ( result2 , result3 ) {
t . Errorf ( "Results should be different for different inputs" )
}
// Verify that the cache contains elements from both configs
if _ , exists := cache . GetCustomZone ( "example.com" ) ; ! exists {
t . Errorf ( "Cache should contain custom zone for example.com" )
}
if _ , exists := cache . GetCustomZone ( "example.org" ) ; ! exists {
t . Errorf ( "Cache should contain custom zone for example.org" )
}
if _ , exists := cache . GetNameServerGroup ( "group1" ) ; ! exists {
t . Errorf ( "Cache should contain name server group 'group1'" )
}
if _ , exists := cache . GetNameServerGroup ( "group2" ) ; ! exists {
t . Errorf ( "Cache should contain name server group 'group2'" )
}
}