From bc0e61b6d02db4c3b71bff87cc4530218976057d Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Fri, 11 Nov 2022 12:20:48 +0100 Subject: [PATCH 01/48] configure darwin split domain resolution --- client/internal/dns/host_darwin.go | 76 ++++++++++++++++++++++++++++++ client/internal/dns/local.go | 13 ++--- client/internal/dns/server.go | 25 +++++++++- 3 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 client/internal/dns/host_darwin.go diff --git a/client/internal/dns/host_darwin.go b/client/internal/dns/host_darwin.go new file mode 100644 index 00000000000..108ba74c0d5 --- /dev/null +++ b/client/internal/dns/host_darwin.go @@ -0,0 +1,76 @@ +package dns + +import ( + "bytes" + "fmt" + "github.com/miekg/dns" + log "github.com/sirupsen/logrus" + "os" +) + +const ( + darwinResolverContent = "domain %s\nnameserver %s\nport %d" + darwinSearchDomainContent = "search %s" + fileSuffix = "netbird" + searchFilePrefix = "search." + fileSuffix + resolverPath = "/etc/resolver" +) + +func applySplitDNS(domains []string) { + err := createResolverPath() + if err != nil { + log.Debugf("got an error creating resolver path: %s", err) + } + for _, domain := range domains { + log.Debugf("creating file for domain: %s", domain) + content := fmt.Sprintf(darwinResolverContent, domain, "127.0.0.1", port) + fileName := buildPath(domain) + writeDNSConfig(content, fileName, 0755) + } +} + +func removeSplitDNS(domainsToRemove []string) { + for _, toRemove := range domainsToRemove { + fileName := buildPath(toRemove) + log.Debugf("removing file %s for domain %s", fileName, toRemove) + _, err := os.Stat(fileName) + if err == nil { + err = os.RemoveAll(fileName) + if err != nil { + log.Debugf("got an error while removing resolver dns file %s for domain %s", fileName, toRemove) + } + } + } +} + +func addSearchDomain(domain string) { + content := fmt.Sprintf(darwinSearchDomainContent, domain) + fileName := buildPath(searchFilePrefix) + writeDNSConfig(content, fileName, 0755) +} + +func writeDNSConfig(content, fileName string, permissions os.FileMode) { + err := createResolverPath() + if err != nil { + log.Debugf("got an error creating resolver path: %s", err) + } + log.Debugf("creating file %s", fileName) + var buf bytes.Buffer + buf.WriteString(content) + err = os.WriteFile(fileName, buf.Bytes(), permissions) + if err != nil { + log.Debugf("got an creating resolver file %s err: %s", fileName, err) + } +} + +func buildPath(domain string) string { + return resolverPath + "/" + dns.Fqdn(domain) + fileSuffix +} + +func createResolverPath() error { + err := os.MkdirAll(resolverPath, 0755) + if err != nil { + return err + } + return nil +} diff --git a/client/internal/dns/local.go b/client/internal/dns/local.go index 741ab97b433..9f1a38ac80f 100644 --- a/client/internal/dns/local.go +++ b/client/internal/dns/local.go @@ -14,16 +14,13 @@ type localResolver struct { // ServeDNS handles a DNS request func (d *localResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { - log.Tracef("received question: %#v\n", r.Question[0]) - response := d.lookupRecord(r) - if response == nil { - log.Debugf("got empty response for question: %#v\n", r.Question[0]) - return - } - + log.Debugf("received question: %#v\n", r.Question[0]) replyMessage := &dns.Msg{} replyMessage.SetReply(r) - replyMessage.Answer = append(replyMessage.Answer, response) + response := d.lookupRecord(r) + if response != nil { + replyMessage.Answer = append(replyMessage.Answer, response) + } err := w.WriteMsg(replyMessage) if err != nil { diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 67f3788ea09..946d0b83b8b 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -86,8 +86,17 @@ func (s *DefaultServer) setListenerStatus(running bool) { // Stop stops the server func (s *DefaultServer) Stop() { + s.mux.Lock() + defer s.mux.Unlock() s.stop() + var domainsToRemove []string + for domain := range s.dnsMuxMap { + domainsToRemove = append(domainsToRemove, domain) + } + domainsToRemove = append(domainsToRemove, searchFilePrefix) + removeSplitDNS(domainsToRemove) + err := s.stopListener() if err != nil { log.Error(err) @@ -144,9 +153,18 @@ func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) erro } muxUpdates := append(localMuxUpdates, upstreamMuxUpdates...) + var domains []string + for _, u := range muxUpdates { + domains = append(domains, u.domain) + } - s.updateMux(muxUpdates) + domainsToRemove := s.updateMux(muxUpdates) s.updateLocalResolver(localRecords) + removeSplitDNS(domainsToRemove) + applySplitDNS(domains) + for _, localMuxUpdate := range localMuxUpdates { + addSearchDomain(localMuxUpdate.domain) + } s.updateSerial = serial @@ -226,7 +244,7 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam return muxUpdates, nil } -func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) { +func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) []string { muxUpdateMap := make(registrationMap) for _, update := range muxUpdates { @@ -234,14 +252,17 @@ func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) { muxUpdateMap[update.domain] = struct{}{} } + var removed []string for key := range s.dnsMuxMap { _, found := muxUpdateMap[key] if !found { s.deregisterMux(key) + removed = append(removed, key) } } s.dnsMuxMap = muxUpdateMap + return removed } func (s *DefaultServer) updateLocalResolver(update map[string]nbdns.SimpleRecord) { From 220e9eb26690c91ed8d8a17f25cdd8e5361b18b5 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sat, 12 Nov 2022 11:37:21 +0100 Subject: [PATCH 02/48] check if answer is empty --- client/internal/dns/local_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/internal/dns/local_test.go b/client/internal/dns/local_test.go index 79a57881b5a..db69d9ad86d 100644 --- a/client/internal/dns/local_test.go +++ b/client/internal/dns/local_test.go @@ -64,7 +64,7 @@ func TestLocalResolver_ServeDNS(t *testing.T) { resolver.ServeDNS(responseWriter, testCase.inputMSG) - if responseMSG == nil { + if responseMSG == nil || len(responseMSG.Answer) == 0 { if testCase.responseShouldBeNil { return } From 25ae67b13d99b20791c2e91e34d1a2cb50ba8ebd Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 13 Nov 2022 14:59:31 +0100 Subject: [PATCH 03/48] pass wireguard interface to dnsserver --- client/internal/dns/server_test.go | 18 ++++++++++++++---- client/internal/dns/upstream.go | 2 +- client/internal/engine.go | 3 ++- client/internal/engine_test.go | 2 ++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index 6bbfef507cd..7ef0094f65a 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/iface" "net" "net/netip" "os" @@ -188,15 +189,19 @@ func TestUpdateDNSServer(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { + wgInterface, err := iface.NewWGIFace("utun10000", "127.0.0.1/32", iface.DefaultMTU) + if err != nil { + t.Error(err) + } ctx := context.Background() - dnsServer := NewDefaultServer(ctx) + dnsServer := NewDefaultServer(ctx, wgInterface) dnsServer.dnsMuxMap = testCase.initUpstreamMap dnsServer.localResolver.registeredMap = testCase.initLocalMap dnsServer.updateSerial = testCase.initSerial dnsServer.listenerIsRunning = true - err := dnsServer.UpdateDNSServer(testCase.inputSerial, testCase.inputUpdate) + err = dnsServer.UpdateDNSServer(testCase.inputSerial, testCase.inputUpdate) if err != nil { if testCase.shouldFail { return @@ -231,7 +236,12 @@ func TestUpdateDNSServer(t *testing.T) { func TestDNSServerStartStop(t *testing.T) { ctx := context.Background() - dnsServer := NewDefaultServer(ctx) + wgInterface, err := iface.NewWGIFace("utun10000", "127.0.0.1/32", iface.DefaultMTU) + if err != nil { + t.Error(err) + } + + dnsServer := NewDefaultServer(ctx, wgInterface) if runtime.GOOS == "windows" && os.Getenv("CI") == "true" { // todo review why this test is not working only on github actions workflows t.Skip("skipping test in Windows CI workflows.") @@ -239,7 +249,7 @@ func TestDNSServerStartStop(t *testing.T) { dnsServer.Start() - err := dnsServer.localResolver.registerRecord(zoneRecords[0]) + err = dnsServer.localResolver.registerRecord(zoneRecords[0]) if err != nil { t.Error(err) } diff --git a/client/internal/dns/upstream.go b/client/internal/dns/upstream.go index fcc8bc68582..e2e61203c38 100644 --- a/client/internal/dns/upstream.go +++ b/client/internal/dns/upstream.go @@ -21,7 +21,7 @@ type upstreamResolver struct { // ServeDNS handles a DNS request func (u *upstreamResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { - log.Tracef("received an upstream question: %#v", r.Question[0]) + log.Debugf("received an upstream question: %#v", r.Question[0]) select { case <-u.parentCTX.Done(): diff --git a/client/internal/engine.go b/client/internal/engine.go index 7f7e52de5e2..2e2a11912be 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -135,7 +135,6 @@ func NewEngine( networkSerial: 0, sshServerFunc: nbssh.DefaultSSHServer, statusRecorder: statusRecorder, - dnsServer: dns.NewDefaultServer(ctx), } } @@ -252,6 +251,8 @@ func (e *Engine) Start() error { e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder) + e.dnsServer = dns.NewDefaultServer(e.ctx, e.wgInterface) + e.receiveSignalEvents() e.receiveManagementEvents() diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 56d9eb66f69..9b291318739 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -202,6 +202,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) { }, nbstatus.NewRecorder()) engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", iface.DefaultMTU) engine.routeManager = routemanager.NewManager(ctx, key.PublicKey().String(), engine.wgInterface, engine.statusRecorder) + engine.dnsServer = dns.NewDefaultServer(ctx, engine.wgInterface) type testCase struct { name string @@ -551,6 +552,7 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) { } engine.routeManager = mockRouteManager + engine.dnsServer = &dns.MockServer{} defer func() { exitErr := engine.Stop() From 345509831d826afa26589d75c42e24865f7dc8d7 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 13 Nov 2022 14:59:51 +0100 Subject: [PATCH 04/48] add macos host manager --- client/internal/dns/host.go | 8 + client/internal/dns/host_darwin.go | 245 ++++++++++++++++++++++++----- client/internal/dns/server.go | 54 +++++-- 3 files changed, 253 insertions(+), 54 deletions(-) create mode 100644 client/internal/dns/host.go diff --git a/client/internal/dns/host.go b/client/internal/dns/host.go new file mode 100644 index 00000000000..cd7578c8229 --- /dev/null +++ b/client/internal/dns/host.go @@ -0,0 +1,8 @@ +package dns + +type hostManager interface { + applyDNSSettings(domains []string, ip string, port int) error + addSearchDomain(domain string, ip string, port int) error + removeDomainSettings(domains []string) error + removeDNSSettings() error +} diff --git a/client/internal/dns/host_darwin.go b/client/internal/dns/host_darwin.go index 108ba74c0d5..f3411eb83d3 100644 --- a/client/internal/dns/host_darwin.go +++ b/client/internal/dns/host_darwin.go @@ -1,76 +1,239 @@ package dns import ( + "bufio" "bytes" "fmt" - "github.com/miekg/dns" + nbdns "github.com/netbirdio/netbird/dns" log "github.com/sirupsen/logrus" - "os" + "os/exec" + "strconv" + "strings" ) const ( - darwinResolverContent = "domain %s\nnameserver %s\nport %d" - darwinSearchDomainContent = "search %s" - fileSuffix = "netbird" - searchFilePrefix = "search." + fileSuffix - resolverPath = "/etc/resolver" + netbirdDNSStateKeyFormat = "State:/Network/Service/NetBird-%s/DNS" + globalIPv4State = "State:/Network/Global/IPv4" + primaryServiceSetupKeyFormat = "Setup:/Network/Service/%s/DNS" + keyDomainName = "DomainName" + keySupplementalMatchDomains = "SupplementalMatchDomains" + keySupplementalMatchDomainsNoSearch = "SupplementalMatchDomainsNoSearch" + keyServerAddresses = "ServerAddresses" + ServerPort = "ServerPort" + arraySymbol = "* " + digitSymbol = "# " + scutilPath = "/usr/sbin/scutil" + searchSuffix = "Search" ) -func applySplitDNS(domains []string) { - err := createResolverPath() - if err != nil { - log.Debugf("got an error creating resolver path: %s", err) - } - for _, domain := range domains { - log.Debugf("creating file for domain: %s", domain) - content := fmt.Sprintf(darwinResolverContent, domain, "127.0.0.1", port) - fileName := buildPath(domain) - writeDNSConfig(content, fileName, 0755) +type systemConfigurator struct { + primaryServiceID string + createdKeys map[string]struct{} +} + +func newHostManager() hostManager { + return &systemConfigurator{ + createdKeys: make(map[string]struct{}), } } -func removeSplitDNS(domainsToRemove []string) { - for _, toRemove := range domainsToRemove { - fileName := buildPath(toRemove) - log.Debugf("removing file %s for domain %s", fileName, toRemove) - _, err := os.Stat(fileName) - if err == nil { - err = os.RemoveAll(fileName) +func (s *systemConfigurator) applyDNSSettings(domains []string, ip string, port int) error { + var err error + for _, domain := range domains { + if domain == nbdns.RootZone || domain == "" { + err = s.addDNSSetupForAll(ip, port) if err != nil { - log.Debugf("got an error while removing resolver dns file %s for domain %s", fileName, toRemove) + log.Error(err) } + continue + } + err = s.addDNSStateForDomain(domain, ip, port) + if err != nil { + log.Error(err) } } + return nil } -func addSearchDomain(domain string) { - content := fmt.Sprintf(darwinSearchDomainContent, domain) - fileName := buildPath(searchFilePrefix) - writeDNSConfig(content, fileName, 0755) +func (s *systemConfigurator) addSearchDomain(domain string, ip string, port int) error { + key := getKeyWithInput(netbirdDNSStateKeyFormat, domain+searchSuffix) + err := s.addDNSState(key, domain, ip, port, true) + if err != nil { + return err + } + + s.createdKeys[key] = struct{}{} + + return nil } -func writeDNSConfig(content, fileName string, permissions os.FileMode) { - err := createResolverPath() +func (s *systemConfigurator) removeDNSSettings() error { + lines := "" + for key := range s.createdKeys { + lines += buildRemoveKeyOperation(key) + } + if s.primaryServiceID != "" { + lines += buildRemoveKeyOperation(getKeyWithInput(primaryServiceSetupKeyFormat, s.primaryServiceID)) + } + _, err := runSystemConfigCommand(wrapCommand(lines)) if err != nil { - log.Debugf("got an error creating resolver path: %s", err) + log.Errorf("got an error while cleaning the system configuration: %s", err) + return err + } + + return nil +} + +func (s *systemConfigurator) removeDomainSettings(domains []string) error { + var err error + for _, domain := range domains { + if domain == nbdns.RootZone || domain == "" { + if s.primaryServiceID != "" { + err = removeKeyFromSystemConfig(getKeyWithInput(primaryServiceSetupKeyFormat, s.primaryServiceID)) + if err != nil { + log.Errorf("unable to remove primary service configuration, got error: %s", err) + continue + } + s.primaryServiceID = "" + } + continue + } + + key := getKeyWithInput(netbirdDNSStateKeyFormat, domain) + err = removeKeyFromSystemConfig(key) + if err != nil { + log.Errorf("unable to remove system configuration for domain %s and key %s", domain, key) + continue + } + + delete(s.createdKeys, key) } - log.Debugf("creating file %s", fileName) - var buf bytes.Buffer - buf.WriteString(content) - err = os.WriteFile(fileName, buf.Bytes(), permissions) + return nil +} + +func removeKeyFromSystemConfig(key string) error { + line := buildRemoveKeyOperation(key) + _, err := runSystemConfigCommand(wrapCommand(line)) if err != nil { - log.Debugf("got an creating resolver file %s err: %s", fileName, err) + return err } + return nil } -func buildPath(domain string) string { - return resolverPath + "/" + dns.Fqdn(domain) + fileSuffix +func (s *systemConfigurator) addDNSStateForDomain(domain, dnsServer string, port int) error { + key := getKeyWithInput(netbirdDNSStateKeyFormat, domain) + err := s.addDNSState(key, domain, dnsServer, port, false) + if err != nil { + return err + } + + s.createdKeys[key] = struct{}{} + + return nil } -func createResolverPath() error { - err := os.MkdirAll(resolverPath, 0755) +func (s *systemConfigurator) addDNSState(state, domain, dnsServer string, port int, enableSearch bool) error { + noSearch := "1" + if enableSearch { + noSearch = "0" + } + lines := buildAddCommandLine(keyDomainName, domain) + lines += buildAddCommandLine(keySupplementalMatchDomains, arraySymbol+domain) + lines += buildAddCommandLine(keySupplementalMatchDomainsNoSearch, digitSymbol+noSearch) + lines += buildAddCommandLine(keyServerAddresses, arraySymbol+dnsServer) + lines += buildAddCommandLine(ServerPort, digitSymbol+strconv.Itoa(port)) + + addDomainCommand := buildCreateStateWithOperation(state, lines) + stdinCommands := wrapCommand(addDomainCommand) + + _, err := runSystemConfigCommand(stdinCommands) + if err != nil { + return fmt.Errorf("got error while applying dns state for domain %s, error: %s", domain, err) + } + return nil +} + +func (s *systemConfigurator) addDNSSetupForAll(dnsServer string, port int) error { + primaryServiceKey := s.getPrimaryService() + if primaryServiceKey == "" { + return fmt.Errorf("couldn't find the primary service key") + } + + err := s.addDNSSetup(getKeyWithInput(primaryServiceSetupKeyFormat, primaryServiceKey), dnsServer, port) if err != nil { return err } + + s.primaryServiceID = primaryServiceKey return nil } + +func (s *systemConfigurator) getPrimaryService() string { + line := buildCommandLine("show", globalIPv4State, "") + stdinCommands := wrapCommand(line) + b, err := runSystemConfigCommand(stdinCommands) + if err != nil { + log.Error("got error while sending the command: ", err) + return "" + } + fmt.Printf("original command \n%s\ncommand out: %s\n,err: %v\n", stdinCommands, string(b), err) + scanner := bufio.NewScanner(bytes.NewReader(b)) + for scanner.Scan() { + text := scanner.Text() + if strings.Contains(text, "PrimaryService") { + return strings.TrimSpace(strings.Split(text, ":")[1]) + } + } + return "" +} + +func (s *systemConfigurator) addDNSSetup(setupKey, dnsServer string, port int) error { + lines := buildAddCommandLine(keySupplementalMatchDomainsNoSearch, digitSymbol+strconv.Itoa(0)) + lines += buildAddCommandLine(keyServerAddresses, arraySymbol+dnsServer) + lines += buildAddCommandLine(ServerPort, digitSymbol+strconv.Itoa(port)) + addDomainCommand := buildCreateStateWithOperation(setupKey, lines) + stdinCommands := wrapCommand(addDomainCommand) + _, err := runSystemConfigCommand(stdinCommands) + if err != nil { + return fmt.Errorf("got error while applying dns setup, error: %s", err) + } + return nil +} + +func getKeyWithInput(format, key string) string { + return fmt.Sprintf(format, key) +} + +func buildAddCommandLine(key, value string) string { + return buildCommandLine("d.add", key, value) +} + +func buildCommandLine(action, key, value string) string { + return fmt.Sprintf("%s %s %s\n", action, key, value) +} + +func wrapCommand(commands string) string { + return fmt.Sprintf("open\n%s\nquit\n", commands) +} + +func buildRemoveKeyOperation(key string) string { + return fmt.Sprintf("remove %s\n", key) +} + +func buildCreateStateWithOperation(state, commands string) string { + return buildWriteStateOperation("set", state, commands) +} + +func buildWriteStateOperation(operation, state, commands string) string { + return fmt.Sprintf("d.init\n%s %s\n%s\nset %s\n", operation, state, commands, state) +} + +func runSystemConfigCommand(command string) ([]byte, error) { + cmd := exec.Command(scutilPath) + cmd.Stdin = strings.NewReader(command) + out, err := cmd.Output() + if err != nil { + return nil, fmt.Errorf("got error while running system configuration command: \"%s\", error: %s", command, err) + } + return out, nil +} diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 946d0b83b8b..7a653ce8345 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -5,14 +5,16 @@ import ( "fmt" "github.com/miekg/dns" nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/iface" log "github.com/sirupsen/logrus" "sync" "time" ) const ( - port = 5053 - defaultIP = "0.0.0.0" + port = 53 + customPort = 5053 + defaultIP = "127.0.0.1" ) // Server is a dns server interface @@ -31,8 +33,11 @@ type DefaultServer struct { dnsMux *dns.ServeMux dnsMuxMap registrationMap localResolver *localResolver + wgInterface *iface.WGIface + hostManager hostManager updateSerial uint64 listenerIsRunning bool + runtimePort int } type registrationMap map[string]struct{} @@ -43,7 +48,7 @@ type muxUpdate struct { } // NewDefaultServer returns a new dns server -func NewDefaultServer(ctx context.Context) *DefaultServer { +func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface) *DefaultServer { mux := dns.NewServeMux() dnsServer := &dns.Server{ @@ -64,6 +69,8 @@ func NewDefaultServer(ctx context.Context) *DefaultServer { localResolver: &localResolver{ registeredMap: make(registrationMap), }, + wgInterface: wgInterface, + hostManager: newHostManager(), } } @@ -73,9 +80,16 @@ func (s *DefaultServer) Start() { go func() { s.setListenerStatus(true) defer s.setListenerStatus(false) + s.runtimePort = port err := s.server.ListenAndServe() if err != nil { - log.Errorf("dns server returned an error: %v", err) + log.Errorf("dns server returned an error using the default port, will try with custom port %d: %v", customPort, err) + s.server.Addr = fmt.Sprintf("%s:%d", defaultIP, customPort) + s.runtimePort = customPort + err = s.server.ListenAndServe() + if err != nil { + log.Errorf("dns server running with custom port returned an error: %v. Will not retry", err) + } } }() } @@ -90,14 +104,12 @@ func (s *DefaultServer) Stop() { defer s.mux.Unlock() s.stop() - var domainsToRemove []string - for domain := range s.dnsMuxMap { - domainsToRemove = append(domainsToRemove, domain) + err := s.hostManager.removeDNSSettings() + if err != nil { + log.Error(err) } - domainsToRemove = append(domainsToRemove, searchFilePrefix) - removeSplitDNS(domainsToRemove) - err := s.stopListener() + err = s.stopListener() if err != nil { log.Error(err) } @@ -160,10 +172,26 @@ func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) erro domainsToRemove := s.updateMux(muxUpdates) s.updateLocalResolver(localRecords) - removeSplitDNS(domainsToRemove) - applySplitDNS(domains) + + for s.runtimePort == 0 { + time.Sleep(10 * time.Millisecond) + } + + err = s.hostManager.removeDomainSettings(domainsToRemove) + if err != nil { + log.Error(err) + } + + err = s.hostManager.applyDNSSettings(domains, defaultIP, s.runtimePort) + if err != nil { + log.Error(err) + } + for _, localMuxUpdate := range localMuxUpdates { - addSearchDomain(localMuxUpdate.domain) + err = s.hostManager.addSearchDomain(localMuxUpdate.domain, defaultIP, s.runtimePort) + if err != nil { + log.Error(err) + } } s.updateSerial = serial From aa815f846879028591d4a87330453a4ee922b103 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Mon, 14 Nov 2022 17:42:26 +0100 Subject: [PATCH 05/48] add windows host manager --- client/internal/dns/host.go | 6 + client/internal/dns/host_darwin.go | 7 +- client/internal/dns/host_windows.go | 211 ++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 client/internal/dns/host_windows.go diff --git a/client/internal/dns/host.go b/client/internal/dns/host.go index cd7578c8229..cf49a02e854 100644 --- a/client/internal/dns/host.go +++ b/client/internal/dns/host.go @@ -1,8 +1,14 @@ package dns +import nbdns "github.com/netbirdio/netbird/dns" + type hostManager interface { applyDNSSettings(domains []string, ip string, port int) error addSearchDomain(domain string, ip string, port int) error removeDomainSettings(domains []string) error removeDNSSettings() error } + +func isRootZoneDomain(domain string) bool { + return domain == nbdns.RootZone || domain == "" +} diff --git a/client/internal/dns/host_darwin.go b/client/internal/dns/host_darwin.go index f3411eb83d3..cb9b4afa616 100644 --- a/client/internal/dns/host_darwin.go +++ b/client/internal/dns/host_darwin.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "fmt" - nbdns "github.com/netbirdio/netbird/dns" log "github.com/sirupsen/logrus" "os/exec" "strconv" @@ -31,7 +30,7 @@ type systemConfigurator struct { createdKeys map[string]struct{} } -func newHostManager() hostManager { +func newHostManager(_, iface.WGIface) hostManager { return &systemConfigurator{ createdKeys: make(map[string]struct{}), } @@ -40,7 +39,7 @@ func newHostManager() hostManager { func (s *systemConfigurator) applyDNSSettings(domains []string, ip string, port int) error { var err error for _, domain := range domains { - if domain == nbdns.RootZone || domain == "" { + if isRootZoneDomain(domain) { err = s.addDNSSetupForAll(ip, port) if err != nil { log.Error(err) @@ -87,7 +86,7 @@ func (s *systemConfigurator) removeDNSSettings() error { func (s *systemConfigurator) removeDomainSettings(domains []string) error { var err error for _, domain := range domains { - if domain == nbdns.RootZone || domain == "" { + if isRootZoneDomain(domain) { if s.primaryServiceID != "" { err = removeKeyFromSystemConfig(getKeyWithInput(primaryServiceSetupKeyFormat, s.primaryServiceID)) if err != nil { diff --git a/client/internal/dns/host_windows.go b/client/internal/dns/host_windows.go new file mode 100644 index 00000000000..6ca710cfdf6 --- /dev/null +++ b/client/internal/dns/host_windows.go @@ -0,0 +1,211 @@ +package dns + +import ( + "fmt" + "github.com/netbirdio/netbird/iface" + log "github.com/sirupsen/logrus" + "golang.org/x/sys/windows/registry" + "golang.zx2c4.com/wireguard/windows/driver" + "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" +) + +const ( + dnsPolicyConfigPathFormat = "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig\\NetBird-%s" + dnsPolicyConfigVersionKey = "Version" + dnsPolicyConfigVersionValue = 2 + dnsPolicyConfigNameKey = "Name" + dnsPolicyConfigGenericDNSServersKey = "GenericDNSServers" + dnsPolicyConfigConfigOptionsKey = "ConfigOptions" + dnsPolicyConfigConfigOptionsValue = 0x8 +) + +const ( + interfaceConfigPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces" + interfaceConfigNameServerKey = "NameServer" + interfaceConfigSearchListKey = "SearchList" +) + +type windowsConfigurator struct { + luid winipcfg.LUID + primaryServiceID string + createdKeys map[string]struct{} +} + +func newHostManager(wgInterface *iface.WGIface) hostManager { + windowsDevice := wgInterface.Interface.(*driver.Adapter) + luid := windowsDevice.LUID() + return &windowsConfigurator{ + luid: luid, + createdKeys: make(map[string]struct{}), + } +} + +func (w *windowsConfigurator) applyDNSSettings(domains []string, ip string, _ int) error { + var err error + for _, domain := range domains { + if isRootZoneDomain(domain) { + err = w.addDNSSetupForAll(ip) + if err != nil { + log.Error(err) + } + continue + } + err = w.addDNSStateForDomain(domain, ip) + if err != nil { + log.Error(err) + } + } + return nil +} + +func (w *windowsConfigurator) addDNSSetupForAll(ip string) error { + err := w.setInterfaceRegistryKeyStringValue(interfaceConfigNameServerKey, ip) + if err != nil { + return fmt.Errorf("adding dns setup for all failed with error: %s", err) + } + + return nil +} + +func (w *windowsConfigurator) getInterfaceRegistryKey() (registry.Key, error) { + var regKey registry.Key + + guid, err := w.luid.GUID() + if err != nil { + return regKey, fmt.Errorf("unable to get interface GUID, error: %s", err) + } + + regKeyPath := interfaceConfigPath + "\\" + guid.String() + + regKey, err = registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.SET_VALUE) + if err != nil { + return regKey, fmt.Errorf("unable to open the interface registry key, key: HKEY_LOCAL_MACHINE\\%s, error: %s", regKeyPath, err) + } + + return regKey, nil +} + +func (w *windowsConfigurator) addDNSStateForDomain(domain, ip string) error { + regKeyPath := getRegistryKeyPath(dnsPolicyConfigPathFormat, domain) + + _, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.QUERY_VALUE) + if err == nil { + err = registry.DeleteKey(registry.LOCAL_MACHINE, regKeyPath) + if err != nil { + return fmt.Errorf("unable to remove existing key from registry, key: HKEY_LOCAL_MACHINE\\%s, error: %s", regKeyPath, err) + } + } + regKey, _, err := registry.CreateKey(registry.LOCAL_MACHINE, regKeyPath, registry.SET_VALUE) + if err != nil { + return fmt.Errorf("unable to create registry key, key: HKEY_LOCAL_MACHINE\\%s, error: %s", regKeyPath, err) + } + + err = regKey.SetDWordValue(dnsPolicyConfigVersionKey, dnsPolicyConfigVersionValue) + if err != nil { + return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigVersionKey, err) + } + + err = regKey.SetStringsValue(dnsPolicyConfigNameKey, []string{domain}) + if err != nil { + return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigNameKey, err) + } + + err = regKey.SetStringValue(dnsPolicyConfigGenericDNSServersKey, ip) + if err != nil { + return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigGenericDNSServersKey, err) + } + + err = regKey.SetDWordValue(dnsPolicyConfigConfigOptionsKey, dnsPolicyConfigConfigOptionsValue) + if err != nil { + return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigConfigOptionsKey, err) + } + + w.createdKeys[regKeyPath] = struct{}{} + + return nil +} + +func (w *windowsConfigurator) addSearchDomain(domain string, ip string, port int) error { + err := w.setInterfaceRegistryKeyStringValue(interfaceConfigSearchListKey, domain) + if err != nil { + return fmt.Errorf("adding search domain failed with error: %s", err) + } + + return nil +} + +func (w *windowsConfigurator) removeDomainSettings(domains []string) error { + var err error + for _, domain := range domains { + if isRootZoneDomain(domain) { + err = w.deleteInterfaceRegistryKey(interfaceConfigNameServerKey) + if err != nil { + log.Error(err) + } + continue + } + + regKeyPath := getRegistryKeyPath(dnsPolicyConfigPathFormat, domain) + err = removeRegistryKeyFromDNSPolicyConfig(regKeyPath) + if err != nil { + log.Error(err) + continue + } + + delete(w.createdKeys, regKeyPath) + } + return nil +} + +func (w *windowsConfigurator) removeDNSSettings() error { + for key := range w.createdKeys { + err := removeRegistryKeyFromDNSPolicyConfig(key) + if err != nil { + log.Error(err) + } + } + return nil +} + +func getRegistryKeyPath(format, input string) string { + return fmt.Sprintf(format, input) +} + +func removeRegistryKeyFromDNSPolicyConfig(regKeyPath string) error { + _, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.QUERY_VALUE) + if err == nil { + err = registry.DeleteKey(registry.LOCAL_MACHINE, regKeyPath) + if err != nil { + return fmt.Errorf("unable to remove existing key from registry, key: HKEY_LOCAL_MACHINE\\%s, error: %s", regKeyPath, err) + } + } + return nil +} + +func (w *windowsConfigurator) setInterfaceRegistryKeyStringValue(key, value string) error { + regKey, err := w.getInterfaceRegistryKey() + if err != nil { + return err + } + + err = regKey.SetStringValue(key, value) + if err != nil { + return fmt.Errorf("applying key %s with value \"%s\" for interface failed with error: %s", key, value, err) + } + + return nil +} + +func (w *windowsConfigurator) deleteInterfaceRegistryKey(key string) error { + regKey, err := w.getInterfaceRegistryKey() + if err != nil { + return err + } + + err = regKey.DeleteValue(key) + if err != nil { + return fmt.Errorf("deleting key %s for interface failed with error: %s", key, err) + } + + return nil +} From c95e591eecaf64c46b2553f52036693d0483d5a9 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 15 Nov 2022 00:41:03 +0100 Subject: [PATCH 06/48] improve dns search and match configuration --- client/internal/dns/host_windows.go | 89 ++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 9 deletions(-) diff --git a/client/internal/dns/host_windows.go b/client/internal/dns/host_windows.go index 6ca710cfdf6..fa8724b85d9 100644 --- a/client/internal/dns/host_windows.go +++ b/client/internal/dns/host_windows.go @@ -7,6 +7,7 @@ import ( "golang.org/x/sys/windows/registry" "golang.zx2c4.com/wireguard/windows/driver" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" + "strings" ) const ( @@ -23,20 +24,23 @@ const ( interfaceConfigPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces" interfaceConfigNameServerKey = "NameServer" interfaceConfigSearchListKey = "SearchList" + tcpipParametersPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters" ) type windowsConfigurator struct { - luid winipcfg.LUID - primaryServiceID string - createdKeys map[string]struct{} + luid winipcfg.LUID + primaryServiceID string + createdKeys map[string]struct{} + addedSearchDomains map[string]struct{} } func newHostManager(wgInterface *iface.WGIface) hostManager { windowsDevice := wgInterface.Interface.(*driver.Adapter) luid := windowsDevice.LUID() return &windowsConfigurator{ - luid: luid, - createdKeys: make(map[string]struct{}), + luid: luid, + createdKeys: make(map[string]struct{}), + addedSearchDomains: make(map[string]struct{}), } } @@ -105,7 +109,9 @@ func (w *windowsConfigurator) addDNSStateForDomain(domain, ip string) error { return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigVersionKey, err) } - err = regKey.SetStringsValue(dnsPolicyConfigNameKey, []string{domain}) + prefixDotedDomain := "." + domain + + err = regKey.SetStringsValue(dnsPolicyConfigNameKey, []string{prefixDotedDomain}) if err != nil { return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigNameKey, err) } @@ -126,11 +132,28 @@ func (w *windowsConfigurator) addDNSStateForDomain(domain, ip string) error { } func (w *windowsConfigurator) addSearchDomain(domain string, ip string, port int) error { - err := w.setInterfaceRegistryKeyStringValue(interfaceConfigSearchListKey, domain) + value, err := getLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey) + if err != nil { + return fmt.Errorf("unable to get current search domains failed with error: %s", err) + } + + valueList := strings.Split(value, ",") + for _, existingDomain := range valueList { + if existingDomain == domain { + log.Debugf("not adding domain %s to the search list. Already exist", domain) + return nil + } + } + + valueList = append(valueList, domain) + + err = setLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey, strings.Join(valueList, ",")) if err != nil { return fmt.Errorf("adding search domain failed with error: %s", err) } + w.addedSearchDomains[domain] = struct{}{} + return nil } @@ -164,7 +187,22 @@ func (w *windowsConfigurator) removeDNSSettings() error { log.Error(err) } } - return nil + + value, err := getLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey) + if err != nil { + return fmt.Errorf("unable to get current search domains failed with error: %s", err) + } + + existingValueList := strings.Split(value, ",") + var newValueList []string + for _, existingDomain := range existingValueList { + _, found := w.addedSearchDomains[existingDomain] + if !found { + newValueList = append(newValueList, existingDomain) + } + } + + return setLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey, strings.Join(newValueList, ",")) } func getRegistryKeyPath(format, input string) string { @@ -172,8 +210,9 @@ func getRegistryKeyPath(format, input string) string { } func removeRegistryKeyFromDNSPolicyConfig(regKeyPath string) error { - _, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.QUERY_VALUE) + k, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.QUERY_VALUE) if err == nil { + k.Close() err = registry.DeleteKey(registry.LOCAL_MACHINE, regKeyPath) if err != nil { return fmt.Errorf("unable to remove existing key from registry, key: HKEY_LOCAL_MACHINE\\%s, error: %s", regKeyPath, err) @@ -182,11 +221,42 @@ func removeRegistryKeyFromDNSPolicyConfig(regKeyPath string) error { return nil } +func getLocalMachineRegistryKeyStringValue(keyPath, key string) (string, error) { + regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.QUERY_VALUE) + if err != nil { + return "", fmt.Errorf("unable to open existing key from registry, key path: HKEY_LOCAL_MACHINE\\%s, error: %s", keyPath, err) + } + defer regKey.Close() + + val, _, err := regKey.GetStringValue(key) + if err != nil { + return "", fmt.Errorf("getting %s value for key path HKEY_LOCAL_MACHINE\\%s failed with error: %s", key, keyPath, err) + } + + return val, nil +} + +func setLocalMachineRegistryKeyStringValue(keyPath, key, value string) error { + regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.SET_VALUE) + if err != nil { + return fmt.Errorf("unable to open existing key from registry, key path: HKEY_LOCAL_MACHINE\\%s, error: %s", keyPath, err) + } + defer regKey.Close() + + err = regKey.SetStringValue(key, value) + if err != nil { + return fmt.Errorf("setting %s value %s for key path HKEY_LOCAL_MACHINE\\%s failed with error: %s", key, value, keyPath, err) + } + + return nil +} + func (w *windowsConfigurator) setInterfaceRegistryKeyStringValue(key, value string) error { regKey, err := w.getInterfaceRegistryKey() if err != nil { return err } + defer regKey.Close() err = regKey.SetStringValue(key, value) if err != nil { @@ -201,6 +271,7 @@ func (w *windowsConfigurator) deleteInterfaceRegistryKey(key string) error { if err != nil { return err } + defer regKey.Close() err = regKey.DeleteValue(key) if err != nil { From 9636f5416aec009a0da03d6294392405bdfa3625 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 15 Nov 2022 00:41:23 +0100 Subject: [PATCH 07/48] add init linux and pass wg iface --- client/internal/dns/host_darwin.go | 3 +- client/internal/dns/host_linux.go | 58 ++++++++++++++++++++++++++++++ client/internal/dns/server.go | 7 ++-- 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 client/internal/dns/host_linux.go diff --git a/client/internal/dns/host_darwin.go b/client/internal/dns/host_darwin.go index cb9b4afa616..77e86897960 100644 --- a/client/internal/dns/host_darwin.go +++ b/client/internal/dns/host_darwin.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "fmt" + "github.com/netbirdio/netbird/iface" log "github.com/sirupsen/logrus" "os/exec" "strconv" @@ -30,7 +31,7 @@ type systemConfigurator struct { createdKeys map[string]struct{} } -func newHostManager(_, iface.WGIface) hostManager { +func newHostManager(_ *iface.WGIface) hostManager { return &systemConfigurator{ createdKeys: make(map[string]struct{}), } diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go new file mode 100644 index 00000000000..0037750d81e --- /dev/null +++ b/client/internal/dns/host_linux.go @@ -0,0 +1,58 @@ +package dns + +import ( + "bufio" + "github.com/netbirdio/netbird/iface" + "os" + "strings" +) + +const ( + defaultResolvConfPath = "/etc/resolv.conf" +) + +const ( + fileManager osManagerType = iota + networkManager + systemdManager + resolvConfManager +) + +const ( + systemDResolvedDest = "org.freedesktop.resolve1" +) + +type osManagerType int + +func newHostManager(wgInterface *iface.WGIface) hostManager { + switch getOSDNSManagerType() { + default: + return nil + } +} + +func getOSDNSManagerType() osManagerType { + file, err := os.Open(defaultResolvConfPath) + if err != nil { + panic(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + text := scanner.Text() + if text[0] != '#' { + return fileManager + } + if strings.Contains(text, "NetworkManager") { + return networkManager + } + if strings.Contains(text, "systemd-resolved") { + return systemdManager + } + if strings.Contains(text, "resolvconf") { + return resolvConfManager + } + } + return fileManager +} diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 7a653ce8345..95a8270a14a 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -7,6 +7,7 @@ import ( nbdns "github.com/netbirdio/netbird/dns" "github.com/netbirdio/netbird/iface" log "github.com/sirupsen/logrus" + "strings" "sync" "time" ) @@ -70,7 +71,7 @@ func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface) *DefaultS registeredMap: make(registrationMap), }, wgInterface: wgInterface, - hostManager: newHostManager(), + hostManager: newHostManager(wgInterface), } } @@ -167,7 +168,7 @@ func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) erro muxUpdates := append(localMuxUpdates, upstreamMuxUpdates...) var domains []string for _, u := range muxUpdates { - domains = append(domains, u.domain) + domains = append(domains, strings.TrimSuffix(u.domain, ".")) } domainsToRemove := s.updateMux(muxUpdates) @@ -188,7 +189,7 @@ func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) erro } for _, localMuxUpdate := range localMuxUpdates { - err = s.hostManager.addSearchDomain(localMuxUpdate.domain, defaultIP, s.runtimePort) + err = s.hostManager.addSearchDomain(strings.TrimSuffix(localMuxUpdate.domain, "."), defaultIP, s.runtimePort) if err != nil { log.Error(err) } From 3e95a8faf0866044526668b13af6e10b664349ec Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 15 Nov 2022 11:03:14 +0100 Subject: [PATCH 08/48] rename configurator --- client/internal/dns/host_windows.go | 47 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/client/internal/dns/host_windows.go b/client/internal/dns/host_windows.go index fa8724b85d9..e5a767128c7 100644 --- a/client/internal/dns/host_windows.go +++ b/client/internal/dns/host_windows.go @@ -27,9 +27,8 @@ const ( tcpipParametersPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters" ) -type windowsConfigurator struct { +type registryConfigurator struct { luid winipcfg.LUID - primaryServiceID string createdKeys map[string]struct{} addedSearchDomains map[string]struct{} } @@ -37,24 +36,24 @@ type windowsConfigurator struct { func newHostManager(wgInterface *iface.WGIface) hostManager { windowsDevice := wgInterface.Interface.(*driver.Adapter) luid := windowsDevice.LUID() - return &windowsConfigurator{ + return ®istryConfigurator{ luid: luid, createdKeys: make(map[string]struct{}), addedSearchDomains: make(map[string]struct{}), } } -func (w *windowsConfigurator) applyDNSSettings(domains []string, ip string, _ int) error { +func (r *registryConfigurator) applyDNSSettings(domains []string, ip string, _ int) error { var err error for _, domain := range domains { if isRootZoneDomain(domain) { - err = w.addDNSSetupForAll(ip) + err = r.addDNSSetupForAll(ip) if err != nil { log.Error(err) } continue } - err = w.addDNSStateForDomain(domain, ip) + err = r.addDNSStateForDomain(domain, ip) if err != nil { log.Error(err) } @@ -62,8 +61,8 @@ func (w *windowsConfigurator) applyDNSSettings(domains []string, ip string, _ in return nil } -func (w *windowsConfigurator) addDNSSetupForAll(ip string) error { - err := w.setInterfaceRegistryKeyStringValue(interfaceConfigNameServerKey, ip) +func (r *registryConfigurator) addDNSSetupForAll(ip string) error { + err := r.setInterfaceRegistryKeyStringValue(interfaceConfigNameServerKey, ip) if err != nil { return fmt.Errorf("adding dns setup for all failed with error: %s", err) } @@ -71,10 +70,10 @@ func (w *windowsConfigurator) addDNSSetupForAll(ip string) error { return nil } -func (w *windowsConfigurator) getInterfaceRegistryKey() (registry.Key, error) { +func (r *registryConfigurator) getInterfaceRegistryKey() (registry.Key, error) { var regKey registry.Key - guid, err := w.luid.GUID() + guid, err := r.luid.GUID() if err != nil { return regKey, fmt.Errorf("unable to get interface GUID, error: %s", err) } @@ -89,7 +88,7 @@ func (w *windowsConfigurator) getInterfaceRegistryKey() (registry.Key, error) { return regKey, nil } -func (w *windowsConfigurator) addDNSStateForDomain(domain, ip string) error { +func (r *registryConfigurator) addDNSStateForDomain(domain, ip string) error { regKeyPath := getRegistryKeyPath(dnsPolicyConfigPathFormat, domain) _, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.QUERY_VALUE) @@ -126,12 +125,12 @@ func (w *windowsConfigurator) addDNSStateForDomain(domain, ip string) error { return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigConfigOptionsKey, err) } - w.createdKeys[regKeyPath] = struct{}{} + r.createdKeys[regKeyPath] = struct{}{} return nil } -func (w *windowsConfigurator) addSearchDomain(domain string, ip string, port int) error { +func (r *registryConfigurator) addSearchDomain(domain string, ip string, port int) error { value, err := getLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey) if err != nil { return fmt.Errorf("unable to get current search domains failed with error: %s", err) @@ -152,16 +151,16 @@ func (w *windowsConfigurator) addSearchDomain(domain string, ip string, port int return fmt.Errorf("adding search domain failed with error: %s", err) } - w.addedSearchDomains[domain] = struct{}{} + r.addedSearchDomains[domain] = struct{}{} return nil } -func (w *windowsConfigurator) removeDomainSettings(domains []string) error { +func (r *registryConfigurator) removeDomainSettings(domains []string) error { var err error for _, domain := range domains { if isRootZoneDomain(domain) { - err = w.deleteInterfaceRegistryKey(interfaceConfigNameServerKey) + err = r.deleteInterfaceRegistryKey(interfaceConfigNameServerKey) if err != nil { log.Error(err) } @@ -175,13 +174,13 @@ func (w *windowsConfigurator) removeDomainSettings(domains []string) error { continue } - delete(w.createdKeys, regKeyPath) + delete(r.createdKeys, regKeyPath) } return nil } -func (w *windowsConfigurator) removeDNSSettings() error { - for key := range w.createdKeys { +func (r *registryConfigurator) removeDNSSettings() error { + for key := range r.createdKeys { err := removeRegistryKeyFromDNSPolicyConfig(key) if err != nil { log.Error(err) @@ -196,7 +195,7 @@ func (w *windowsConfigurator) removeDNSSettings() error { existingValueList := strings.Split(value, ",") var newValueList []string for _, existingDomain := range existingValueList { - _, found := w.addedSearchDomains[existingDomain] + _, found := r.addedSearchDomains[existingDomain] if !found { newValueList = append(newValueList, existingDomain) } @@ -251,8 +250,8 @@ func setLocalMachineRegistryKeyStringValue(keyPath, key, value string) error { return nil } -func (w *windowsConfigurator) setInterfaceRegistryKeyStringValue(key, value string) error { - regKey, err := w.getInterfaceRegistryKey() +func (r *registryConfigurator) setInterfaceRegistryKeyStringValue(key, value string) error { + regKey, err := r.getInterfaceRegistryKey() if err != nil { return err } @@ -266,8 +265,8 @@ func (w *windowsConfigurator) setInterfaceRegistryKeyStringValue(key, value stri return nil } -func (w *windowsConfigurator) deleteInterfaceRegistryKey(key string) error { - regKey, err := w.getInterfaceRegistryKey() +func (r *registryConfigurator) deleteInterfaceRegistryKey(key string) error { + regKey, err := r.getInterfaceRegistryKey() if err != nil { return err } From 35f3a2ff89d4b641b341464ac68b347665d2c329 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 15 Nov 2022 15:18:40 +0100 Subject: [PATCH 09/48] add linux systemd-resolver configurator --- client/internal/dns/host_linux.go | 203 +++++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 2 deletions(-) diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index 0037750d81e..af18eacf332 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -2,9 +2,18 @@ package dns import ( "bufio" + "context" + "fmt" + "github.com/godbus/dbus/v5" + "github.com/miekg/dns" "github.com/netbirdio/netbird/iface" + log "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" + "net" + "net/netip" "os" "strings" + "time" ) const ( @@ -19,15 +28,65 @@ const ( ) const ( - systemDResolvedDest = "org.freedesktop.resolve1" + systemdDbusInterface = "org.freedesktop.resolve1.Manager" + systemdResolvedDest = "org.freedesktop.resolve1" + systemdDbusObjectNode = "/org/freedesktop/resolve1" + systemdDbusGetLinkMethod = systemdDbusInterface + ".GetLink" + systemdDbusRevertMethodSuffix = ".RevertLink" + systemdDbusSetLinkDNSExMethodSuffix = ".SetLinkDNSEx" + systemdDbusSetLinkDefaultRouteMethodSuffix = ".SetLinkDefaultRoute" + systemdDbusSetLinkDomainsMethodSuffix = ".SetLinkDomains" + systemdDbusDefaultFlag = 0 ) +type systemdDbusConfigurator struct { + dbusLinkInterface string + createdLinkedDomains map[string]systemdDbusLinkDomainsInput +} + +type systemdDbusDNSExInput struct { + Family int + Address []byte + Port int +} + +type systemdDbusLinkDomainsInput struct { + Domain string + MatchOnly bool +} + type osManagerType int func newHostManager(wgInterface *iface.WGIface) hostManager { switch getOSDNSManagerType() { default: - return nil + log.Debugf("discovered mode is: %d", getOSDNSManagerType()) + return newSystemdDbusConfigurator(wgInterface) + } +} + +func newSystemdDbusConfigurator(wgInterface *iface.WGIface) hostManager { + iface, err := net.InterfaceByName(wgInterface.GetName()) + if err != nil { + // todo add proper error handling + panic(err) + } + + obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode) + if err != nil { + panic(err) + } + defer closeConn() + var s string + err = obj.Call(systemdDbusGetLinkMethod, systemdDbusDefaultFlag, iface.Index).Store(&s) + if err != nil { + // todo add proper error handling + panic(err) + } + + return &systemdDbusConfigurator{ + dbusLinkInterface: s, + createdLinkedDomains: make(map[string]systemdDbusLinkDomainsInput), } } @@ -56,3 +115,143 @@ func getOSDNSManagerType() osManagerType { } return fileManager } + +func (s *systemdDbusConfigurator) applyDNSSettings(domains []string, ip string, port int) error { + parsedIP := netip.MustParseAddr(ip).As4() + defaultLinkInput := systemdDbusDNSExInput{ + Family: unix.AF_INET, + Address: parsedIP[:], + Port: port, + } + err := s.callLinkMethod(systemdDbusSetLinkDNSExMethodSuffix, defaultLinkInput) + if err != nil { + return fmt.Errorf("setting the interface DNS server %s:%d failed with error: %s", ip, port, err) + } + + var domainsInput []systemdDbusLinkDomainsInput + + for _, domain := range domains { + if isRootZoneDomain(domain) { + err = s.callLinkMethod(systemdDbusSetLinkDefaultRouteMethodSuffix, true) + if err != nil { + log.Errorf("setting link as default dns router, failed with error: %s", err) + } + continue + } + domainsInput = append(domainsInput, systemdDbusLinkDomainsInput{ + Domain: dns.Fqdn(domain), + MatchOnly: true, + }) + } + err = s.addDNSStateForDomain(domainsInput) + if err != nil { + log.Error(err) + } + return nil +} + +func (s *systemdDbusConfigurator) addDNSSetupForAll() error { + err := s.callLinkMethod(systemdDbusSetLinkDefaultRouteMethodSuffix, true) + if err != nil { + return fmt.Errorf("setting link as default dns router, failed with error: %s", err) + } + return nil +} + +func (s *systemdDbusConfigurator) addDNSStateForDomain(domainsInput []systemdDbusLinkDomainsInput) error { + err := s.callLinkMethod(systemdDbusSetLinkDomainsMethodSuffix, domainsInput) + if err != nil { + return fmt.Errorf("setting domains configuration failed with error: %s", err) + } + for _, input := range domainsInput { + s.createdLinkedDomains[input.Domain] = input + } + return nil +} + +func (s *systemdDbusConfigurator) addSearchDomain(domain string, ip string, port int) error { + var newDomainsInput []systemdDbusLinkDomainsInput + + fqdnDomain := dns.Fqdn(domain) + + existingDomain, found := s.createdLinkedDomains[fqdnDomain] + if found && !existingDomain.MatchOnly { + return nil + } + + delete(s.createdLinkedDomains, fqdnDomain) + for _, existingInput := range s.createdLinkedDomains { + newDomainsInput = append(newDomainsInput, existingInput) + } + + newDomainsInput = append(newDomainsInput, systemdDbusLinkDomainsInput{ + Domain: fqdnDomain, + MatchOnly: false, + }) + + err := s.addDNSStateForDomain(newDomainsInput) + if err != nil { + return fmt.Errorf("setting domains configuration with search domain %s failed with error: %s", domain, err) + } + + return nil +} +func (s *systemdDbusConfigurator) removeDomainSettings(domains []string) error { + var err error + for _, domain := range domains { + if isRootZoneDomain(domain) { + err = s.callLinkMethod(systemdDbusSetLinkDefaultRouteMethodSuffix, false) + if err != nil { + log.Errorf("setting link as non default dns router, failed with error: %s", err) + } + break + } + } + + // cleaning the configuration as it gets rebuild + emptyList := make([]systemdDbusLinkDomainsInput, 0) + + err = s.callLinkMethod(systemdDbusSetLinkDomainsMethodSuffix, emptyList) + if err != nil { + log.Error(err) + } + + s.createdLinkedDomains = make(map[string]systemdDbusLinkDomainsInput) + + return nil +} +func (s *systemdDbusConfigurator) removeDNSSettings() error { + err := s.callLinkMethod(systemdDbusRevertMethodSuffix, nil) + if err != nil { + return fmt.Errorf("unable to revert link configuration, got error: %s", err) + } + return nil +} + +func (s *systemdDbusConfigurator) callLinkMethod(methodSuffix string, value any) error { + obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode) + if err != nil { + return err + } + defer closeConn() + + ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) + defer cancel() + + err = obj.CallWithContext(ctx, s.dbusLinkInterface+methodSuffix, systemdDbusDefaultFlag, value).Store() + if err != nil { + return err + } + + return nil +} + +func getDbusObject(dest string, path dbus.ObjectPath) (dbus.BusObject, func() error, error) { + conn, err := dbus.ConnectSessionBus() + if err != nil { + return nil, nil, err + } + obj := conn.Object(dest, path) + + return obj, conn.Close, nil +} From c0582761d24bd84c6fb924dece4cd6de16cad8c3 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 15 Nov 2022 15:25:13 +0100 Subject: [PATCH 10/48] sync go.sum --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 676a7e4db00..be97338ec92 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,7 @@ require ( github.com/eko/gocache/v3 v3.1.1 github.com/getlantern/systray v1.2.1 github.com/gliderlabs/ssh v0.3.4 + github.com/godbus/dbus/v5 v5.0.4 github.com/google/nftables v0.0.0-20220808154552-2eca00135732 github.com/libp2p/go-netroute v0.2.0 github.com/magiconair/properties v1.8.5 @@ -74,7 +75,6 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-stack/stack v1.8.0 // indirect - github.com/godbus/dbus/v5 v5.0.4 // indirect github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/gopacket v1.1.19 // indirect From 983d5beb544b3707cd308acfcf9d29afd610f089 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 15 Nov 2022 21:58:16 +0100 Subject: [PATCH 11/48] flush cache and fix correct link interface --- client/internal/dns/host_linux.go | 79 ++++++++++++++++++++----------- client/internal/dns/server.go | 2 + 2 files changed, 53 insertions(+), 28 deletions(-) diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index af18eacf332..048f36bbe8f 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -28,26 +28,31 @@ const ( ) const ( - systemdDbusInterface = "org.freedesktop.resolve1.Manager" - systemdResolvedDest = "org.freedesktop.resolve1" - systemdDbusObjectNode = "/org/freedesktop/resolve1" - systemdDbusGetLinkMethod = systemdDbusInterface + ".GetLink" - systemdDbusRevertMethodSuffix = ".RevertLink" - systemdDbusSetLinkDNSExMethodSuffix = ".SetLinkDNSEx" - systemdDbusSetLinkDefaultRouteMethodSuffix = ".SetLinkDefaultRoute" - systemdDbusSetLinkDomainsMethodSuffix = ".SetLinkDomains" - systemdDbusDefaultFlag = 0 + systemdDbusManagerInterface = "org.freedesktop.resolve1.Manager" + systemdResolvedDest = "org.freedesktop.resolve1" + systemdDbusObjectNode = "/org/freedesktop/resolve1" + systemdDbusGetLinkMethod = systemdDbusManagerInterface + ".GetLink" + systemdDbusFlushCachesMethod = systemdDbusManagerInterface + ".FlushCaches" + systemdDbusLinkInterface = "org.freedesktop.resolve1.Link" + systemdDbusRevertMethodSuffix = systemdDbusLinkInterface + ".Revert" + systemdDbusSetDNSExMethodSuffix = systemdDbusLinkInterface + ".SetDNSEx" + systemdDbusSetDefaultRouteMethodSuffix = systemdDbusLinkInterface + ".SetDefaultRoute" + systemdDbusSetDomainsMethodSuffix = systemdDbusLinkInterface + ".SetDomains" + systemdDbusDefaultFlag = 0 ) type systemdDbusConfigurator struct { - dbusLinkInterface string + dbusLinkInterface dbus.ObjectPath createdLinkedDomains map[string]systemdDbusLinkDomainsInput } +// https://dbus.freedesktop.org/doc/dbus-specification.html +// https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html type systemdDbusDNSExInput struct { - Family int + Family int32 Address []byte - Port int + Port uint16 + Domain string } type systemdDbusLinkDomainsInput struct { @@ -84,8 +89,10 @@ func newSystemdDbusConfigurator(wgInterface *iface.WGIface) hostManager { panic(err) } + log.Debugf("got dbus Link interface: %s from net interface %s and index %d", s, iface.Name, iface.Index) + return &systemdDbusConfigurator{ - dbusLinkInterface: s, + dbusLinkInterface: dbus.ObjectPath(s), createdLinkedDomains: make(map[string]systemdDbusLinkDomainsInput), } } @@ -121,9 +128,9 @@ func (s *systemdDbusConfigurator) applyDNSSettings(domains []string, ip string, defaultLinkInput := systemdDbusDNSExInput{ Family: unix.AF_INET, Address: parsedIP[:], - Port: port, + Port: uint16(port), } - err := s.callLinkMethod(systemdDbusSetLinkDNSExMethodSuffix, defaultLinkInput) + err := s.callLinkMethod(systemdDbusSetDNSExMethodSuffix, []systemdDbusDNSExInput{defaultLinkInput}) if err != nil { return fmt.Errorf("setting the interface DNS server %s:%d failed with error: %s", ip, port, err) } @@ -132,11 +139,10 @@ func (s *systemdDbusConfigurator) applyDNSSettings(domains []string, ip string, for _, domain := range domains { if isRootZoneDomain(domain) { - err = s.callLinkMethod(systemdDbusSetLinkDefaultRouteMethodSuffix, true) + err = s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, true) if err != nil { log.Errorf("setting link as default dns router, failed with error: %s", err) } - continue } domainsInput = append(domainsInput, systemdDbusLinkDomainsInput{ Domain: dns.Fqdn(domain), @@ -151,7 +157,7 @@ func (s *systemdDbusConfigurator) applyDNSSettings(domains []string, ip string, } func (s *systemdDbusConfigurator) addDNSSetupForAll() error { - err := s.callLinkMethod(systemdDbusSetLinkDefaultRouteMethodSuffix, true) + err := s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, true) if err != nil { return fmt.Errorf("setting link as default dns router, failed with error: %s", err) } @@ -159,7 +165,7 @@ func (s *systemdDbusConfigurator) addDNSSetupForAll() error { } func (s *systemdDbusConfigurator) addDNSStateForDomain(domainsInput []systemdDbusLinkDomainsInput) error { - err := s.callLinkMethod(systemdDbusSetLinkDomainsMethodSuffix, domainsInput) + err := s.callLinkMethod(systemdDbusSetDomainsMethodSuffix, domainsInput) if err != nil { return fmt.Errorf("setting domains configuration failed with error: %s", err) } @@ -194,13 +200,13 @@ func (s *systemdDbusConfigurator) addSearchDomain(domain string, ip string, port return fmt.Errorf("setting domains configuration with search domain %s failed with error: %s", domain, err) } - return nil + return s.flushCaches() } func (s *systemdDbusConfigurator) removeDomainSettings(domains []string) error { var err error for _, domain := range domains { if isRootZoneDomain(domain) { - err = s.callLinkMethod(systemdDbusSetLinkDefaultRouteMethodSuffix, false) + err = s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, false) if err != nil { log.Errorf("setting link as non default dns router, failed with error: %s", err) } @@ -211,36 +217,53 @@ func (s *systemdDbusConfigurator) removeDomainSettings(domains []string) error { // cleaning the configuration as it gets rebuild emptyList := make([]systemdDbusLinkDomainsInput, 0) - err = s.callLinkMethod(systemdDbusSetLinkDomainsMethodSuffix, emptyList) + err = s.callLinkMethod(systemdDbusSetDomainsMethodSuffix, emptyList) if err != nil { log.Error(err) } s.createdLinkedDomains = make(map[string]systemdDbusLinkDomainsInput) - return nil + return s.flushCaches() } func (s *systemdDbusConfigurator) removeDNSSettings() error { err := s.callLinkMethod(systemdDbusRevertMethodSuffix, nil) if err != nil { return fmt.Errorf("unable to revert link configuration, got error: %s", err) } - return nil + return s.flushCaches() } -func (s *systemdDbusConfigurator) callLinkMethod(methodSuffix string, value any) error { +func (s *systemdDbusConfigurator) flushCaches() error { obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode) if err != nil { - return err + return fmt.Errorf("got error while attempting to retrieve the object %s, err: %s", systemdDbusObjectNode, err) + } + defer closeConn() + ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) + defer cancel() + + err = obj.CallWithContext(ctx, systemdDbusFlushCachesMethod, systemdDbusDefaultFlag).Store() + if err != nil { + return fmt.Errorf("got error while calling the FlushCaches method with context, err: %s", err) + } + + return nil +} + +func (s *systemdDbusConfigurator) callLinkMethod(method string, value any) error { + obj, closeConn, err := getDbusObject(systemdResolvedDest, s.dbusLinkInterface) + if err != nil { + return fmt.Errorf("got error while attempting to retrieve the object, err: %s", err) } defer closeConn() ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) defer cancel() - err = obj.CallWithContext(ctx, s.dbusLinkInterface+methodSuffix, systemdDbusDefaultFlag, value).Store() + err = obj.CallWithContext(ctx, method, systemdDbusDefaultFlag, value).Store() if err != nil { - return err + return fmt.Errorf("got error while calling command with context, err: %s", err) } return nil diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 95a8270a14a..20d2601c39f 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -195,6 +195,8 @@ func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) erro } } + log.Debugf("applied dns update, added %d peer records, %d domain records and removed %d", len(localRecords), len(muxUpdates), len(domainsToRemove)) + s.updateSerial = serial return nil From 3b5eb557066a3426a99f82fb4946ee334e7039c5 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 15 Nov 2022 22:03:36 +0100 Subject: [PATCH 12/48] use latest dbus version --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index be97338ec92..fa6d2fb1871 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/eko/gocache/v3 v3.1.1 github.com/getlantern/systray v1.2.1 github.com/gliderlabs/ssh v0.3.4 - github.com/godbus/dbus/v5 v5.0.4 + github.com/godbus/dbus/v5 v5.1.0 github.com/google/nftables v0.0.0-20220808154552-2eca00135732 github.com/libp2p/go-netroute v0.2.0 github.com/magiconair/properties v1.8.5 diff --git a/go.sum b/go.sum index 43a64c2a875..9b13faa7a63 100644 --- a/go.sum +++ b/go.sum @@ -223,8 +223,9 @@ github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= From 17e095850c9b95e557ff40ad88adae25d8b7e10c Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 15 Nov 2022 23:02:32 +0100 Subject: [PATCH 13/48] use the system bus --- client/internal/dns/host_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index 048f36bbe8f..6ae5531f3b5 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -270,7 +270,7 @@ func (s *systemdDbusConfigurator) callLinkMethod(method string, value any) error } func getDbusObject(dest string, path dbus.ObjectPath) (dbus.BusObject, func() error, error) { - conn, err := dbus.ConnectSessionBus() + conn, err := dbus.SystemBus() if err != nil { return nil, nil, err } From 3289e2ba1022bff20195cd7053464f6dace7b018 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 15 Nov 2022 23:03:17 +0100 Subject: [PATCH 14/48] add mocking configurator --- client/internal/dns/host.go | 49 +++++++++++++++++++++++++++++- client/internal/dns/server.go | 10 ++++-- client/internal/dns/server_test.go | 5 +++ 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/client/internal/dns/host.go b/client/internal/dns/host.go index cf49a02e854..b902ea7913f 100644 --- a/client/internal/dns/host.go +++ b/client/internal/dns/host.go @@ -1,6 +1,9 @@ package dns -import nbdns "github.com/netbirdio/netbird/dns" +import ( + "fmt" + nbdns "github.com/netbirdio/netbird/dns" +) type hostManager interface { applyDNSSettings(domains []string, ip string, port int) error @@ -12,3 +15,47 @@ type hostManager interface { func isRootZoneDomain(domain string) bool { return domain == nbdns.RootZone || domain == "" } + +type mockHostConfigurator struct { + applyDNSSettingsFunc func(domains []string, ip string, port int) error + addSearchDomainFunc func(domain string, ip string, port int) error + removeDomainSettingsFunc func(domains []string) error + removeDNSSettingsFunc func() error +} + +func (m *mockHostConfigurator) applyDNSSettings(domains []string, ip string, port int) error { + if m.applyDNSSettingsFunc != nil { + return m.applyDNSSettingsFunc(domains, ip, port) + } + return fmt.Errorf("method applyDNSSettings is not implemented") +} + +func (m *mockHostConfigurator) addSearchDomain(domain string, ip string, port int) error { + if m.addSearchDomainFunc != nil { + return m.addSearchDomainFunc(domain, ip, port) + } + return fmt.Errorf("method addSearchDomain is not implemented") +} + +func (m *mockHostConfigurator) removeDomainSettings(domains []string) error { + if m.removeDomainSettingsFunc != nil { + return m.removeDomainSettingsFunc(domains) + } + return fmt.Errorf("method removeDomainSettings is not implemented") +} + +func (m *mockHostConfigurator) removeDNSSettings() error { + if m.removeDNSSettingsFunc != nil { + return m.removeDNSSettingsFunc() + } + return fmt.Errorf("method removeDNSSettings is not implemented") +} + +func newNoopHostMocker() hostManager { + return &mockHostConfigurator{ + applyDNSSettingsFunc: func(domains []string, ip string, port int) error { return nil }, + addSearchDomainFunc: func(domain string, ip string, port int) error { return nil }, + removeDomainSettingsFunc: func(domains []string) error { return nil }, + removeDNSSettingsFunc: func() error { return nil }, + } +} diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 20d2601c39f..7e766b02b45 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -61,7 +61,7 @@ func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface) *DefaultS ctx, stop := context.WithCancel(ctx) - return &DefaultServer{ + defaultServer := &DefaultServer{ ctx: ctx, stop: stop, server: dnsServer, @@ -71,8 +71,14 @@ func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface) *DefaultS registeredMap: make(registrationMap), }, wgInterface: wgInterface, - hostManager: newHostManager(wgInterface), } + // this should only happen on tests + if wgInterface.Interface == nil { + log.Debugf("returning a server without host manager") + return defaultServer + } + defaultServer.hostManager = newHostManager(wgInterface) + return defaultServer } // Start runs the listener in a go routine diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index 7ef0094f65a..e72ba017f4f 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -195,11 +195,14 @@ func TestUpdateDNSServer(t *testing.T) { } ctx := context.Background() dnsServer := NewDefaultServer(ctx, wgInterface) + dnsServer.hostManager = newNoopHostMocker() dnsServer.dnsMuxMap = testCase.initUpstreamMap dnsServer.localResolver.registeredMap = testCase.initLocalMap dnsServer.updateSerial = testCase.initSerial + // pretend we are running dnsServer.listenerIsRunning = true + dnsServer.runtimePort = 53 err = dnsServer.UpdateDNSServer(testCase.inputSerial, testCase.inputUpdate) if err != nil { @@ -247,6 +250,8 @@ func TestDNSServerStartStop(t *testing.T) { t.Skip("skipping test in Windows CI workflows.") } + dnsServer.hostManager = newNoopHostMocker() + dnsServer.Start() err = dnsServer.localResolver.registerRecord(zoneRecords[0]) From 94f00f098a3e9821aa7e01775ea691a5583c6ee5 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 15 Nov 2022 23:28:57 +0100 Subject: [PATCH 15/48] probe port before listen and serve --- client/internal/dns/server.go | 27 ++++++++++++++------------- client/internal/dns/server_test.go | 1 - 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 7e766b02b45..de3071b8e98 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -7,6 +7,7 @@ import ( nbdns "github.com/netbirdio/netbird/dns" "github.com/netbirdio/netbird/iface" log "github.com/sirupsen/logrus" + "net" "strings" "sync" "time" @@ -84,19 +85,23 @@ func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface) *DefaultS // Start runs the listener in a go routine func (s *DefaultServer) Start() { log.Debugf("starting dns on %s:%d", defaultIP, port) + s.runtimePort = port + probeListener, err := net.Listen("udp", s.server.Addr) + if err != nil { + s.runtimePort = customPort + s.server.Addr = fmt.Sprintf("%s:%d", defaultIP, customPort) + } + err = probeListener.Close() + if err != nil { + log.Errorf("got an error closing the probe listener, error: %s", err) + } go func() { s.setListenerStatus(true) defer s.setListenerStatus(false) - s.runtimePort = port - err := s.server.ListenAndServe() + + err = s.server.ListenAndServe() if err != nil { - log.Errorf("dns server returned an error using the default port, will try with custom port %d: %v", customPort, err) - s.server.Addr = fmt.Sprintf("%s:%d", defaultIP, customPort) - s.runtimePort = customPort - err = s.server.ListenAndServe() - if err != nil { - log.Errorf("dns server running with custom port returned an error: %v. Will not retry", err) - } + log.Errorf("dns server running with %d port returned an error: %v. Will not retry", s.runtimePort, err) } }() } @@ -180,10 +185,6 @@ func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) erro domainsToRemove := s.updateMux(muxUpdates) s.updateLocalResolver(localRecords) - for s.runtimePort == 0 { - time.Sleep(10 * time.Millisecond) - } - err = s.hostManager.removeDomainSettings(domainsToRemove) if err != nil { log.Error(err) diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index e72ba017f4f..48a901fde45 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -202,7 +202,6 @@ func TestUpdateDNSServer(t *testing.T) { dnsServer.updateSerial = testCase.initSerial // pretend we are running dnsServer.listenerIsRunning = true - dnsServer.runtimePort = 53 err = dnsServer.UpdateDNSServer(testCase.inputSerial, testCase.inputUpdate) if err != nil { From 25388ad714f5b86b378e9b391b0112f15e2bbead Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Wed, 16 Nov 2022 12:06:59 +0100 Subject: [PATCH 16/48] check if systemd resolver is running and use SetDNS --- client/internal/dns/host_linux.go | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index 6ae5531f3b5..3cdb96a6e21 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -35,7 +35,7 @@ const ( systemdDbusFlushCachesMethod = systemdDbusManagerInterface + ".FlushCaches" systemdDbusLinkInterface = "org.freedesktop.resolve1.Link" systemdDbusRevertMethodSuffix = systemdDbusLinkInterface + ".Revert" - systemdDbusSetDNSExMethodSuffix = systemdDbusLinkInterface + ".SetDNSEx" + systemdDbusSetDNSMethodSuffix = systemdDbusLinkInterface + ".SetDNS" systemdDbusSetDefaultRouteMethodSuffix = systemdDbusLinkInterface + ".SetDefaultRoute" systemdDbusSetDomainsMethodSuffix = systemdDbusLinkInterface + ".SetDomains" systemdDbusDefaultFlag = 0 @@ -48,11 +48,9 @@ type systemdDbusConfigurator struct { // https://dbus.freedesktop.org/doc/dbus-specification.html // https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html -type systemdDbusDNSExInput struct { +type systemdDbusDNSInput struct { Family int32 Address []byte - Port uint16 - Domain string } type systemdDbusLinkDomainsInput struct { @@ -113,7 +111,7 @@ func getOSDNSManagerType() osManagerType { if strings.Contains(text, "NetworkManager") { return networkManager } - if strings.Contains(text, "systemd-resolved") { + if strings.Contains(text, "systemd-resolved") && isDbusListenerRunning(systemdResolvedDest, systemdDbusObjectNode) { return systemdManager } if strings.Contains(text, "resolvconf") { @@ -125,12 +123,11 @@ func getOSDNSManagerType() osManagerType { func (s *systemdDbusConfigurator) applyDNSSettings(domains []string, ip string, port int) error { parsedIP := netip.MustParseAddr(ip).As4() - defaultLinkInput := systemdDbusDNSExInput{ + defaultLinkInput := systemdDbusDNSInput{ Family: unix.AF_INET, Address: parsedIP[:], - Port: uint16(port), } - err := s.callLinkMethod(systemdDbusSetDNSExMethodSuffix, []systemdDbusDNSExInput{defaultLinkInput}) + err := s.callLinkMethod(systemdDbusSetDNSMethodSuffix, defaultLinkInput) if err != nil { return fmt.Errorf("setting the interface DNS server %s:%d failed with error: %s", ip, port, err) } @@ -269,6 +266,23 @@ func (s *systemdDbusConfigurator) callLinkMethod(method string, value any) error return nil } +func isDbusListenerRunning(dest string, path dbus.ObjectPath) bool { + obj, closeConn, err := getDbusObject(dest, path) + if err != nil { + return false + } + defer closeConn() + + ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) + defer cancel() + + err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0).Store() + if err != nil { + return false + } + return true +} + func getDbusObject(dest string, path dbus.ObjectPath) (dbus.BusObject, func() error, error) { conn, err := dbus.SystemBus() if err != nil { From 486a9e3abe734c576f84c6394a274ccafa195208 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Wed, 16 Nov 2022 12:23:45 +0100 Subject: [PATCH 17/48] issue proper message for listener --- client/internal/dns/host_linux.go | 24 ++++++++++-------------- client/internal/dns/server.go | 6 +++++- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index 3cdb96a6e21..e6b58279e34 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -153,14 +153,6 @@ func (s *systemdDbusConfigurator) applyDNSSettings(domains []string, ip string, return nil } -func (s *systemdDbusConfigurator) addDNSSetupForAll() error { - err := s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, true) - if err != nil { - return fmt.Errorf("setting link as default dns router, failed with error: %s", err) - } - return nil -} - func (s *systemdDbusConfigurator) addDNSStateForDomain(domainsInput []systemdDbusLinkDomainsInput) error { err := s.callLinkMethod(systemdDbusSetDomainsMethodSuffix, domainsInput) if err != nil { @@ -277,18 +269,22 @@ func isDbusListenerRunning(dest string, path dbus.ObjectPath) bool { defer cancel() err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0).Store() - if err != nil { - return false - } - return true + return err == nil } -func getDbusObject(dest string, path dbus.ObjectPath) (dbus.BusObject, func() error, error) { +func getDbusObject(dest string, path dbus.ObjectPath) (dbus.BusObject, func(), error) { conn, err := dbus.SystemBus() if err != nil { return nil, nil, err } obj := conn.Object(dest, path) - return obj, conn.Close, nil + closeFunc := func() { + closeErr := conn.Close() + if closeErr != nil { + log.Warnf("got an error closing dbus connection, err: %s", closeErr) + } + } + + return obj, closeFunc, nil } diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index de3071b8e98..0fea326931f 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -88,10 +88,14 @@ func (s *DefaultServer) Start() { s.runtimePort = port probeListener, err := net.Listen("udp", s.server.Addr) if err != nil { + log.Warnf("using a custom port for dns server") s.runtimePort = customPort s.server.Addr = fmt.Sprintf("%s:%d", defaultIP, customPort) + } else { + log.Infof("using the default DNS port for dns server") + err = probeListener.Close() } - err = probeListener.Close() + if err != nil { log.Errorf("got an error closing the probe listener, error: %s", err) } From ffd071cd5ac45b7375c3064821241da7951253cc Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Wed, 16 Nov 2022 12:35:42 +0100 Subject: [PATCH 18/48] issue proper udp listener --- client/internal/dns/host_darwin.go | 1 - client/internal/dns/server.go | 14 ++++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/client/internal/dns/host_darwin.go b/client/internal/dns/host_darwin.go index 77e86897960..9fdf299f949 100644 --- a/client/internal/dns/host_darwin.go +++ b/client/internal/dns/host_darwin.go @@ -176,7 +176,6 @@ func (s *systemConfigurator) getPrimaryService() string { log.Error("got error while sending the command: ", err) return "" } - fmt.Printf("original command \n%s\ncommand out: %s\n,err: %v\n", stdinCommands, string(b), err) scanner := bufio.NewScanner(bytes.NewReader(b)) for scanner.Scan() { text := scanner.Text() diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 0fea326931f..474b4abdb51 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -8,6 +8,7 @@ import ( "github.com/netbirdio/netbird/iface" log "github.com/sirupsen/logrus" "net" + "net/netip" "strings" "sync" "time" @@ -84,21 +85,22 @@ func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface) *DefaultS // Start runs the listener in a go routine func (s *DefaultServer) Start() { - log.Debugf("starting dns on %s:%d", defaultIP, port) s.runtimePort = port - probeListener, err := net.Listen("udp", s.server.Addr) + udpAddr := net.UDPAddrFromAddrPort(netip.MustParseAddrPort(s.server.Addr)) + probeListener, err := net.ListenUDP("udp", udpAddr) if err != nil { log.Warnf("using a custom port for dns server") s.runtimePort = customPort s.server.Addr = fmt.Sprintf("%s:%d", defaultIP, customPort) } else { - log.Infof("using the default DNS port for dns server") err = probeListener.Close() + if err != nil { + log.Errorf("got an error closing the probe listener, error: %s", err) + } } - if err != nil { - log.Errorf("got an error closing the probe listener, error: %s", err) - } + log.Debugf("starting dns on %s", s.server.Addr) + go func() { s.setListenerStatus(true) defer s.setListenerStatus(false) From 912aec459c397b1eb8c355acaa09abe27df4d516 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Thu, 17 Nov 2022 16:09:02 +0100 Subject: [PATCH 19/48] add network manager configurator and split code in different files --- client/internal/dns/dbus_linux.go | 41 +++ client/internal/dns/host_linux.go | 244 +--------------- client/internal/dns/nm_configuratior_linux.go | 270 ++++++++++++++++++ .../dns/systemd_configurator_linux.go | 212 ++++++++++++++ 4 files changed, 530 insertions(+), 237 deletions(-) create mode 100644 client/internal/dns/dbus_linux.go create mode 100644 client/internal/dns/nm_configuratior_linux.go create mode 100644 client/internal/dns/systemd_configurator_linux.go diff --git a/client/internal/dns/dbus_linux.go b/client/internal/dns/dbus_linux.go new file mode 100644 index 00000000000..0f6d4156afe --- /dev/null +++ b/client/internal/dns/dbus_linux.go @@ -0,0 +1,41 @@ +package dns + +import ( + "context" + "github.com/godbus/dbus/v5" + log "github.com/sirupsen/logrus" + "time" +) + +const dbusDefaultFlag = 0 + +func isDbusListenerRunning(dest string, path dbus.ObjectPath) bool { + obj, closeConn, err := getDbusObject(dest, path) + if err != nil { + return false + } + defer closeConn() + + ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) + defer cancel() + + err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0).Store() + return err == nil +} + +func getDbusObject(dest string, path dbus.ObjectPath) (dbus.BusObject, func(), error) { + conn, err := dbus.SystemBus() + if err != nil { + return nil, nil, err + } + obj := conn.Object(dest, path) + + closeFunc := func() { + closeErr := conn.Close() + if closeErr != nil { + log.Warnf("got an error closing dbus connection, err: %s", closeErr) + } + } + + return obj, closeFunc, nil +} diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index e6b58279e34..13b6728824a 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -2,18 +2,10 @@ package dns import ( "bufio" - "context" - "fmt" - "github.com/godbus/dbus/v5" - "github.com/miekg/dns" "github.com/netbirdio/netbird/iface" log "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" - "net" - "net/netip" "os" "strings" - "time" ) const ( @@ -27,77 +19,23 @@ const ( resolvConfManager ) -const ( - systemdDbusManagerInterface = "org.freedesktop.resolve1.Manager" - systemdResolvedDest = "org.freedesktop.resolve1" - systemdDbusObjectNode = "/org/freedesktop/resolve1" - systemdDbusGetLinkMethod = systemdDbusManagerInterface + ".GetLink" - systemdDbusFlushCachesMethod = systemdDbusManagerInterface + ".FlushCaches" - systemdDbusLinkInterface = "org.freedesktop.resolve1.Link" - systemdDbusRevertMethodSuffix = systemdDbusLinkInterface + ".Revert" - systemdDbusSetDNSMethodSuffix = systemdDbusLinkInterface + ".SetDNS" - systemdDbusSetDefaultRouteMethodSuffix = systemdDbusLinkInterface + ".SetDefaultRoute" - systemdDbusSetDomainsMethodSuffix = systemdDbusLinkInterface + ".SetDomains" - systemdDbusDefaultFlag = 0 -) - -type systemdDbusConfigurator struct { - dbusLinkInterface dbus.ObjectPath - createdLinkedDomains map[string]systemdDbusLinkDomainsInput -} - -// https://dbus.freedesktop.org/doc/dbus-specification.html -// https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html -type systemdDbusDNSInput struct { - Family int32 - Address []byte -} - -type systemdDbusLinkDomainsInput struct { - Domain string - MatchOnly bool -} - type osManagerType int func newHostManager(wgInterface *iface.WGIface) hostManager { - switch getOSDNSManagerType() { + osManager := getOSDNSManagerType() + log.Debugf("discovered mode is: %d", osManager) + switch osManager { + case networkManager: + return newNetworkManagerDbusConfigurator(wgInterface) default: - log.Debugf("discovered mode is: %d", getOSDNSManagerType()) return newSystemdDbusConfigurator(wgInterface) } } -func newSystemdDbusConfigurator(wgInterface *iface.WGIface) hostManager { - iface, err := net.InterfaceByName(wgInterface.GetName()) - if err != nil { - // todo add proper error handling - panic(err) - } - - obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode) - if err != nil { - panic(err) - } - defer closeConn() - var s string - err = obj.Call(systemdDbusGetLinkMethod, systemdDbusDefaultFlag, iface.Index).Store(&s) - if err != nil { - // todo add proper error handling - panic(err) - } - - log.Debugf("got dbus Link interface: %s from net interface %s and index %d", s, iface.Name, iface.Index) - - return &systemdDbusConfigurator{ - dbusLinkInterface: dbus.ObjectPath(s), - createdLinkedDomains: make(map[string]systemdDbusLinkDomainsInput), - } -} - func getOSDNSManagerType() osManagerType { file, err := os.Open(defaultResolvConfPath) if err != nil { + // todo add proper error handling panic(err) } defer file.Close() @@ -108,7 +46,7 @@ func getOSDNSManagerType() osManagerType { if text[0] != '#' { return fileManager } - if strings.Contains(text, "NetworkManager") { + if strings.Contains(text, "NetworkManager") && isDbusListenerRunning(networkManagerDest, networkManagerDbusDeviceInterface) { return networkManager } if strings.Contains(text, "systemd-resolved") && isDbusListenerRunning(systemdResolvedDest, systemdDbusObjectNode) { @@ -120,171 +58,3 @@ func getOSDNSManagerType() osManagerType { } return fileManager } - -func (s *systemdDbusConfigurator) applyDNSSettings(domains []string, ip string, port int) error { - parsedIP := netip.MustParseAddr(ip).As4() - defaultLinkInput := systemdDbusDNSInput{ - Family: unix.AF_INET, - Address: parsedIP[:], - } - err := s.callLinkMethod(systemdDbusSetDNSMethodSuffix, defaultLinkInput) - if err != nil { - return fmt.Errorf("setting the interface DNS server %s:%d failed with error: %s", ip, port, err) - } - - var domainsInput []systemdDbusLinkDomainsInput - - for _, domain := range domains { - if isRootZoneDomain(domain) { - err = s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, true) - if err != nil { - log.Errorf("setting link as default dns router, failed with error: %s", err) - } - } - domainsInput = append(domainsInput, systemdDbusLinkDomainsInput{ - Domain: dns.Fqdn(domain), - MatchOnly: true, - }) - } - err = s.addDNSStateForDomain(domainsInput) - if err != nil { - log.Error(err) - } - return nil -} - -func (s *systemdDbusConfigurator) addDNSStateForDomain(domainsInput []systemdDbusLinkDomainsInput) error { - err := s.callLinkMethod(systemdDbusSetDomainsMethodSuffix, domainsInput) - if err != nil { - return fmt.Errorf("setting domains configuration failed with error: %s", err) - } - for _, input := range domainsInput { - s.createdLinkedDomains[input.Domain] = input - } - return nil -} - -func (s *systemdDbusConfigurator) addSearchDomain(domain string, ip string, port int) error { - var newDomainsInput []systemdDbusLinkDomainsInput - - fqdnDomain := dns.Fqdn(domain) - - existingDomain, found := s.createdLinkedDomains[fqdnDomain] - if found && !existingDomain.MatchOnly { - return nil - } - - delete(s.createdLinkedDomains, fqdnDomain) - for _, existingInput := range s.createdLinkedDomains { - newDomainsInput = append(newDomainsInput, existingInput) - } - - newDomainsInput = append(newDomainsInput, systemdDbusLinkDomainsInput{ - Domain: fqdnDomain, - MatchOnly: false, - }) - - err := s.addDNSStateForDomain(newDomainsInput) - if err != nil { - return fmt.Errorf("setting domains configuration with search domain %s failed with error: %s", domain, err) - } - - return s.flushCaches() -} -func (s *systemdDbusConfigurator) removeDomainSettings(domains []string) error { - var err error - for _, domain := range domains { - if isRootZoneDomain(domain) { - err = s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, false) - if err != nil { - log.Errorf("setting link as non default dns router, failed with error: %s", err) - } - break - } - } - - // cleaning the configuration as it gets rebuild - emptyList := make([]systemdDbusLinkDomainsInput, 0) - - err = s.callLinkMethod(systemdDbusSetDomainsMethodSuffix, emptyList) - if err != nil { - log.Error(err) - } - - s.createdLinkedDomains = make(map[string]systemdDbusLinkDomainsInput) - - return s.flushCaches() -} -func (s *systemdDbusConfigurator) removeDNSSettings() error { - err := s.callLinkMethod(systemdDbusRevertMethodSuffix, nil) - if err != nil { - return fmt.Errorf("unable to revert link configuration, got error: %s", err) - } - return s.flushCaches() -} - -func (s *systemdDbusConfigurator) flushCaches() error { - obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode) - if err != nil { - return fmt.Errorf("got error while attempting to retrieve the object %s, err: %s", systemdDbusObjectNode, err) - } - defer closeConn() - ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) - defer cancel() - - err = obj.CallWithContext(ctx, systemdDbusFlushCachesMethod, systemdDbusDefaultFlag).Store() - if err != nil { - return fmt.Errorf("got error while calling the FlushCaches method with context, err: %s", err) - } - - return nil -} - -func (s *systemdDbusConfigurator) callLinkMethod(method string, value any) error { - obj, closeConn, err := getDbusObject(systemdResolvedDest, s.dbusLinkInterface) - if err != nil { - return fmt.Errorf("got error while attempting to retrieve the object, err: %s", err) - } - defer closeConn() - - ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) - defer cancel() - - err = obj.CallWithContext(ctx, method, systemdDbusDefaultFlag, value).Store() - if err != nil { - return fmt.Errorf("got error while calling command with context, err: %s", err) - } - - return nil -} - -func isDbusListenerRunning(dest string, path dbus.ObjectPath) bool { - obj, closeConn, err := getDbusObject(dest, path) - if err != nil { - return false - } - defer closeConn() - - ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) - defer cancel() - - err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0).Store() - return err == nil -} - -func getDbusObject(dest string, path dbus.ObjectPath) (dbus.BusObject, func(), error) { - conn, err := dbus.SystemBus() - if err != nil { - return nil, nil, err - } - obj := conn.Object(dest, path) - - closeFunc := func() { - closeErr := conn.Close() - if closeErr != nil { - log.Warnf("got an error closing dbus connection, err: %s", closeErr) - } - } - - return obj, closeFunc, nil -} diff --git a/client/internal/dns/nm_configuratior_linux.go b/client/internal/dns/nm_configuratior_linux.go new file mode 100644 index 00000000000..4c83a6ff266 --- /dev/null +++ b/client/internal/dns/nm_configuratior_linux.go @@ -0,0 +1,270 @@ +package dns + +import ( + "context" + "encoding/binary" + "fmt" + "github.com/godbus/dbus/v5" + "github.com/miekg/dns" + "github.com/netbirdio/netbird/iface" + log "github.com/sirupsen/logrus" + "net/netip" + "time" +) + +const ( + networkManagerDest = "org.freedesktop.NetworkManager" + networkManagerDbusObjectNode = "/org/freedesktop/NetworkManager" + networkManagerDbusGetDeviceByIpIfaceMethod = networkManagerDest + ".GetDeviceByIpIface" + networkManagerDbusDeviceInterface = "org.freedesktop.NetworkManager.Device" + networkManagerDbusDeviceGetAppliedConnectionMethod = networkManagerDbusDeviceInterface + ".GetAppliedConnection" + networkManagerDbusDeviceReapplyMethod = networkManagerDbusDeviceInterface + ".Reapply" + networkManagerDbusDefaultBehaviorFlag networkManagerConfigBehavior = 0 + networkManagerDbusIPv4Key = "ipv4" + networkManagerDbusIPv6Key = "ipv6" + networkManagerDbusDNSKey = "dns" + networkManagerDbusDNSSearchKey = "dns-search" + networkManagerDbusDNSPriorityKey = "dns-priority" + + // dns priority doc https://wiki.gnome.org/Projects/NetworkManager/DNS + networkManagerDbusPrimaryDNSPriority int32 = -2147483648 + networkManagerDbusWithMatchDomainPriority int32 = 0 + networkManagerDbusSearchDomainOnlyPriority int32 = 50 + networkManagerDbusSearchDefaultPriority int32 = 100 +) + +type networkManagerDbusConfigurator struct { + dbusLinkObject dbus.ObjectPath +} + +// the types below are based on dbus specification, each field is mapped to a dbus type +// see https://dbus.freedesktop.org/doc/dbus-specification.html#basic-types for more details on dbus types +// see https://networkmanager.dev/docs/api/latest/gdbus-org.freedesktop.NetworkManager.Device.html on Network Manager input types + +// networkManagerConnSettings maps to a (a{sa{sv}}) dbus output from GetAppliedConnection and input for Reapply methods +type networkManagerConnSettings map[string]map[string]dbus.Variant + +// networkManagerConfigVersion maps to a (t) dbus output from GetAppliedConnection and input for Reapply methods +type networkManagerConfigVersion uint64 + +// networkManagerConfigBehavior maps to a (u) dbus input for GetAppliedConnection and Reapply methods +type networkManagerConfigBehavior uint32 + +// cleanDeprecatedSettings cleans deprecated settings that still returned by +// the GetAppliedConnection methods but can't be reApplied +func (s networkManagerConnSettings) cleanDeprecatedSettings() { + for _, key := range []string{"addresses", "routes"} { + delete(s[networkManagerDbusIPv4Key], key) + delete(s[networkManagerDbusIPv6Key], key) + } +} + +func newNetworkManagerDbusConfigurator(wgInterface *iface.WGIface) hostManager { + obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusObjectNode) + if err != nil { + // todo add proper error handling + panic(err) + } + defer closeConn() + var s string + err = obj.Call(networkManagerDbusGetDeviceByIpIfaceMethod, dbusDefaultFlag, wgInterface.GetName()).Store(&s) + if err != nil { + // todo add proper error handling + panic(err) + } + + log.Debugf("got network manager dbus Link Object: %s from net interface %s", s, wgInterface.GetName()) + + return &networkManagerDbusConfigurator{ + dbusLinkObject: dbus.ObjectPath(s), + } +} + +func (n *networkManagerDbusConfigurator) applyDNSSettings(domains []string, ip string, port int) error { + connSettings, configVersion, err := n.getAppliedConnectionSettings() + if err != nil { + return fmt.Errorf("got an error while retrieving the applied connection settings, error: %s", err) + } + + connSettings.cleanDeprecatedSettings() + // todo remove this + _, found := connSettings[networkManagerDbusIPv4Key]["routes"] + if found { + panic("removing deprecated settings didn't work") + } + + dnsIP := netip.MustParseAddr(ip) + convDNSIP := binary.LittleEndian.Uint32(dnsIP.AsSlice()) + connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSKey] = dbus.MakeVariant([]uint32{convDNSIP}) + + priority := networkManagerDbusSearchDomainOnlyPriority + if len(domains) > 1 { + priority = networkManagerDbusWithMatchDomainPriority + } + + var newDomainList []string + + for _, domain := range domains { + if isRootZoneDomain(domain) { + priority = networkManagerDbusPrimaryDNSPriority + newDomainList = append(newDomainList, "~.") + continue + } + newDomainList = append(newDomainList, "~."+dns.Fqdn(domain)) + } + connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSPriorityKey] = dbus.MakeVariant(priority) + connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] = dbus.MakeVariant(newDomainList) + + err = n.reApplyConnectionSettings(connSettings, configVersion) + if err != nil { + log.Errorf("got an error while reapplying the connection with new settings, error: %s", err) + } + return nil +} + +func (n *networkManagerDbusConfigurator) addSearchDomain(domain string, _ string, _ int) error { + connSettings, configVersion, err := n.getAppliedConnectionSettings() + if err != nil { + return fmt.Errorf("got an error while retrieving the applied connection settings, error: %s", err) + } + + connSettings.cleanDeprecatedSettings() + + currentDomainsVariant := connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] + currentDomainsInt := currentDomainsVariant.Value() + currentDomains := currentDomainsInt.([]string) + + fqdnDomain := dns.Fqdn(domain) + matchOnlyDomain := "~." + fqdnDomain + var newDomainList []string + for _, currDomain := range currentDomains { + if currDomain != fqdnDomain && currDomain != matchOnlyDomain { + newDomainList = append(newDomainList, currDomain) + } + } + + newDomainList = append(newDomainList, fqdnDomain) + + connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] = dbus.MakeVariant(newDomainList) + + err = n.reApplyConnectionSettings(connSettings, configVersion) + if err != nil { + log.Errorf("got an error while reapplying the connection with new search domain settings, error: %s", err) + } + return nil +} +func (n *networkManagerDbusConfigurator) removeDomainSettings(domains []string) error { + connSettings, configVersion, err := n.getAppliedConnectionSettings() + if err != nil { + return fmt.Errorf("got an error while retrieving the applied connection settings, error: %s", err) + } + + connSettings.cleanDeprecatedSettings() + + currentDomainsVariant := connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] + currentDomainsInt := currentDomainsVariant.Value() + currentDomains := currentDomainsInt.([]string) + + currentMap := make(map[string]struct{}) + for _, currentDomain := range currentDomains { + currentMap[currentDomain] = struct{}{} + } + + for _, domain := range domains { + fqdnDomain := dns.Fqdn(domain) + matchOnlyDomain := "~." + fqdnDomain + _, found := currentMap[fqdnDomain] + if found { + delete(currentMap, fqdnDomain) + continue + } + _, found = currentMap[matchOnlyDomain] + if found { + delete(currentMap, matchOnlyDomain) + } + } + + priority := networkManagerDbusSearchDomainOnlyPriority + if len(currentMap) > 1 { + priority = networkManagerDbusWithMatchDomainPriority + } + + var newDomainList []string + for domainLeft := range currentMap { + if domainLeft == "~." { + priority = networkManagerDbusPrimaryDNSPriority + } + newDomainList = append(newDomainList, domainLeft) + } + + connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSPriorityKey] = dbus.MakeVariant(priority) + connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] = dbus.MakeVariant(newDomainList) + + err = n.reApplyConnectionSettings(connSettings, configVersion) + if err != nil { + log.Errorf("got an error while reapplying settings after removing domains, error: %s", err) + } + return nil +} +func (n *networkManagerDbusConfigurator) removeDNSSettings() error { + connSettings, configVersion, err := n.getAppliedConnectionSettings() + if err != nil { + return fmt.Errorf("got an error while retrieving the applied connection settings, error: %s", err) + } + + connSettings.cleanDeprecatedSettings() + + connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSKey] = dbus.MakeVariant([]uint32{}) + connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSPriorityKey] = dbus.MakeVariant(networkManagerDbusSearchDefaultPriority) + connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] = dbus.MakeVariant([]string{}) + + err = n.reApplyConnectionSettings(connSettings, configVersion) + if err != nil { + log.Errorf("got an error while reapplying removed settings, error: %s", err) + } + + return nil +} + +func (n *networkManagerDbusConfigurator) getAppliedConnectionSettings() (networkManagerConnSettings, networkManagerConfigVersion, error) { + obj, closeConn, err := getDbusObject(networkManagerDest, n.dbusLinkObject) + if err != nil { + return nil, 0, fmt.Errorf("got error while attempting to retrieve the applied connection settings, err: %s", err) + } + defer closeConn() + + ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) + defer cancel() + + var ( + connSettings networkManagerConnSettings + configVersion networkManagerConfigVersion + ) + + err = obj.CallWithContext(ctx, networkManagerDbusDeviceGetAppliedConnectionMethod, dbusDefaultFlag, + networkManagerDbusDefaultBehaviorFlag).Store(&connSettings, &configVersion) + if err != nil { + return nil, 0, fmt.Errorf("got error while calling command with context, err: %s", err) + } + + return connSettings, configVersion, nil +} + +func (n *networkManagerDbusConfigurator) reApplyConnectionSettings(connSettings networkManagerConnSettings, configVersion networkManagerConfigVersion) error { + obj, closeConn, err := getDbusObject(networkManagerDest, n.dbusLinkObject) + if err != nil { + return fmt.Errorf("got error while attempting to retrieve the applied connection settings, err: %s", err) + } + defer closeConn() + + ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) + defer cancel() + + err = obj.CallWithContext(ctx, networkManagerDbusDeviceReapplyMethod, dbusDefaultFlag, + connSettings, configVersion, networkManagerDbusDefaultBehaviorFlag).Store() + if err != nil { + return fmt.Errorf("got error while calling command with context, err: %s", err) + } + + return nil +} diff --git a/client/internal/dns/systemd_configurator_linux.go b/client/internal/dns/systemd_configurator_linux.go new file mode 100644 index 00000000000..83646393c23 --- /dev/null +++ b/client/internal/dns/systemd_configurator_linux.go @@ -0,0 +1,212 @@ +package dns + +import ( + "context" + "fmt" + "github.com/godbus/dbus/v5" + "github.com/miekg/dns" + "github.com/netbirdio/netbird/iface" + log "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" + "net" + "net/netip" + "time" +) + +const ( + systemdDbusManagerInterface = "org.freedesktop.resolve1.Manager" + systemdResolvedDest = "org.freedesktop.resolve1" + systemdDbusObjectNode = "/org/freedesktop/resolve1" + systemdDbusGetLinkMethod = systemdDbusManagerInterface + ".GetLink" + systemdDbusFlushCachesMethod = systemdDbusManagerInterface + ".FlushCaches" + systemdDbusLinkInterface = "org.freedesktop.resolve1.Link" + systemdDbusRevertMethodSuffix = systemdDbusLinkInterface + ".Revert" + systemdDbusSetDNSMethodSuffix = systemdDbusLinkInterface + ".SetDNS" + systemdDbusSetDefaultRouteMethodSuffix = systemdDbusLinkInterface + ".SetDefaultRoute" + systemdDbusSetDomainsMethodSuffix = systemdDbusLinkInterface + ".SetDomains" +) + +type systemdDbusConfigurator struct { + dbusLinkObject dbus.ObjectPath + createdLinkedDomains map[string]systemdDbusLinkDomainsInput +} + +// the types below are based on dbus specification, each field is mapped to a dbus type +// see https://dbus.freedesktop.org/doc/dbus-specification.html#basic-types for more details on dbus types +// see https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html on resolve1 input types +// systemdDbusDNSInput maps to a (iay) dbus input for SetDNS method +type systemdDbusDNSInput struct { + Family int32 + Address []byte +} + +// systemdDbusLinkDomainsInput maps to a (sb) dbus input for SetDomains method +type systemdDbusLinkDomainsInput struct { + Domain string + MatchOnly bool +} + +func newSystemdDbusConfigurator(wgInterface *iface.WGIface) hostManager { + iface, err := net.InterfaceByName(wgInterface.GetName()) + if err != nil { + // todo add proper error handling + panic(err) + } + + obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode) + if err != nil { + // todo add proper error handling + panic(err) + } + defer closeConn() + var s string + err = obj.Call(systemdDbusGetLinkMethod, dbusDefaultFlag, iface.Index).Store(&s) + if err != nil { + // todo add proper error handling + panic(err) + } + + log.Debugf("got dbus Link interface: %s from net interface %s and index %d", s, iface.Name, iface.Index) + + return &systemdDbusConfigurator{ + dbusLinkObject: dbus.ObjectPath(s), + createdLinkedDomains: make(map[string]systemdDbusLinkDomainsInput), + } +} + +func (s *systemdDbusConfigurator) applyDNSSettings(domains []string, ip string, port int) error { + parsedIP := netip.MustParseAddr(ip).As4() + defaultLinkInput := systemdDbusDNSInput{ + Family: unix.AF_INET, + Address: parsedIP[:], + } + err := s.callLinkMethod(systemdDbusSetDNSMethodSuffix, []systemdDbusDNSInput{defaultLinkInput}) + if err != nil { + return fmt.Errorf("setting the interface DNS server %s:%d failed with error: %s", ip, port, err) + } + + var domainsInput []systemdDbusLinkDomainsInput + + for _, domain := range domains { + if isRootZoneDomain(domain) { + err = s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, true) + if err != nil { + log.Errorf("setting link as default dns router, failed with error: %s", err) + } + } + domainsInput = append(domainsInput, systemdDbusLinkDomainsInput{ + Domain: dns.Fqdn(domain), + MatchOnly: true, + }) + } + err = s.addDNSStateForDomain(domainsInput) + if err != nil { + log.Error(err) + } + return nil +} + +func (s *systemdDbusConfigurator) addDNSStateForDomain(domainsInput []systemdDbusLinkDomainsInput) error { + err := s.callLinkMethod(systemdDbusSetDomainsMethodSuffix, domainsInput) + if err != nil { + return fmt.Errorf("setting domains configuration failed with error: %s", err) + } + for _, input := range domainsInput { + s.createdLinkedDomains[input.Domain] = input + } + return nil +} + +func (s *systemdDbusConfigurator) addSearchDomain(domain string, ip string, port int) error { + var newDomainsInput []systemdDbusLinkDomainsInput + + fqdnDomain := dns.Fqdn(domain) + + existingDomain, found := s.createdLinkedDomains[fqdnDomain] + if found && !existingDomain.MatchOnly { + return nil + } + + delete(s.createdLinkedDomains, fqdnDomain) + for _, existingInput := range s.createdLinkedDomains { + newDomainsInput = append(newDomainsInput, existingInput) + } + + newDomainsInput = append(newDomainsInput, systemdDbusLinkDomainsInput{ + Domain: fqdnDomain, + MatchOnly: false, + }) + + err := s.addDNSStateForDomain(newDomainsInput) + if err != nil { + return fmt.Errorf("setting domains configuration with search domain %s failed with error: %s", domain, err) + } + + return s.flushCaches() +} +func (s *systemdDbusConfigurator) removeDomainSettings(domains []string) error { + var err error + for _, domain := range domains { + if isRootZoneDomain(domain) { + err = s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, false) + if err != nil { + log.Errorf("setting link as non default dns router, failed with error: %s", err) + } + break + } + } + + // cleaning the configuration as it gets rebuild + emptyList := make([]systemdDbusLinkDomainsInput, 0) + + err = s.callLinkMethod(systemdDbusSetDomainsMethodSuffix, emptyList) + if err != nil { + log.Error(err) + } + + s.createdLinkedDomains = make(map[string]systemdDbusLinkDomainsInput) + + return s.flushCaches() +} +func (s *systemdDbusConfigurator) removeDNSSettings() error { + err := s.callLinkMethod(systemdDbusRevertMethodSuffix, nil) + if err != nil { + return fmt.Errorf("unable to revert link configuration, got error: %s", err) + } + return s.flushCaches() +} + +func (s *systemdDbusConfigurator) flushCaches() error { + obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode) + if err != nil { + return fmt.Errorf("got error while attempting to retrieve the object %s, err: %s", systemdDbusObjectNode, err) + } + defer closeConn() + ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) + defer cancel() + + err = obj.CallWithContext(ctx, systemdDbusFlushCachesMethod, dbusDefaultFlag).Store() + if err != nil { + return fmt.Errorf("got error while calling the FlushCaches method with context, err: %s", err) + } + + return nil +} + +func (s *systemdDbusConfigurator) callLinkMethod(method string, value any) error { + obj, closeConn, err := getDbusObject(systemdResolvedDest, s.dbusLinkObject) + if err != nil { + return fmt.Errorf("got error while attempting to retrieve the object, err: %s", err) + } + defer closeConn() + + ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) + defer cancel() + + err = obj.CallWithContext(ctx, method, dbusDefaultFlag, value).Store() + if err != nil { + return fmt.Errorf("got error while calling command with context, err: %s", err) + } + + return nil +} From ef08ca6f7c9f42d7a4ec3a812016bf7e22fe3ad7 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Fri, 18 Nov 2022 12:12:50 +0100 Subject: [PATCH 20/48] add network manager configurator --- client/internal/dns/host_linux.go | 2 +- client/internal/dns/nm_configuratior_linux.go | 37 +++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index 13b6728824a..537ca60d74a 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -46,7 +46,7 @@ func getOSDNSManagerType() osManagerType { if text[0] != '#' { return fileManager } - if strings.Contains(text, "NetworkManager") && isDbusListenerRunning(networkManagerDest, networkManagerDbusDeviceInterface) { + if strings.Contains(text, "NetworkManager") && isDbusListenerRunning(networkManagerDest, networkManagerDbusObjectNode) { return networkManager } if strings.Contains(text, "systemd-resolved") && isDbusListenerRunning(systemdResolvedDest, systemdDbusObjectNode) { diff --git a/client/internal/dns/nm_configuratior_linux.go b/client/internal/dns/nm_configuratior_linux.go index 4c83a6ff266..796edd92802 100644 --- a/client/internal/dns/nm_configuratior_linux.go +++ b/client/internal/dns/nm_configuratior_linux.go @@ -15,6 +15,8 @@ import ( const ( networkManagerDest = "org.freedesktop.NetworkManager" networkManagerDbusObjectNode = "/org/freedesktop/NetworkManager" + networkManagerDbusDnsManagerObjectNode = "/org/freedesktop/NetworkManager/DnsManager" + networkManagerDbusDnsManagerModeProperty = "org.freedesktop.NetworkManager.DnsManager.Mode" networkManagerDbusGetDeviceByIpIfaceMethod = networkManagerDest + ".GetDeviceByIpIface" networkManagerDbusDeviceInterface = "org.freedesktop.NetworkManager.Device" networkManagerDbusDeviceGetAppliedConnectionMethod = networkManagerDbusDeviceInterface + ".GetAppliedConnection" @@ -27,7 +29,7 @@ const ( networkManagerDbusDNSPriorityKey = "dns-priority" // dns priority doc https://wiki.gnome.org/Projects/NetworkManager/DNS - networkManagerDbusPrimaryDNSPriority int32 = -2147483648 + networkManagerDbusPrimaryDNSPriority int32 = -500 networkManagerDbusWithMatchDomainPriority int32 = 0 networkManagerDbusSearchDomainOnlyPriority int32 = 50 networkManagerDbusSearchDefaultPriority int32 = 100 @@ -87,11 +89,6 @@ func (n *networkManagerDbusConfigurator) applyDNSSettings(domains []string, ip s } connSettings.cleanDeprecatedSettings() - // todo remove this - _, found := connSettings[networkManagerDbusIPv4Key]["routes"] - if found { - panic("removing deprecated settings didn't work") - } dnsIP := netip.MustParseAddr(ip) convDNSIP := binary.LittleEndian.Uint32(dnsIP.AsSlice()) @@ -110,8 +107,14 @@ func (n *networkManagerDbusConfigurator) applyDNSSettings(domains []string, ip s newDomainList = append(newDomainList, "~.") continue } - newDomainList = append(newDomainList, "~."+dns.Fqdn(domain)) + matchDomain := "~." + dns.Fqdn(domain) + newDomainList = append(newDomainList, matchDomain) + } + + if priority == networkManagerDbusPrimaryDNSPriority { + newDomainList = append(newDomainList, "~.") } + connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSPriorityKey] = dbus.MakeVariant(priority) connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] = dbus.MakeVariant(newDomainList) @@ -143,7 +146,7 @@ func (n *networkManagerDbusConfigurator) addSearchDomain(domain string, _ string } } - newDomainList = append(newDomainList, fqdnDomain) + newDomainList = append([]string{fqdnDomain}, newDomainList...) connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] = dbus.MakeVariant(newDomainList) @@ -151,6 +154,7 @@ func (n *networkManagerDbusConfigurator) addSearchDomain(domain string, _ string if err != nil { log.Errorf("got an error while reapplying the connection with new search domain settings, error: %s", err) } + return nil } func (n *networkManagerDbusConfigurator) removeDomainSettings(domains []string) error { @@ -268,3 +272,20 @@ func (n *networkManagerDbusConfigurator) reApplyConnectionSettings(connSettings return nil } + +//func isNetworkManagerSupportedVersion() bool { +// obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusDnsManagerObjectNode) +// if err != nil { +// log.Errorf("got error while attempting to get the network manager object, err: %s", err) +// return false +// } +// +// defer closeConn() +// +// value, err := obj.GetProperty(networkManagerDbusDnsManagerModeProperty) +// if err != nil { +// log.Errorf("unable to retrieve network manager mode, got error: %s", err) +// return false +// } +// stringValue := value.Value().(string) +//} From 65222652f3f34471cf8eb77505a646e3c365d593 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Fri, 18 Nov 2022 15:58:04 +0100 Subject: [PATCH 21/48] compare nm version --- client/internal/dns/nm_configuratior_linux.go | 63 ++++++++++++++----- go.mod | 1 + go.sum | 2 + 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/client/internal/dns/nm_configuratior_linux.go b/client/internal/dns/nm_configuratior_linux.go index 796edd92802..c9952d3d59c 100644 --- a/client/internal/dns/nm_configuratior_linux.go +++ b/client/internal/dns/nm_configuratior_linux.go @@ -5,10 +5,13 @@ import ( "encoding/binary" "fmt" "github.com/godbus/dbus/v5" + "github.com/hashicorp/go-version" "github.com/miekg/dns" "github.com/netbirdio/netbird/iface" log "github.com/sirupsen/logrus" "net/netip" + "regexp" + "sort" "time" ) @@ -273,19 +276,47 @@ func (n *networkManagerDbusConfigurator) reApplyConnectionSettings(connSettings return nil } -//func isNetworkManagerSupportedVersion() bool { -// obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusDnsManagerObjectNode) -// if err != nil { -// log.Errorf("got error while attempting to get the network manager object, err: %s", err) -// return false -// } -// -// defer closeConn() -// -// value, err := obj.GetProperty(networkManagerDbusDnsManagerModeProperty) -// if err != nil { -// log.Errorf("unable to retrieve network manager mode, got error: %s", err) -// return false -// } -// stringValue := value.Value().(string) -//} +func isNetworkManagerSupportedVersion() bool { + obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusDnsManagerObjectNode) + if err != nil { + log.Errorf("got error while attempting to get the network manager object, err: %s", err) + return false + } + + defer closeConn() + + value, err := obj.GetProperty(networkManagerDbusDnsManagerModeProperty) + if err != nil { + log.Errorf("unable to retrieve network manager mode, got error: %s", err) + return false + } + stringValue := value.Value().(string) +} + +func compareVersions(inputList []string) (string, string) { + reg, err := regexp.Compile(version.SemverRegexpRaw) + if err != nil { + return "", "" + } + + versions := make([]*version.Version, 0) + + for _, raw := range inputList { + if raw != "" && reg.MatchString(raw) { + v, err := version.NewVersion(raw) + if err == nil { + versions = append(versions, v) + } + } + } + switch len(versions) { + case 0: + return "", "" + case 1: + v := versions[0].String() + return v, v + default: + sort.Sort(version.Collection(versions)) + return versions[0].String(), versions[len(versions)-1].String() + } +} diff --git a/go.mod b/go.mod index fa6d2fb1871..b7f92bc73b7 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( github.com/gliderlabs/ssh v0.3.4 github.com/godbus/dbus/v5 v5.1.0 github.com/google/nftables v0.0.0-20220808154552-2eca00135732 + github.com/hashicorp/go-version v1.6.0 github.com/libp2p/go-netroute v0.2.0 github.com/magiconair/properties v1.8.5 github.com/miekg/dns v1.1.41 diff --git a/go.sum b/go.sum index 9b13faa7a63..707d7c808f1 100644 --- a/go.sum +++ b/go.sum @@ -348,6 +348,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= From 9e44d0c9164027fc2011323efe5e9cfc402801dc Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Fri, 18 Nov 2022 23:13:02 +0100 Subject: [PATCH 22/48] simplify hostManager interface --- client/internal/dns/host.go | 46 +++++++++++-------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/client/internal/dns/host.go b/client/internal/dns/host.go index b902ea7913f..1f5a8a7d4df 100644 --- a/client/internal/dns/host.go +++ b/client/internal/dns/host.go @@ -6,10 +6,8 @@ import ( ) type hostManager interface { - applyDNSSettings(domains []string, ip string, port int) error - addSearchDomain(domain string, ip string, port int) error - removeDomainSettings(domains []string) error - removeDNSSettings() error + applyDNSConfig() error + restoreHostDNS() error } func isRootZoneDomain(domain string) bool { @@ -17,45 +15,27 @@ func isRootZoneDomain(domain string) bool { } type mockHostConfigurator struct { - applyDNSSettingsFunc func(domains []string, ip string, port int) error - addSearchDomainFunc func(domain string, ip string, port int) error - removeDomainSettingsFunc func(domains []string) error - removeDNSSettingsFunc func() error + applyDNSConfigFunc func() error + restoreHostDNSFunc func() error } -func (m *mockHostConfigurator) applyDNSSettings(domains []string, ip string, port int) error { - if m.applyDNSSettingsFunc != nil { - return m.applyDNSSettingsFunc(domains, ip, port) +func (m *mockHostConfigurator) applyDNSConfig() error { + if m.applyDNSConfigFunc != nil { + return m.applyDNSConfigFunc() } return fmt.Errorf("method applyDNSSettings is not implemented") } -func (m *mockHostConfigurator) addSearchDomain(domain string, ip string, port int) error { - if m.addSearchDomainFunc != nil { - return m.addSearchDomainFunc(domain, ip, port) +func (m *mockHostConfigurator) restoreHostDNS() error { + if m.restoreHostDNSFunc != nil { + return m.restoreHostDNSFunc() } - return fmt.Errorf("method addSearchDomain is not implemented") -} - -func (m *mockHostConfigurator) removeDomainSettings(domains []string) error { - if m.removeDomainSettingsFunc != nil { - return m.removeDomainSettingsFunc(domains) - } - return fmt.Errorf("method removeDomainSettings is not implemented") -} - -func (m *mockHostConfigurator) removeDNSSettings() error { - if m.removeDNSSettingsFunc != nil { - return m.removeDNSSettingsFunc() - } - return fmt.Errorf("method removeDNSSettings is not implemented") + return fmt.Errorf("method restoreHostDNS is not implemented") } func newNoopHostMocker() hostManager { return &mockHostConfigurator{ - applyDNSSettingsFunc: func(domains []string, ip string, port int) error { return nil }, - addSearchDomainFunc: func(domain string, ip string, port int) error { return nil }, - removeDomainSettingsFunc: func(domains []string) error { return nil }, - removeDNSSettingsFunc: func() error { return nil }, + applyDNSConfigFunc: func() error { return nil }, + restoreHostDNSFunc: func() error { return nil }, } } From 9ee740573c50a3d0dac492dc905dcc140fddbeb2 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sat, 19 Nov 2022 00:46:14 +0100 Subject: [PATCH 23/48] update system configurator for updated interface --- client/internal/dns/host.go | 58 +++++++++++++-- client/internal/dns/host_darwin.go | 111 ++++++++++++++++------------- client/internal/dns/server.go | 30 ++------ 3 files changed, 117 insertions(+), 82 deletions(-) diff --git a/client/internal/dns/host.go b/client/internal/dns/host.go index 1f5a8a7d4df..f7cdaa441e0 100644 --- a/client/internal/dns/host.go +++ b/client/internal/dns/host.go @@ -3,25 +3,34 @@ package dns import ( "fmt" nbdns "github.com/netbirdio/netbird/dns" + "strings" ) type hostManager interface { - applyDNSConfig() error + applyDNSConfig(config hostDNSConfig) error restoreHostDNS() error } -func isRootZoneDomain(domain string) bool { - return domain == nbdns.RootZone || domain == "" +type hostDNSConfig struct { + domains []domainConfig + routeAll bool + serverIP string + serverPort int +} + +type domainConfig struct { + domain string + matchOnly bool } type mockHostConfigurator struct { - applyDNSConfigFunc func() error + applyDNSConfigFunc func(config hostDNSConfig) error restoreHostDNSFunc func() error } -func (m *mockHostConfigurator) applyDNSConfig() error { +func (m *mockHostConfigurator) applyDNSConfig(config hostDNSConfig) error { if m.applyDNSConfigFunc != nil { - return m.applyDNSConfigFunc() + return m.applyDNSConfigFunc(config) } return fmt.Errorf("method applyDNSSettings is not implemented") } @@ -35,7 +44,42 @@ func (m *mockHostConfigurator) restoreHostDNS() error { func newNoopHostMocker() hostManager { return &mockHostConfigurator{ - applyDNSConfigFunc: func() error { return nil }, + applyDNSConfigFunc: func(config hostDNSConfig) error { return nil }, restoreHostDNSFunc: func() error { return nil }, } } + +func isRootZoneDomain(domain string) bool { + return domain == nbdns.RootZone || domain == "" +} + +func dnsConfigToHostDNSConfig(dnsConfig nbdns.Config, ip string, port int) hostDNSConfig { + config := hostDNSConfig{ + routeAll: false, + serverIP: ip, + serverPort: port, + } + for _, nsConfig := range dnsConfig.NameServerGroups { + if nsConfig.Primary { + config.routeAll = true + config.domains = []domainConfig{} + break + } + + for _, domain := range nsConfig.Domains { + config.domains = append(config.domains, domainConfig{ + domain: strings.TrimSuffix(domain, "."), + matchOnly: true, + }) + } + } + + for _, customZone := range dnsConfig.CustomZones { + config.domains = append(config.domains, domainConfig{ + domain: strings.TrimSuffix(customZone.Domain, "."), + matchOnly: false, + }) + } + + return config +} diff --git a/client/internal/dns/host_darwin.go b/client/internal/dns/host_darwin.go index 9fdf299f949..c8d92bff0d6 100644 --- a/client/internal/dns/host_darwin.go +++ b/client/internal/dns/host_darwin.go @@ -24,6 +24,7 @@ const ( digitSymbol = "# " scutilPath = "/usr/sbin/scutil" searchSuffix = "Search" + matchSuffix = "Match" ) type systemConfigurator struct { @@ -37,37 +38,64 @@ func newHostManager(_ *iface.WGIface) hostManager { } } -func (s *systemConfigurator) applyDNSSettings(domains []string, ip string, port int) error { +func (s *systemConfigurator) applyDNSConfig(config hostDNSConfig) error { var err error - for _, domain := range domains { - if isRootZoneDomain(domain) { - err = s.addDNSSetupForAll(ip, port) - if err != nil { - log.Error(err) - } - continue + + if config.routeAll { + err = s.addDNSSetupForAll(config.serverIP, config.serverPort) + if err != nil { + return err } - err = s.addDNSStateForDomain(domain, ip, port) + log.Infof("configured %s:%d as main DNS server for this peer", config.serverIP, config.serverPort) + } else if s.primaryServiceID != "" { + err = s.removeKeyFromSystemConfig(getKeyWithInput(primaryServiceSetupKeyFormat, s.primaryServiceID)) if err != nil { - log.Error(err) + return err } + s.primaryServiceID = "" + log.Infof("removed %s:%d as main DNS server for this peer", config.serverIP, config.serverPort) } - return nil -} -func (s *systemConfigurator) addSearchDomain(domain string, ip string, port int) error { - key := getKeyWithInput(netbirdDNSStateKeyFormat, domain+searchSuffix) - err := s.addDNSState(key, domain, ip, port, true) + var ( + searchDomains []string + matchDomains []string + ) + + for _, dConf := range config.domains { + if dConf.matchOnly { + matchDomains = append(matchDomains, dConf.domain) + continue + } + searchDomains = append(searchDomains, dConf.domain) + } + + matchKey := getKeyWithInput(netbirdDNSStateKeyFormat, matchSuffix) + if len(matchDomains) != 0 { + err = s.addMatchDomains(matchKey, strings.Join(matchDomains, " "), config.serverIP, config.serverPort) + } else { + err = s.removeKeyFromSystemConfig(matchKey) + } if err != nil { return err } - s.createdKeys[key] = struct{}{} + log.Infof("added %d match domains to the state. Domain list: %s", len(matchDomains), matchDomains) + + searchKey := getKeyWithInput(netbirdDNSStateKeyFormat, searchSuffix) + if len(searchDomains) != 0 { + err = s.addSearchDomains(searchKey, strings.Join(searchDomains, " "), config.serverIP, config.serverPort) + } else { + err = s.removeKeyFromSystemConfig(searchKey) + } + if err != nil { + return err + } + log.Infof("added %d search domains to the state. Domain list: %s", len(searchDomains), searchDomains) return nil } -func (s *systemConfigurator) removeDNSSettings() error { +func (s *systemConfigurator) restoreHostDNS() error { lines := "" for key := range s.createdKeys { lines += buildRemoveKeyOperation(key) @@ -84,45 +112,31 @@ func (s *systemConfigurator) removeDNSSettings() error { return nil } -func (s *systemConfigurator) removeDomainSettings(domains []string) error { - var err error - for _, domain := range domains { - if isRootZoneDomain(domain) { - if s.primaryServiceID != "" { - err = removeKeyFromSystemConfig(getKeyWithInput(primaryServiceSetupKeyFormat, s.primaryServiceID)) - if err != nil { - log.Errorf("unable to remove primary service configuration, got error: %s", err) - continue - } - s.primaryServiceID = "" - } - continue - } +func (s *systemConfigurator) removeKeyFromSystemConfig(key string) error { + line := buildRemoveKeyOperation(key) + _, err := runSystemConfigCommand(wrapCommand(line)) + if err != nil { + return err + } - key := getKeyWithInput(netbirdDNSStateKeyFormat, domain) - err = removeKeyFromSystemConfig(key) - if err != nil { - log.Errorf("unable to remove system configuration for domain %s and key %s", domain, key) - continue - } + delete(s.createdKeys, key) - delete(s.createdKeys, key) - } return nil } -func removeKeyFromSystemConfig(key string) error { - line := buildRemoveKeyOperation(key) - _, err := runSystemConfigCommand(wrapCommand(line)) +func (s *systemConfigurator) addSearchDomains(key, domains string, ip string, port int) error { + err := s.addDNSState(key, domains, ip, port, true) if err != nil { return err } + + s.createdKeys[key] = struct{}{} + return nil } -func (s *systemConfigurator) addDNSStateForDomain(domain, dnsServer string, port int) error { - key := getKeyWithInput(netbirdDNSStateKeyFormat, domain) - err := s.addDNSState(key, domain, dnsServer, port, false) +func (s *systemConfigurator) addMatchDomains(key, domains, dnsServer string, port int) error { + err := s.addDNSState(key, domains, dnsServer, port, false) if err != nil { return err } @@ -132,13 +146,12 @@ func (s *systemConfigurator) addDNSStateForDomain(domain, dnsServer string, port return nil } -func (s *systemConfigurator) addDNSState(state, domain, dnsServer string, port int, enableSearch bool) error { +func (s *systemConfigurator) addDNSState(state, domains, dnsServer string, port int, enableSearch bool) error { noSearch := "1" if enableSearch { noSearch = "0" } - lines := buildAddCommandLine(keyDomainName, domain) - lines += buildAddCommandLine(keySupplementalMatchDomains, arraySymbol+domain) + lines := buildAddCommandLine(keySupplementalMatchDomains, arraySymbol+domains) lines += buildAddCommandLine(keySupplementalMatchDomainsNoSearch, digitSymbol+noSearch) lines += buildAddCommandLine(keyServerAddresses, arraySymbol+dnsServer) lines += buildAddCommandLine(ServerPort, digitSymbol+strconv.Itoa(port)) @@ -148,7 +161,7 @@ func (s *systemConfigurator) addDNSState(state, domain, dnsServer string, port i _, err := runSystemConfigCommand(stdinCommands) if err != nil { - return fmt.Errorf("got error while applying dns state for domain %s, error: %s", domain, err) + return fmt.Errorf("got error while applying state for domains %s, error: %s", domains, err) } return nil } diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 474b4abdb51..c4f0500e357 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -9,7 +9,6 @@ import ( log "github.com/sirupsen/logrus" "net" "net/netip" - "strings" "sync" "time" ) @@ -122,7 +121,7 @@ func (s *DefaultServer) Stop() { defer s.mux.Unlock() s.stop() - err := s.hostManager.removeDNSSettings() + err := s.hostManager.restoreHostDNS() if err != nil { log.Error(err) } @@ -183,33 +182,15 @@ func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) erro } muxUpdates := append(localMuxUpdates, upstreamMuxUpdates...) - var domains []string - for _, u := range muxUpdates { - domains = append(domains, strings.TrimSuffix(u.domain, ".")) - } - domainsToRemove := s.updateMux(muxUpdates) + s.updateMux(muxUpdates) s.updateLocalResolver(localRecords) - err = s.hostManager.removeDomainSettings(domainsToRemove) - if err != nil { - log.Error(err) - } - - err = s.hostManager.applyDNSSettings(domains, defaultIP, s.runtimePort) + err = s.hostManager.applyDNSConfig(dnsConfigToHostDNSConfig(update, defaultIP, s.runtimePort)) if err != nil { log.Error(err) } - for _, localMuxUpdate := range localMuxUpdates { - err = s.hostManager.addSearchDomain(strings.TrimSuffix(localMuxUpdate.domain, "."), defaultIP, s.runtimePort) - if err != nil { - log.Error(err) - } - } - - log.Debugf("applied dns update, added %d peer records, %d domain records and removed %d", len(localRecords), len(muxUpdates), len(domainsToRemove)) - s.updateSerial = serial return nil @@ -288,7 +269,7 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam return muxUpdates, nil } -func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) []string { +func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) { muxUpdateMap := make(registrationMap) for _, update := range muxUpdates { @@ -296,17 +277,14 @@ func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) []string { muxUpdateMap[update.domain] = struct{}{} } - var removed []string for key := range s.dnsMuxMap { _, found := muxUpdateMap[key] if !found { s.deregisterMux(key) - removed = append(removed, key) } } s.dnsMuxMap = muxUpdateMap - return removed } func (s *DefaultServer) updateLocalResolver(update map[string]nbdns.SimpleRecord) { From 6753a399fb5f1146b7ae77adb905f718ef5333b2 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sat, 19 Nov 2022 00:46:45 +0100 Subject: [PATCH 24/48] check socket if not windows --- iface/iface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iface/iface.go b/iface/iface.go index bdfa78abb99..206b9d5aa2c 100644 --- a/iface/iface.go +++ b/iface/iface.go @@ -79,7 +79,7 @@ func (w *WGIface) Close() error { return err } - if runtime.GOOS == "darwin" { + if runtime.GOOS != "windows" { sockPath := "/var/run/wireguard/" + w.Name + ".sock" if _, statErr := os.Stat(sockPath); statErr == nil { statErr = os.Remove(sockPath) From b37aa021ee7f7927184addaf2ce5c05f33566c58 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sat, 19 Nov 2022 02:43:02 +0100 Subject: [PATCH 25/48] updated windows configurator with new interface --- client/internal/dns/host_windows.go | 227 +++++++++++++--------------- 1 file changed, 104 insertions(+), 123 deletions(-) diff --git a/client/internal/dns/host_windows.go b/client/internal/dns/host_windows.go index e5a767128c7..5444bfe8e13 100644 --- a/client/internal/dns/host_windows.go +++ b/client/internal/dns/host_windows.go @@ -11,7 +11,7 @@ import ( ) const ( - dnsPolicyConfigPathFormat = "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig\\NetBird-%s" + dnsPolicyConfigMatchPath = "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig\\NetBird-Match" dnsPolicyConfigVersionKey = "Version" dnsPolicyConfigVersionValue = 2 dnsPolicyConfigNameKey = "Name" @@ -28,79 +28,83 @@ const ( ) type registryConfigurator struct { - luid winipcfg.LUID - createdKeys map[string]struct{} - addedSearchDomains map[string]struct{} + luid winipcfg.LUID + existingSearchDomains []string } func newHostManager(wgInterface *iface.WGIface) hostManager { windowsDevice := wgInterface.Interface.(*driver.Adapter) luid := windowsDevice.LUID() return ®istryConfigurator{ - luid: luid, - createdKeys: make(map[string]struct{}), - addedSearchDomains: make(map[string]struct{}), + luid: luid, } } -func (r *registryConfigurator) applyDNSSettings(domains []string, ip string, _ int) error { +func (r *registryConfigurator) applyDNSConfig(config hostDNSConfig) error { var err error - for _, domain := range domains { - if isRootZoneDomain(domain) { - err = r.addDNSSetupForAll(ip) - if err != nil { - log.Error(err) - } - continue + if config.routeAll { + err = r.addDNSSetupForAll(config.serverIP) + if err != nil { + return err } - err = r.addDNSStateForDomain(domain, ip) + } else { + err = r.deleteInterfaceRegistryKeyProperty(interfaceConfigNameServerKey) if err != nil { - log.Error(err) + return err } + log.Infof("removed %s as main DNS server for this peer", config.serverIP) } - return nil -} -func (r *registryConfigurator) addDNSSetupForAll(ip string) error { - err := r.setInterfaceRegistryKeyStringValue(interfaceConfigNameServerKey, ip) - if err != nil { - return fmt.Errorf("adding dns setup for all failed with error: %s", err) - } + var ( + searchDomains []string + matchDomains []string + ) - return nil -} - -func (r *registryConfigurator) getInterfaceRegistryKey() (registry.Key, error) { - var regKey registry.Key + for _, dConf := range config.domains { + if !dConf.matchOnly { + searchDomains = append(searchDomains, dConf.domain) + } + matchDomains = append(matchDomains, "."+dConf.domain) + } - guid, err := r.luid.GUID() + if len(matchDomains) != 0 { + err = r.addDNSMatchPolicy(matchDomains, config.serverIP) + } else { + err = removeRegistryKeyFromDNSPolicyConfig(dnsPolicyConfigMatchPath) + } if err != nil { - return regKey, fmt.Errorf("unable to get interface GUID, error: %s", err) + return err } - regKeyPath := interfaceConfigPath + "\\" + guid.String() - - regKey, err = registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.SET_VALUE) + err = r.updateSearchDomains(searchDomains) if err != nil { - return regKey, fmt.Errorf("unable to open the interface registry key, key: HKEY_LOCAL_MACHINE\\%s, error: %s", regKeyPath, err) + return err } - return regKey, nil + return nil } -func (r *registryConfigurator) addDNSStateForDomain(domain, ip string) error { - regKeyPath := getRegistryKeyPath(dnsPolicyConfigPathFormat, domain) +func (r *registryConfigurator) addDNSSetupForAll(ip string) error { + err := r.setInterfaceRegistryKeyStringValue(interfaceConfigNameServerKey, ip) + if err != nil { + return fmt.Errorf("adding dns setup for all failed with error: %s", err) + } + log.Infof("configured %s:53 as main DNS server for this peer", ip) + return nil +} - _, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.QUERY_VALUE) +func (r *registryConfigurator) addDNSMatchPolicy(domains []string, ip string) error { + _, err := registry.OpenKey(registry.LOCAL_MACHINE, dnsPolicyConfigMatchPath, registry.QUERY_VALUE) if err == nil { - err = registry.DeleteKey(registry.LOCAL_MACHINE, regKeyPath) + err = registry.DeleteKey(registry.LOCAL_MACHINE, dnsPolicyConfigMatchPath) if err != nil { - return fmt.Errorf("unable to remove existing key from registry, key: HKEY_LOCAL_MACHINE\\%s, error: %s", regKeyPath, err) + return fmt.Errorf("unable to remove existing key from registry, key: HKEY_LOCAL_MACHINE\\%s, error: %s", dnsPolicyConfigMatchPath, err) } } - regKey, _, err := registry.CreateKey(registry.LOCAL_MACHINE, regKeyPath, registry.SET_VALUE) + + regKey, _, err := registry.CreateKey(registry.LOCAL_MACHINE, dnsPolicyConfigMatchPath, registry.SET_VALUE) if err != nil { - return fmt.Errorf("unable to create registry key, key: HKEY_LOCAL_MACHINE\\%s, error: %s", regKeyPath, err) + return fmt.Errorf("unable to create registry key, key: HKEY_LOCAL_MACHINE\\%s, error: %s", dnsPolicyConfigMatchPath, err) } err = regKey.SetDWordValue(dnsPolicyConfigVersionKey, dnsPolicyConfigVersionValue) @@ -108,9 +112,7 @@ func (r *registryConfigurator) addDNSStateForDomain(domain, ip string) error { return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigVersionKey, err) } - prefixDotedDomain := "." + domain - - err = regKey.SetStringsValue(dnsPolicyConfigNameKey, []string{prefixDotedDomain}) + err = regKey.SetStringsValue(dnsPolicyConfigNameKey, domains) if err != nil { return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigNameKey, err) } @@ -125,87 +127,96 @@ func (r *registryConfigurator) addDNSStateForDomain(domain, ip string) error { return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigConfigOptionsKey, err) } - r.createdKeys[regKeyPath] = struct{}{} + log.Infof("added %d match domains to the state. Domain list: %s", len(domains), domains) return nil } -func (r *registryConfigurator) addSearchDomain(domain string, ip string, port int) error { +func (r *registryConfigurator) restoreHostDNS() error { + err := removeRegistryKeyFromDNSPolicyConfig(dnsPolicyConfigMatchPath) + if err != nil { + log.Error(err) + } + + return r.updateSearchDomains([]string{}) +} + +func (r *registryConfigurator) updateSearchDomains(domains []string) error { value, err := getLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey) if err != nil { return fmt.Errorf("unable to get current search domains failed with error: %s", err) } valueList := strings.Split(value, ",") - for _, existingDomain := range valueList { - if existingDomain == domain { - log.Debugf("not adding domain %s to the search list. Already exist", domain) - return nil - } + setExisting := false + if len(r.existingSearchDomains) == 0 { + r.existingSearchDomains = valueList + setExisting = true } - valueList = append(valueList, domain) + if len(domains) == 0 && setExisting { + log.Infof("added %d search domains to the registry. Domain list: %s", len(domains), domains) + return nil + } + + newList := append(r.existingSearchDomains, domains...) - err = setLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey, strings.Join(valueList, ",")) + err = setLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey, strings.Join(newList, ",")) if err != nil { return fmt.Errorf("adding search domain failed with error: %s", err) } - r.addedSearchDomains[domain] = struct{}{} + log.Infof("updated the search domains in the registry with %d domains. Domain list: %s", len(domains), domains) return nil } -func (r *registryConfigurator) removeDomainSettings(domains []string) error { - var err error - for _, domain := range domains { - if isRootZoneDomain(domain) { - err = r.deleteInterfaceRegistryKey(interfaceConfigNameServerKey) - if err != nil { - log.Error(err) - } - continue - } - - regKeyPath := getRegistryKeyPath(dnsPolicyConfigPathFormat, domain) - err = removeRegistryKeyFromDNSPolicyConfig(regKeyPath) - if err != nil { - log.Error(err) - continue - } +func (r *registryConfigurator) setInterfaceRegistryKeyStringValue(key, value string) error { + regKey, err := r.getInterfaceRegistryKey() + if err != nil { + return err + } + defer regKey.Close() - delete(r.createdKeys, regKeyPath) + err = regKey.SetStringValue(key, value) + if err != nil { + return fmt.Errorf("applying key %s with value \"%s\" for interface failed with error: %s", key, value, err) } + return nil } -func (r *registryConfigurator) removeDNSSettings() error { - for key := range r.createdKeys { - err := removeRegistryKeyFromDNSPolicyConfig(key) - if err != nil { - log.Error(err) - } +func (r *registryConfigurator) deleteInterfaceRegistryKeyProperty(propertyKey string) error { + regKey, err := r.getInterfaceRegistryKey() + if err != nil { + return err } + defer regKey.Close() - value, err := getLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey) + err = regKey.DeleteValue(propertyKey) if err != nil { - return fmt.Errorf("unable to get current search domains failed with error: %s", err) + return fmt.Errorf("deleting registry key %s for interface failed with error: %s", propertyKey, err) } - existingValueList := strings.Split(value, ",") - var newValueList []string - for _, existingDomain := range existingValueList { - _, found := r.addedSearchDomains[existingDomain] - if !found { - newValueList = append(newValueList, existingDomain) - } + return nil +} + +func (r *registryConfigurator) getInterfaceRegistryKey() (registry.Key, error) { + var regKey registry.Key + + guid, err := r.luid.GUID() + if err != nil { + return regKey, fmt.Errorf("unable to get interface GUID, error: %s", err) } - return setLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey, strings.Join(newValueList, ",")) -} + regKeyPath := interfaceConfigPath + "\\" + guid.String() + + regKey, err = registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.SET_VALUE) + if err != nil { + return regKey, fmt.Errorf("unable to open the interface registry key, key: HKEY_LOCAL_MACHINE\\%s, error: %s", regKeyPath, err) + } -func getRegistryKeyPath(format, input string) string { - return fmt.Sprintf(format, input) + return regKey, nil } func removeRegistryKeyFromDNSPolicyConfig(regKeyPath string) error { @@ -249,33 +260,3 @@ func setLocalMachineRegistryKeyStringValue(keyPath, key, value string) error { return nil } - -func (r *registryConfigurator) setInterfaceRegistryKeyStringValue(key, value string) error { - regKey, err := r.getInterfaceRegistryKey() - if err != nil { - return err - } - defer regKey.Close() - - err = regKey.SetStringValue(key, value) - if err != nil { - return fmt.Errorf("applying key %s with value \"%s\" for interface failed with error: %s", key, value, err) - } - - return nil -} - -func (r *registryConfigurator) deleteInterfaceRegistryKey(key string) error { - regKey, err := r.getInterfaceRegistryKey() - if err != nil { - return err - } - defer regKey.Close() - - err = regKey.DeleteValue(key) - if err != nil { - return fmt.Errorf("deleting key %s for interface failed with error: %s", key, err) - } - - return nil -} From 8aac2aa6d87763fdfc06b1145fa28490865c96c2 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sat, 19 Nov 2022 15:31:16 +0100 Subject: [PATCH 26/48] adjust logs --- client/internal/dns/host.go | 2 -- client/internal/dns/host_darwin.go | 22 +++++++++++++++------- client/internal/dns/host_windows.go | 9 ++++++--- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/client/internal/dns/host.go b/client/internal/dns/host.go index f7cdaa441e0..9c6a2135cf1 100644 --- a/client/internal/dns/host.go +++ b/client/internal/dns/host.go @@ -62,8 +62,6 @@ func dnsConfigToHostDNSConfig(dnsConfig nbdns.Config, ip string, port int) hostD for _, nsConfig := range dnsConfig.NameServerGroups { if nsConfig.Primary { config.routeAll = true - config.domains = []domainConfig{} - break } for _, domain := range nsConfig.Domains { diff --git a/client/internal/dns/host_darwin.go b/client/internal/dns/host_darwin.go index c8d92bff0d6..f583d4027fd 100644 --- a/client/internal/dns/host_darwin.go +++ b/client/internal/dns/host_darwin.go @@ -15,7 +15,6 @@ const ( netbirdDNSStateKeyFormat = "State:/Network/Service/NetBird-%s/DNS" globalIPv4State = "State:/Network/Global/IPv4" primaryServiceSetupKeyFormat = "Setup:/Network/Service/%s/DNS" - keyDomainName = "DomainName" keySupplementalMatchDomains = "SupplementalMatchDomains" keySupplementalMatchDomainsNoSearch = "SupplementalMatchDomainsNoSearch" keyServerAddresses = "ServerAddresses" @@ -28,6 +27,7 @@ const ( ) type systemConfigurator struct { + // primaryServiceID primary interface in the system. AKA the interface with the default route primaryServiceID string createdKeys map[string]struct{} } @@ -46,14 +46,13 @@ func (s *systemConfigurator) applyDNSConfig(config hostDNSConfig) error { if err != nil { return err } - log.Infof("configured %s:%d as main DNS server for this peer", config.serverIP, config.serverPort) } else if s.primaryServiceID != "" { err = s.removeKeyFromSystemConfig(getKeyWithInput(primaryServiceSetupKeyFormat, s.primaryServiceID)) if err != nil { return err } s.primaryServiceID = "" - log.Infof("removed %s:%d as main DNS server for this peer", config.serverIP, config.serverPort) + log.Infof("removed %s:%d as main DNS resolver for this peer", config.serverIP, config.serverPort) } var ( @@ -73,24 +72,23 @@ func (s *systemConfigurator) applyDNSConfig(config hostDNSConfig) error { if len(matchDomains) != 0 { err = s.addMatchDomains(matchKey, strings.Join(matchDomains, " "), config.serverIP, config.serverPort) } else { + log.Infof("removing match domains from the system") err = s.removeKeyFromSystemConfig(matchKey) } if err != nil { return err } - log.Infof("added %d match domains to the state. Domain list: %s", len(matchDomains), matchDomains) - searchKey := getKeyWithInput(netbirdDNSStateKeyFormat, searchSuffix) if len(searchDomains) != 0 { err = s.addSearchDomains(searchKey, strings.Join(searchDomains, " "), config.serverIP, config.serverPort) } else { + log.Infof("removing search domains from the system") err = s.removeKeyFromSystemConfig(searchKey) } if err != nil { return err } - log.Infof("added %d search domains to the state. Domain list: %s", len(searchDomains), searchDomains) return nil } @@ -99,9 +97,15 @@ func (s *systemConfigurator) restoreHostDNS() error { lines := "" for key := range s.createdKeys { lines += buildRemoveKeyOperation(key) + keyType := "search" + if strings.Contains(key, matchSuffix) { + keyType = "match" + } + log.Infof("removing %s domains from system", keyType) } if s.primaryServiceID != "" { lines += buildRemoveKeyOperation(getKeyWithInput(primaryServiceSetupKeyFormat, s.primaryServiceID)) + log.Infof("restoring DNS resolver configuration for system") } _, err := runSystemConfigCommand(wrapCommand(lines)) if err != nil { @@ -130,6 +134,8 @@ func (s *systemConfigurator) addSearchDomains(key, domains string, ip string, po return err } + log.Infof("added %d search domains to the state. Domain list: %s", len(strings.Split(domains, " ")), domains) + s.createdKeys[key] = struct{}{} return nil @@ -141,6 +147,8 @@ func (s *systemConfigurator) addMatchDomains(key, domains, dnsServer string, por return err } + log.Infof("added %d match domains to the state. Domain list: %s", len(strings.Split(domains, " ")), domains) + s.createdKeys[key] = struct{}{} return nil @@ -176,7 +184,7 @@ func (s *systemConfigurator) addDNSSetupForAll(dnsServer string, port int) error if err != nil { return err } - + log.Infof("configured %s:%d as main DNS resolver for this peer", dnsServer, port) s.primaryServiceID = primaryServiceKey return nil } diff --git a/client/internal/dns/host_windows.go b/client/internal/dns/host_windows.go index 5444bfe8e13..4dbaa0ac0a3 100644 --- a/client/internal/dns/host_windows.go +++ b/client/internal/dns/host_windows.go @@ -29,6 +29,7 @@ const ( type registryConfigurator struct { luid winipcfg.LUID + routingAll bool existingSearchDomains []string } @@ -47,12 +48,13 @@ func (r *registryConfigurator) applyDNSConfig(config hostDNSConfig) error { if err != nil { return err } - } else { + } else if r.routingAll { err = r.deleteInterfaceRegistryKeyProperty(interfaceConfigNameServerKey) if err != nil { return err } - log.Infof("removed %s as main DNS server for this peer", config.serverIP) + r.routingAll = false + log.Infof("removed %s as main DNS forwarder for this peer", config.serverIP) } var ( @@ -89,7 +91,8 @@ func (r *registryConfigurator) addDNSSetupForAll(ip string) error { if err != nil { return fmt.Errorf("adding dns setup for all failed with error: %s", err) } - log.Infof("configured %s:53 as main DNS server for this peer", ip) + r.routingAll = true + log.Infof("configured %s:53 as main DNS forwarder for this peer", ip) return nil } From 8561256b1d6bd7a282f55b62d0d37b2a8a007e84 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sat, 19 Nov 2022 18:12:34 +0100 Subject: [PATCH 27/48] update systemd with new interface methods --- .../dns/systemd_configurator_linux.go | 111 ++++++------------ 1 file changed, 39 insertions(+), 72 deletions(-) diff --git a/client/internal/dns/systemd_configurator_linux.go b/client/internal/dns/systemd_configurator_linux.go index 83646393c23..e701c4c9b60 100644 --- a/client/internal/dns/systemd_configurator_linux.go +++ b/client/internal/dns/systemd_configurator_linux.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/godbus/dbus/v5" "github.com/miekg/dns" + nbdns "github.com/netbirdio/netbird/dns" "github.com/netbirdio/netbird/iface" log "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -27,8 +28,8 @@ const ( ) type systemdDbusConfigurator struct { - dbusLinkObject dbus.ObjectPath - createdLinkedDomains map[string]systemdDbusLinkDomainsInput + dbusLinkObject dbus.ObjectPath + routingAll bool } // the types below are based on dbus specification, each field is mapped to a dbus type @@ -69,106 +70,72 @@ func newSystemdDbusConfigurator(wgInterface *iface.WGIface) hostManager { log.Debugf("got dbus Link interface: %s from net interface %s and index %d", s, iface.Name, iface.Index) return &systemdDbusConfigurator{ - dbusLinkObject: dbus.ObjectPath(s), - createdLinkedDomains: make(map[string]systemdDbusLinkDomainsInput), + dbusLinkObject: dbus.ObjectPath(s), } } -func (s *systemdDbusConfigurator) applyDNSSettings(domains []string, ip string, port int) error { - parsedIP := netip.MustParseAddr(ip).As4() +func (s *systemdDbusConfigurator) applyDNSConfig(config hostDNSConfig) error { + parsedIP := netip.MustParseAddr(config.serverIP).As4() defaultLinkInput := systemdDbusDNSInput{ Family: unix.AF_INET, Address: parsedIP[:], } err := s.callLinkMethod(systemdDbusSetDNSMethodSuffix, []systemdDbusDNSInput{defaultLinkInput}) if err != nil { - return fmt.Errorf("setting the interface DNS server %s:%d failed with error: %s", ip, port, err) + return fmt.Errorf("setting the interface DNS server %s:%d failed with error: %s", config.serverIP, config.serverPort, err) } - var domainsInput []systemdDbusLinkDomainsInput + var ( + searchDomains []string + matchDomains []string + domainsInput []systemdDbusLinkDomainsInput + ) + for _, dConf := range config.domains { + domainsInput = append(domainsInput, systemdDbusLinkDomainsInput{ + Domain: dns.Fqdn(dConf.domain), + MatchOnly: dConf.matchOnly, + }) + + if dConf.matchOnly { + matchDomains = append(matchDomains, dConf.domain) + continue + } + searchDomains = append(searchDomains, dConf.domain) + } - for _, domain := range domains { - if isRootZoneDomain(domain) { - err = s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, true) - if err != nil { - log.Errorf("setting link as default dns router, failed with error: %s", err) - } + if config.routeAll { + log.Infof("configured %s:%d as main DNS forwarder for this peer", config.serverIP, config.serverPort) + err = s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, true) + if err != nil { + return fmt.Errorf("setting link as default dns router, failed with error: %s", err) } domainsInput = append(domainsInput, systemdDbusLinkDomainsInput{ - Domain: dns.Fqdn(domain), + Domain: nbdns.RootZone, MatchOnly: true, }) + s.routingAll = true + } else if s.routingAll { + log.Infof("removing %s:%d as main DNS forwarder for this peer", config.serverIP, config.serverPort) } - err = s.addDNSStateForDomain(domainsInput) + + log.Infof("adding %d search domains and %d match domains. Search list: %s , Match list: %s", len(searchDomains), len(matchDomains), searchDomains, matchDomains) + err = s.setDomainsForInterface(domainsInput) if err != nil { log.Error(err) } return nil } -func (s *systemdDbusConfigurator) addDNSStateForDomain(domainsInput []systemdDbusLinkDomainsInput) error { +func (s *systemdDbusConfigurator) setDomainsForInterface(domainsInput []systemdDbusLinkDomainsInput) error { err := s.callLinkMethod(systemdDbusSetDomainsMethodSuffix, domainsInput) if err != nil { return fmt.Errorf("setting domains configuration failed with error: %s", err) } - for _, input := range domainsInput { - s.createdLinkedDomains[input.Domain] = input - } - return nil -} - -func (s *systemdDbusConfigurator) addSearchDomain(domain string, ip string, port int) error { - var newDomainsInput []systemdDbusLinkDomainsInput - - fqdnDomain := dns.Fqdn(domain) - - existingDomain, found := s.createdLinkedDomains[fqdnDomain] - if found && !existingDomain.MatchOnly { - return nil - } - - delete(s.createdLinkedDomains, fqdnDomain) - for _, existingInput := range s.createdLinkedDomains { - newDomainsInput = append(newDomainsInput, existingInput) - } - - newDomainsInput = append(newDomainsInput, systemdDbusLinkDomainsInput{ - Domain: fqdnDomain, - MatchOnly: false, - }) - - err := s.addDNSStateForDomain(newDomainsInput) - if err != nil { - return fmt.Errorf("setting domains configuration with search domain %s failed with error: %s", domain, err) - } - return s.flushCaches() } -func (s *systemdDbusConfigurator) removeDomainSettings(domains []string) error { - var err error - for _, domain := range domains { - if isRootZoneDomain(domain) { - err = s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, false) - if err != nil { - log.Errorf("setting link as non default dns router, failed with error: %s", err) - } - break - } - } - - // cleaning the configuration as it gets rebuild - emptyList := make([]systemdDbusLinkDomainsInput, 0) - err = s.callLinkMethod(systemdDbusSetDomainsMethodSuffix, emptyList) - if err != nil { - log.Error(err) - } - - s.createdLinkedDomains = make(map[string]systemdDbusLinkDomainsInput) - - return s.flushCaches() -} -func (s *systemdDbusConfigurator) removeDNSSettings() error { +func (s *systemdDbusConfigurator) restoreHostDNS() error { + log.Infof("reverting link settings and flushing cache") err := s.callLinkMethod(systemdDbusRevertMethodSuffix, nil) if err != nil { return fmt.Errorf("unable to revert link configuration, got error: %s", err) From 9ad3e7eb4e08a4480162037d5e533adfd9a365b5 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sat, 19 Nov 2022 18:15:13 +0100 Subject: [PATCH 28/48] rename systemd file --- .../dns/{systemd_configurator_linux.go => systemd_linux.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/internal/dns/{systemd_configurator_linux.go => systemd_linux.go} (100%) diff --git a/client/internal/dns/systemd_configurator_linux.go b/client/internal/dns/systemd_linux.go similarity index 100% rename from client/internal/dns/systemd_configurator_linux.go rename to client/internal/dns/systemd_linux.go From 0453ae5ed508003b47fe010981abb917885854dc Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sat, 19 Nov 2022 18:15:45 +0100 Subject: [PATCH 29/48] use netbird ip for listener --- client/internal/dns/server.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index c4f0500e357..3f0620d87e9 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -9,6 +9,7 @@ import ( log "github.com/sirupsen/logrus" "net" "net/netip" + "runtime" "sync" "time" ) @@ -40,6 +41,7 @@ type DefaultServer struct { updateSerial uint64 listenerIsRunning bool runtimePort int + runtimeIP string } type registrationMap map[string]struct{} @@ -52,9 +54,13 @@ type muxUpdate struct { // NewDefaultServer returns a new dns server func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface) *DefaultServer { mux := dns.NewServeMux() + listenIP := defaultIP + if runtime.GOOS != "darwin" && wgInterface != nil { + listenIP = wgInterface.GetAddress().IP.String() + } dnsServer := &dns.Server{ - Addr: fmt.Sprintf("%s:%d", defaultIP, port), + Addr: fmt.Sprintf("%s:%d", listenIP, port), Net: "udp", Handler: mux, UDPSize: 65535, @@ -72,7 +78,10 @@ func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface) *DefaultS registeredMap: make(registrationMap), }, wgInterface: wgInterface, + runtimePort: port, + runtimeIP: listenIP, } + // this should only happen on tests if wgInterface.Interface == nil { log.Debugf("returning a server without host manager") @@ -90,7 +99,7 @@ func (s *DefaultServer) Start() { if err != nil { log.Warnf("using a custom port for dns server") s.runtimePort = customPort - s.server.Addr = fmt.Sprintf("%s:%d", defaultIP, customPort) + s.server.Addr = fmt.Sprintf("%s:%d", s.runtimeIP, customPort) } else { err = probeListener.Close() if err != nil { @@ -186,7 +195,7 @@ func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) erro s.updateMux(muxUpdates) s.updateLocalResolver(localRecords) - err = s.hostManager.applyDNSConfig(dnsConfigToHostDNSConfig(update, defaultIP, s.runtimePort)) + err = s.hostManager.applyDNSConfig(dnsConfigToHostDNSConfig(update, s.runtimeIP, s.runtimePort)) if err != nil { log.Error(err) } From 84387cd89aadfc05293ffc3c4456de5feecb352c Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 20 Nov 2022 18:39:55 +0100 Subject: [PATCH 30/48] add installer log --- client/cmd/service_installer.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/cmd/service_installer.go b/client/cmd/service_installer.go index 86439ad1782..8efb5ee60d6 100644 --- a/client/cmd/service_installer.go +++ b/client/cmd/service_installer.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "path/filepath" "runtime" "github.com/spf13/cobra" @@ -32,8 +33,13 @@ var installCmd = &cobra.Command{ } if managementURL != "" { - svcConfig.Arguments = append(svcConfig.Arguments, "--management-url") - svcConfig.Arguments = append(svcConfig.Arguments, managementURL) + svcConfig.Arguments = append(svcConfig.Arguments, "--management-url", managementURL) + } + + if logFile != "console" { + svcConfig.Arguments = append(svcConfig.Arguments, "--log-file", logFile) + svcConfig.Option["LogOutput"] = true + svcConfig.Option["LogDirectory"] = filepath.Dir(logFile) } if runtime.GOOS == "linux" { From c8f99346ef4d1c14ce1328f39a8cd583afd18f19 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 20 Nov 2022 20:55:57 +0100 Subject: [PATCH 31/48] use buildRecordKey for local records --- client/internal/dns/local.go | 15 +++++++++++++-- client/internal/dns/server.go | 7 ++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/client/internal/dns/local.go b/client/internal/dns/local.go index 9f1a38ac80f..fe32b6a97a9 100644 --- a/client/internal/dns/local.go +++ b/client/internal/dns/local.go @@ -1,6 +1,7 @@ package dns import ( + "fmt" "github.com/miekg/dns" nbdns "github.com/netbirdio/netbird/dns" log "github.com/sirupsen/logrus" @@ -17,8 +18,10 @@ func (d *localResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { log.Debugf("received question: %#v\n", r.Question[0]) replyMessage := &dns.Msg{} replyMessage.SetReply(r) + replyMessage.Rcode = dns.RcodeNameError response := d.lookupRecord(r) if response != nil { + replyMessage.Rcode = dns.RcodeSuccess replyMessage.Answer = append(replyMessage.Answer, response) } @@ -29,7 +32,8 @@ func (d *localResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { } func (d *localResolver) lookupRecord(r *dns.Msg) dns.RR { - record, found := d.records.Load(r.Question[0].Name) + question := r.Question[0] + record, found := d.records.Load(buildRecordKey(question.Name, question.Qclass, question.Qtype)) if !found { return nil } @@ -43,7 +47,8 @@ func (d *localResolver) registerRecord(record nbdns.SimpleRecord) error { return err } - d.records.Store(fullRecord.Header().Name, fullRecord) + header := fullRecord.Header() + d.records.Store(buildRecordKey(header.Name, header.Class, header.Rrtype), fullRecord) return nil } @@ -51,3 +56,9 @@ func (d *localResolver) registerRecord(record nbdns.SimpleRecord) error { func (d *localResolver) deleteRecord(recordKey string) { d.records.Delete(dns.Fqdn(recordKey)) } + +func buildRecordKey(name string, class, qtype uint16) string { + key := fmt.Sprintf("%s_%d_%d", name, class, qtype) + log.Debugf("generated the following key: %s", key) + return key +} diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 3f0620d87e9..a972a5b8e9d 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -222,7 +222,12 @@ func (s *DefaultServer) buildLocalHandlerUpdate(customZones []nbdns.CustomZone) }) for _, record := range customZone.Records { - localRecords[record.Name] = record + var class uint16 = dns.ClassINET + if record.Class != nbdns.DefaultClass { + return nil, nil, fmt.Errorf("received an invalid class type: %s", record.Class) + } + key := buildRecordKey(record.Name, class, uint16(record.Type)) + localRecords[key] = record } } return muxUpdates, localRecords, nil From 0288f470f42427cea02725d53f3d3b7aaeb0faee Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 20 Nov 2022 20:57:23 +0100 Subject: [PATCH 32/48] create option map --- client/cmd/service.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client/cmd/service.go b/client/cmd/service.go index 7a67298502b..18fe5d6212b 100644 --- a/client/cmd/service.go +++ b/client/cmd/service.go @@ -32,6 +32,7 @@ func newSVCConfig() *service.Config { Name: name, DisplayName: "Netbird", Description: "A WireGuard-based mesh network that connects your devices into a single private network.", + Option: make(service.KeyValue), } } From d0c4439f587b7d0188863f59857cbd43f3580922 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 20 Nov 2022 20:58:12 +0100 Subject: [PATCH 33/48] check if values is nil before call --- client/internal/dns/systemd_linux.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/client/internal/dns/systemd_linux.go b/client/internal/dns/systemd_linux.go index e701c4c9b60..d811203c9be 100644 --- a/client/internal/dns/systemd_linux.go +++ b/client/internal/dns/systemd_linux.go @@ -136,6 +136,9 @@ func (s *systemdDbusConfigurator) setDomainsForInterface(domainsInput []systemdD func (s *systemdDbusConfigurator) restoreHostDNS() error { log.Infof("reverting link settings and flushing cache") + if !isDbusListenerRunning(systemdResolvedDest, s.dbusLinkObject) { + return nil + } err := s.callLinkMethod(systemdDbusRevertMethodSuffix, nil) if err != nil { return fmt.Errorf("unable to revert link configuration, got error: %s", err) @@ -170,7 +173,12 @@ func (s *systemdDbusConfigurator) callLinkMethod(method string, value any) error ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) defer cancel() - err = obj.CallWithContext(ctx, method, dbusDefaultFlag, value).Store() + if value != nil { + err = obj.CallWithContext(ctx, method, dbusDefaultFlag, value).Store() + } else { + err = obj.CallWithContext(ctx, method, dbusDefaultFlag).Store() + } + if err != nil { return fmt.Errorf("got error while calling command with context, err: %s", err) } From b716e4576103afc14d827c7404401dee773473ae Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 20 Nov 2022 21:00:28 +0100 Subject: [PATCH 34/48] adjust nm to new interface --- client/internal/dns/host_linux.go | 9 +- ...tior_linux.go => network_manager_linux.go} | 211 ++++++------------ 2 files changed, 79 insertions(+), 141 deletions(-) rename client/internal/dns/{nm_configuratior_linux.go => network_manager_linux.go} (59%) diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index 537ca60d74a..04add0b8c7a 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -27,8 +27,11 @@ func newHostManager(wgInterface *iface.WGIface) hostManager { switch osManager { case networkManager: return newNetworkManagerDbusConfigurator(wgInterface) - default: + case systemdManager: return newSystemdDbusConfigurator(wgInterface) + default: + return newNoopHostMocker() + //return newSystemdDbusConfigurator(wgInterface) } } @@ -47,9 +50,13 @@ func getOSDNSManagerType() osManagerType { return fileManager } if strings.Contains(text, "NetworkManager") && isDbusListenerRunning(networkManagerDest, networkManagerDbusObjectNode) { + log.Debugf("is nm running on supported v? %t", isNetworkManagerSupportedVersion()) return networkManager } if strings.Contains(text, "systemd-resolved") && isDbusListenerRunning(systemdResolvedDest, systemdDbusObjectNode) { + //if isDbusListenerRunning(networkManagerDest, networkManagerDbusObjectNode) { + // return networkManager + //} return systemdManager } if strings.Contains(text, "resolvconf") { diff --git a/client/internal/dns/nm_configuratior_linux.go b/client/internal/dns/network_manager_linux.go similarity index 59% rename from client/internal/dns/nm_configuratior_linux.go rename to client/internal/dns/network_manager_linux.go index c9952d3d59c..cc992ad2b9e 100644 --- a/client/internal/dns/nm_configuratior_linux.go +++ b/client/internal/dns/network_manager_linux.go @@ -11,7 +11,6 @@ import ( log "github.com/sirupsen/logrus" "net/netip" "regexp" - "sort" "time" ) @@ -20,6 +19,7 @@ const ( networkManagerDbusObjectNode = "/org/freedesktop/NetworkManager" networkManagerDbusDnsManagerObjectNode = "/org/freedesktop/NetworkManager/DnsManager" networkManagerDbusDnsManagerModeProperty = "org.freedesktop.NetworkManager.DnsManager.Mode" + networkManagerDbusVersionProperty = "org.freedesktop.NetworkManager.Version" networkManagerDbusGetDeviceByIpIfaceMethod = networkManagerDest + ".GetDeviceByIpIface" networkManagerDbusDeviceInterface = "org.freedesktop.NetworkManager.Device" networkManagerDbusDeviceGetAppliedConnectionMethod = networkManagerDbusDeviceInterface + ".GetAppliedConnection" @@ -36,10 +36,12 @@ const ( networkManagerDbusWithMatchDomainPriority int32 = 0 networkManagerDbusSearchDomainOnlyPriority int32 = 50 networkManagerDbusSearchDefaultPriority int32 = 100 + supportedNetworkManagerVersionConstraint = ">= 1.16, < 1.28" ) type networkManagerDbusConfigurator struct { dbusLinkObject dbus.ObjectPath + routingAll bool } // the types below are based on dbus specification, each field is mapped to a dbus type @@ -85,7 +87,7 @@ func newNetworkManagerDbusConfigurator(wgInterface *iface.WGIface) hostManager { } } -func (n *networkManagerDbusConfigurator) applyDNSSettings(domains []string, ip string, port int) error { +func (n *networkManagerDbusConfigurator) applyDNSConfig(config hostDNSConfig) error { connSettings, configVersion, err := n.getAppliedConnectionSettings() if err != nil { return fmt.Errorf("got an error while retrieving the applied connection settings, error: %s", err) @@ -93,143 +95,52 @@ func (n *networkManagerDbusConfigurator) applyDNSSettings(domains []string, ip s connSettings.cleanDeprecatedSettings() - dnsIP := netip.MustParseAddr(ip) + dnsIP := netip.MustParseAddr(config.serverIP) convDNSIP := binary.LittleEndian.Uint32(dnsIP.AsSlice()) connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSKey] = dbus.MakeVariant([]uint32{convDNSIP}) - - priority := networkManagerDbusSearchDomainOnlyPriority - if len(domains) > 1 { - priority = networkManagerDbusWithMatchDomainPriority - } - - var newDomainList []string - - for _, domain := range domains { - if isRootZoneDomain(domain) { - priority = networkManagerDbusPrimaryDNSPriority - newDomainList = append(newDomainList, "~.") + var ( + searchDomains []string + matchDomains []string + ) + for _, dConf := range config.domains { + if dConf.matchOnly { + matchDomains = append(matchDomains, "~."+dns.Fqdn(dConf.domain)) continue } - matchDomain := "~." + dns.Fqdn(domain) - newDomainList = append(newDomainList, matchDomain) - } - - if priority == networkManagerDbusPrimaryDNSPriority { - newDomainList = append(newDomainList, "~.") - } - - connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSPriorityKey] = dbus.MakeVariant(priority) - connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] = dbus.MakeVariant(newDomainList) - - err = n.reApplyConnectionSettings(connSettings, configVersion) - if err != nil { - log.Errorf("got an error while reapplying the connection with new settings, error: %s", err) - } - return nil -} - -func (n *networkManagerDbusConfigurator) addSearchDomain(domain string, _ string, _ int) error { - connSettings, configVersion, err := n.getAppliedConnectionSettings() - if err != nil { - return fmt.Errorf("got an error while retrieving the applied connection settings, error: %s", err) - } - - connSettings.cleanDeprecatedSettings() - - currentDomainsVariant := connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] - currentDomainsInt := currentDomainsVariant.Value() - currentDomains := currentDomainsInt.([]string) - - fqdnDomain := dns.Fqdn(domain) - matchOnlyDomain := "~." + fqdnDomain - var newDomainList []string - for _, currDomain := range currentDomains { - if currDomain != fqdnDomain && currDomain != matchOnlyDomain { - newDomainList = append(newDomainList, currDomain) - } + searchDomains = append(searchDomains, dns.Fqdn(dConf.domain)) } - newDomainList = append([]string{fqdnDomain}, newDomainList...) - - connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] = dbus.MakeVariant(newDomainList) - - err = n.reApplyConnectionSettings(connSettings, configVersion) - if err != nil { - log.Errorf("got an error while reapplying the connection with new search domain settings, error: %s", err) - } - - return nil -} -func (n *networkManagerDbusConfigurator) removeDomainSettings(domains []string) error { - connSettings, configVersion, err := n.getAppliedConnectionSettings() - if err != nil { - return fmt.Errorf("got an error while retrieving the applied connection settings, error: %s", err) - } - - connSettings.cleanDeprecatedSettings() - - currentDomainsVariant := connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] - currentDomainsInt := currentDomainsVariant.Value() - currentDomains := currentDomainsInt.([]string) - - currentMap := make(map[string]struct{}) - for _, currentDomain := range currentDomains { - currentMap[currentDomain] = struct{}{} - } - - for _, domain := range domains { - fqdnDomain := dns.Fqdn(domain) - matchOnlyDomain := "~." + fqdnDomain - _, found := currentMap[fqdnDomain] - if found { - delete(currentMap, fqdnDomain) - continue - } - _, found = currentMap[matchOnlyDomain] - if found { - delete(currentMap, matchOnlyDomain) - } - } + newDomainList := append(searchDomains, matchDomains...) priority := networkManagerDbusSearchDomainOnlyPriority - if len(currentMap) > 1 { + switch { + case config.routeAll: + priority = networkManagerDbusPrimaryDNSPriority + newDomainList = append(newDomainList, "~.") + if !n.routingAll { + log.Infof("configured %s:%d as main DNS forwarder for this peer", config.serverIP, config.serverPort) + } + case len(matchDomains) > 0: priority = networkManagerDbusWithMatchDomainPriority } - var newDomainList []string - for domainLeft := range currentMap { - if domainLeft == "~." { - priority = networkManagerDbusPrimaryDNSPriority - } - newDomainList = append(newDomainList, domainLeft) + if priority != networkManagerDbusPrimaryDNSPriority && n.routingAll { + log.Infof("removing %s:%d as main DNS forwarder for this peer", config.serverIP, config.serverPort) + n.routingAll = false } connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSPriorityKey] = dbus.MakeVariant(priority) connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] = dbus.MakeVariant(newDomainList) + log.Infof("adding %d search domains and %d match domains. Search list: %s , Match list: %s", len(searchDomains), len(matchDomains), searchDomains, matchDomains) err = n.reApplyConnectionSettings(connSettings, configVersion) if err != nil { - log.Errorf("got an error while reapplying settings after removing domains, error: %s", err) + return fmt.Errorf("got an error while reapplying the connection with new settings, error: %s", err) } return nil } -func (n *networkManagerDbusConfigurator) removeDNSSettings() error { - connSettings, configVersion, err := n.getAppliedConnectionSettings() - if err != nil { - return fmt.Errorf("got an error while retrieving the applied connection settings, error: %s", err) - } - - connSettings.cleanDeprecatedSettings() - - connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSKey] = dbus.MakeVariant([]uint32{}) - connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSPriorityKey] = dbus.MakeVariant(networkManagerDbusSearchDefaultPriority) - connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] = dbus.MakeVariant([]string{}) - - err = n.reApplyConnectionSettings(connSettings, configVersion) - if err != nil { - log.Errorf("got an error while reapplying removed settings, error: %s", err) - } +func (n *networkManagerDbusConfigurator) restoreHostDNS() error { return nil } @@ -276,8 +187,28 @@ func (n *networkManagerDbusConfigurator) reApplyConnectionSettings(connSettings return nil } +//func isNetworkManagerSupportedMode() bool { +// obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusDnsManagerObjectNode) +// if err != nil { +// log.Errorf("got error while attempting to get the network manager object, err: %s", err) +// return false +// } +// +// defer closeConn() +// +// value, err := obj.GetProperty(networkManagerDbusDnsManagerModeProperty) +// if err != nil { +// log.Errorf("unable to retrieve network manager mode, got error: %s", err) +// return false +// } +// valueString := value.Value().(string) +// switch valueString { +// case +// } +//} + func isNetworkManagerSupportedVersion() bool { - obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusDnsManagerObjectNode) + obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusObjectNode) if err != nil { log.Errorf("got error while attempting to get the network manager object, err: %s", err) return false @@ -285,38 +216,38 @@ func isNetworkManagerSupportedVersion() bool { defer closeConn() - value, err := obj.GetProperty(networkManagerDbusDnsManagerModeProperty) + value, err := obj.GetProperty(networkManagerDbusVersionProperty) if err != nil { log.Errorf("unable to retrieve network manager mode, got error: %s", err) return false } - stringValue := value.Value().(string) + versionValue, err := parseVersion(value.Value().(string)) + if err != nil { + return false + } + + constraints, err := version.NewConstraint(supportedNetworkManagerVersionConstraint) + if err != nil { + return false + } + + return constraints.Check(versionValue) } -func compareVersions(inputList []string) (string, string) { +func parseVersion(inputVersion string) (*version.Version, error) { reg, err := regexp.Compile(version.SemverRegexpRaw) if err != nil { - return "", "" + return nil, err } - versions := make([]*version.Version, 0) - - for _, raw := range inputList { - if raw != "" && reg.MatchString(raw) { - v, err := version.NewVersion(raw) - if err == nil { - versions = append(versions, v) - } - } + if inputVersion == "" || !reg.MatchString(inputVersion) { + return nil, fmt.Errorf("couldn't parse the provided version: Not SemVer") } - switch len(versions) { - case 0: - return "", "" - case 1: - v := versions[0].String() - return v, v - default: - sort.Sort(version.Collection(versions)) - return versions[0].String(), versions[len(versions)-1].String() + + verObj, err := version.NewVersion(inputVersion) + if err != nil { + return nil, err } + + return verObj, nil } From b33ff4bfb4f48d6212cdbff7cdca8ee79564c5f0 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Mon, 21 Nov 2022 13:08:30 +0100 Subject: [PATCH 35/48] add mode check for network manager check if file is managed by netbird --- client/internal/dns/host_linux.go | 13 ++-- client/internal/dns/network_manager_linux.go | 68 +++++++++++++------- 2 files changed, 53 insertions(+), 28 deletions(-) diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index 04add0b8c7a..0b779702a57 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -13,7 +13,8 @@ const ( ) const ( - fileManager osManagerType = iota + netbirdManager osManagerType = iota + fileManager networkManager systemdManager resolvConfManager @@ -30,7 +31,7 @@ func newHostManager(wgInterface *iface.WGIface) hostManager { case systemdManager: return newSystemdDbusConfigurator(wgInterface) default: - return newNoopHostMocker() + return newNetworkManagerDbusConfigurator(wgInterface) //return newSystemdDbusConfigurator(wgInterface) } } @@ -49,14 +50,14 @@ func getOSDNSManagerType() osManagerType { if text[0] != '#' { return fileManager } - if strings.Contains(text, "NetworkManager") && isDbusListenerRunning(networkManagerDest, networkManagerDbusObjectNode) { + if strings.Contains(text, fileGeneratedResolvConfContentHeader) { + return netbirdManager + } + if strings.Contains(text, "NetworkManager") && isDbusListenerRunning(networkManagerDest, networkManagerDbusObjectNode) && isNetworkManagerSupported() { log.Debugf("is nm running on supported v? %t", isNetworkManagerSupportedVersion()) return networkManager } if strings.Contains(text, "systemd-resolved") && isDbusListenerRunning(systemdResolvedDest, systemdDbusObjectNode) { - //if isDbusListenerRunning(networkManagerDest, networkManagerDbusObjectNode) { - // return networkManager - //} return systemdManager } if strings.Contains(text, "resolvconf") { diff --git a/client/internal/dns/network_manager_linux.go b/client/internal/dns/network_manager_linux.go index cc992ad2b9e..9ab5c048b5a 100644 --- a/client/internal/dns/network_manager_linux.go +++ b/client/internal/dns/network_manager_linux.go @@ -17,8 +17,9 @@ import ( const ( networkManagerDest = "org.freedesktop.NetworkManager" networkManagerDbusObjectNode = "/org/freedesktop/NetworkManager" - networkManagerDbusDnsManagerObjectNode = "/org/freedesktop/NetworkManager/DnsManager" - networkManagerDbusDnsManagerModeProperty = "org.freedesktop.NetworkManager.DnsManager.Mode" + networkManagerDbusDnsManagerInterface = "org.freedesktop.NetworkManager.DnsManager" + networkManagerDbusDnsManagerModeProperty = networkManagerDbusDnsManagerInterface + ".Mode" + networkManagerDbusDnsManagerRcManagerProperty = networkManagerDbusDnsManagerInterface + ".RcManager" networkManagerDbusVersionProperty = "org.freedesktop.NetworkManager.Version" networkManagerDbusGetDeviceByIpIfaceMethod = networkManagerDest + ".GetDeviceByIpIface" networkManagerDbusDeviceInterface = "org.freedesktop.NetworkManager.Device" @@ -35,7 +36,6 @@ const ( networkManagerDbusPrimaryDNSPriority int32 = -500 networkManagerDbusWithMatchDomainPriority int32 = 0 networkManagerDbusSearchDomainOnlyPriority int32 = 50 - networkManagerDbusSearchDefaultPriority int32 = 100 supportedNetworkManagerVersionConstraint = ">= 1.16, < 1.28" ) @@ -141,6 +141,7 @@ func (n *networkManagerDbusConfigurator) applyDNSConfig(config hostDNSConfig) er } func (n *networkManagerDbusConfigurator) restoreHostDNS() error { + // once the interface is gone network manager cleans all config associated with it return nil } @@ -187,25 +188,48 @@ func (n *networkManagerDbusConfigurator) reApplyConnectionSettings(connSettings return nil } -//func isNetworkManagerSupportedMode() bool { -// obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusDnsManagerObjectNode) -// if err != nil { -// log.Errorf("got error while attempting to get the network manager object, err: %s", err) -// return false -// } -// -// defer closeConn() -// -// value, err := obj.GetProperty(networkManagerDbusDnsManagerModeProperty) -// if err != nil { -// log.Errorf("unable to retrieve network manager mode, got error: %s", err) -// return false -// } -// valueString := value.Value().(string) -// switch valueString { -// case -// } -//} +func isNetworkManagerSupported() bool { + return isNetworkManagerSupportedVersion() && isNetworkManagerSupportedMode() +} + +func isNetworkManagerSupportedMode() bool { + var mode string + err := getNetworkManagerDNSProperty(networkManagerDbusDnsManagerModeProperty, &mode) + if err != nil { + log.Error(err) + return false + } + switch mode { + case "dnsmasq", "unbound", "systemd-resolved": + return true + default: + var rcManager string + err = getNetworkManagerDNSProperty(networkManagerDbusDnsManagerRcManagerProperty, &rcManager) + if err != nil { + log.Error(err) + return false + } + if rcManager == "unmanaged" { + return false + } + } + return true +} + +func getNetworkManagerDNSProperty(property string, store any) error { + obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusObjectNode) + if err != nil { + return fmt.Errorf("got error while attempting to retrieve the network manager dns manager object, error: %s", err) + } + defer closeConn() + + v, e := obj.GetProperty(property) + if e != nil { + return fmt.Errorf("got an error getting property %s: %v", property, e) + } + + return v.Store(store) +} func isNetworkManagerSupportedVersion() bool { obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusObjectNode) From a990de01df79027c01276145799dad04a63df08e Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Mon, 21 Nov 2022 14:26:32 +0100 Subject: [PATCH 36/48] added file configurator --- client/internal/dns/file_linux.go | 132 ++++++++++++++++++++++++++++++ client/internal/dns/host_linux.go | 3 +- 2 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 client/internal/dns/file_linux.go diff --git a/client/internal/dns/file_linux.go b/client/internal/dns/file_linux.go new file mode 100644 index 00000000000..02dfb64d1b3 --- /dev/null +++ b/client/internal/dns/file_linux.go @@ -0,0 +1,132 @@ +package dns + +import ( + "bytes" + "fmt" + log "github.com/sirupsen/logrus" + "os" + "os/exec" +) + +const ( + fileGeneratedResolvConfContentHeader = "# Generated by NetBird" + fileGeneratedResolvConfSearchBeginContent = "search " + fileGeneratedResolvConfContentFormat = fileGeneratedResolvConfContentHeader + + "\n# If needed you can restore the original file by copying back %s\n\nnameserver %s\n" + + fileGeneratedResolvConfSearchBeginContent + "%s" +) +const ( + fileDefaultResolvConfBackupLocation = defaultResolvConfPath + ".original.netbird" + fileMaxLineCharsLimit = 256 + fileMaxNumberOfSearchDomains = 6 +) + +var fileSearchLineBeginCharCount = len(fileGeneratedResolvConfSearchBeginContent) + +type fileConfigurator struct { + originalPerms os.FileMode +} + +func newFileConfigurator() hostManager { + return &fileConfigurator{} +} + +func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error { + if !config.routeAll { + return fmt.Errorf("unable to configure DNS for this peer using file manager without a Primary nameserver group") + } + backupFileExist := false + _, err := os.Stat(fileDefaultResolvConfBackupLocation) + if err == nil { + backupFileExist = true + } + + switch getOSDNSManagerType() { + case fileManager, netbirdManager: + if !backupFileExist { + err = f.backup() + if err != nil { + return fmt.Errorf("unable to backup the resolv.conf file") + } + backupFileExist = true + } + default: + // todo improve this and maybe restart DNS manager from scratch + return fmt.Errorf("something happened and file manager is not your prefered host dns configurator, restart the agent") + } + + var searchDomains string + appendedDomains := 0 + for _, dConf := range config.domains { + if dConf.matchOnly { + continue + } + if appendedDomains >= fileMaxNumberOfSearchDomains { + // lets log all skipped domains + log.Infof("already appended %d domains to search list. Skipping append of %s domain", fileMaxNumberOfSearchDomains, dConf.domain) + continue + } + if fileSearchLineBeginCharCount+len(searchDomains) > fileMaxLineCharsLimit { + // lets log all skipped domains + log.Infof("search list line is larger than %d characters. Skipping append of %s domain", fileMaxLineCharsLimit, dConf.domain) + continue + } + + searchDomains += " " + dConf.domain + appendedDomains++ + } + content := fmt.Sprintf(fileGeneratedResolvConfContentFormat, fileDefaultResolvConfBackupLocation, config.serverIP, searchDomains) + err = writeDNSConfig(content, defaultResolvConfPath, f.originalPerms) + if err != nil { + f.restore() + return err + } + log.Infof("created a NetBird managed %s file with your DNS settings", defaultResolvConfPath) + return nil +} + +func (f *fileConfigurator) restoreHostDNS() error { + return f.restore() +} + +func (f *fileConfigurator) backup() error { + stats, err := os.Stat(defaultResolvConfPath) + if err != nil { + return fmt.Errorf("got an error while checking stats for %s file. Error: %s", defaultResolvConfPath, err) + } + + f.originalPerms = stats.Mode() + + err = copyFile(defaultResolvConfPath, fileDefaultResolvConfBackupLocation) + if err != nil { + return fmt.Errorf("got error while backing up the %s file. Error: %s", defaultResolvConfPath, err) + } + return nil +} + +func (f *fileConfigurator) restore() error { + err := copyFile(fileDefaultResolvConfBackupLocation, defaultResolvConfPath) + if err != nil { + return fmt.Errorf("got error while restoring the %s file from %s. Error: %s", defaultResolvConfPath, fileDefaultResolvConfBackupLocation, err) + } + return nil +} + +func writeDNSConfig(content, fileName string, permissions os.FileMode) error { + log.Debugf("creating managed file %s", fileName) + var buf bytes.Buffer + buf.WriteString(content) + err := os.WriteFile(fileName, buf.Bytes(), permissions) + if err != nil { + return fmt.Errorf("got an creating resolver file %s err: %s", fileName, err) + } + return nil +} + +func copyFile(src, dest string) error { + _, err := exec.Command("cp", src, dest).CombinedOutput() + if err != nil { + return err + } + return nil +} diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index 0b779702a57..20f20ae6677 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -31,8 +31,7 @@ func newHostManager(wgInterface *iface.WGIface) hostManager { case systemdManager: return newSystemdDbusConfigurator(wgInterface) default: - return newNetworkManagerDbusConfigurator(wgInterface) - //return newSystemdDbusConfigurator(wgInterface) + return newFileConfigurator() } } From 85d0e8cb441eb59fe57422567e53e1dace89e2cc Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Mon, 21 Nov 2022 18:47:42 +0100 Subject: [PATCH 37/48] return success when not resolved added rdata length to the record headers --- client/internal/dns/local.go | 7 +++++-- dns/dns.go | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/client/internal/dns/local.go b/client/internal/dns/local.go index fe32b6a97a9..001bd28a43b 100644 --- a/client/internal/dns/local.go +++ b/client/internal/dns/local.go @@ -18,10 +18,11 @@ func (d *localResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { log.Debugf("received question: %#v\n", r.Question[0]) replyMessage := &dns.Msg{} replyMessage.SetReply(r) - replyMessage.Rcode = dns.RcodeNameError + replyMessage.RecursionAvailable = true + replyMessage.Rcode = dns.RcodeSuccess + response := d.lookupRecord(r) if response != nil { - replyMessage.Rcode = dns.RcodeSuccess replyMessage.Answer = append(replyMessage.Answer, response) } @@ -47,6 +48,8 @@ func (d *localResolver) registerRecord(record nbdns.SimpleRecord) error { return err } + fullRecord.Header().Rdlength = record.Len() + header := fullRecord.Header() d.records.Store(buildRecordKey(header.Name, header.Class, header.Rrtype), fullRecord) diff --git a/dns/dns.go b/dns/dns.go index a09e4b5df60..16ebd1d96d2 100644 --- a/dns/dns.go +++ b/dns/dns.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/miekg/dns" "golang.org/x/net/idna" + "net" "regexp" "strings" ) @@ -60,6 +61,30 @@ func (s SimpleRecord) String() string { return fmt.Sprintf("%s %d %s %s %s", fqdn, s.TTL, s.Class, dns.Type(s.Type).String(), s.RData) } +// Len returns the length of the RData field, based on its type +func (s SimpleRecord) Len() uint16 { + emptyString := s.RData == "" + switch s.Type { + case 1: + if emptyString { + return 0 + } + return net.IPv4len + case 5: + if emptyString || s.RData == "." { + return 1 + } + return uint16(len(s.RData) + 1) + case 28: + if emptyString { + return 0 + } + return net.IPv6len + default: + return 0 + } +} + // GetParsedDomainLabel returns a domain label with max 59 characters, // parsed for old Hosts.txt requirements, and converted to ASCII and lowercase func GetParsedDomainLabel(name string) (string, error) { From 59ca46a96842410a6bce67bcac11736aac14d86b Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Mon, 21 Nov 2022 18:48:54 +0100 Subject: [PATCH 38/48] skip using command to copy file restore backup if mode is not supported --- client/internal/dns/file_linux.go | 35 +++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/client/internal/dns/file_linux.go b/client/internal/dns/file_linux.go index 02dfb64d1b3..096ec5fbc96 100644 --- a/client/internal/dns/file_linux.go +++ b/client/internal/dns/file_linux.go @@ -5,7 +5,6 @@ import ( "fmt" log "github.com/sirupsen/logrus" "os" - "os/exec" ) const ( @@ -13,7 +12,7 @@ const ( fileGeneratedResolvConfSearchBeginContent = "search " fileGeneratedResolvConfContentFormat = fileGeneratedResolvConfContentHeader + "\n# If needed you can restore the original file by copying back %s\n\nnameserver %s\n" + - fileGeneratedResolvConfSearchBeginContent + "%s" + fileGeneratedResolvConfSearchBeginContent + "%s\n" ) const ( fileDefaultResolvConfBackupLocation = defaultResolvConfPath + ".original.netbird" @@ -32,15 +31,22 @@ func newFileConfigurator() hostManager { } func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error { - if !config.routeAll { - return fmt.Errorf("unable to configure DNS for this peer using file manager without a Primary nameserver group") - } backupFileExist := false _, err := os.Stat(fileDefaultResolvConfBackupLocation) if err == nil { backupFileExist = true } + if !config.routeAll { + if backupFileExist { + err = f.restore() + if err != nil { + return fmt.Errorf("unable to configure DNS for this peer using file manager without a Primary nameserver group. Restoring the original file return err: %s", err) + } + } + return fmt.Errorf("unable to configure DNS for this peer using file manager without a Primary nameserver group") + } + switch getOSDNSManagerType() { case fileManager, netbirdManager: if !backupFileExist { @@ -109,7 +115,8 @@ func (f *fileConfigurator) restore() error { if err != nil { return fmt.Errorf("got error while restoring the %s file from %s. Error: %s", defaultResolvConfPath, fileDefaultResolvConfBackupLocation, err) } - return nil + + return os.RemoveAll(fileDefaultResolvConfBackupLocation) } func writeDNSConfig(content, fileName string, permissions os.FileMode) error { @@ -118,15 +125,25 @@ func writeDNSConfig(content, fileName string, permissions os.FileMode) error { buf.WriteString(content) err := os.WriteFile(fileName, buf.Bytes(), permissions) if err != nil { - return fmt.Errorf("got an creating resolver file %s err: %s", fileName, err) + return fmt.Errorf("got an creating resolver file %s. Error: %s", fileName, err) } return nil } func copyFile(src, dest string) error { - _, err := exec.Command("cp", src, dest).CombinedOutput() + stats, err := os.Stat(src) if err != nil { - return err + return fmt.Errorf("got an error while checking stats for %s file when copying it. Error: %s", src, err) + } + + bytesRead, err := os.ReadFile(src) + if err != nil { + return fmt.Errorf("got an error while reading the file %s file for copy. Error: %s", src, err) + } + + err = os.WriteFile(dest, bytesRead, stats.Mode()) + if err != nil { + return fmt.Errorf("got an writing the destination file %s for copy. Error: %s", dest, err) } return nil } From 1f27804418dc4ce74c021e19a30c9f1c181ae171 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Mon, 21 Nov 2022 19:05:27 +0100 Subject: [PATCH 39/48] fix local tests --- client/internal/dns/local.go | 5 ++--- client/internal/dns/server_test.go | 6 +++--- client/internal/engine_test.go | 4 +++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/client/internal/dns/local.go b/client/internal/dns/local.go index 001bd28a43b..680fcc31a32 100644 --- a/client/internal/dns/local.go +++ b/client/internal/dns/local.go @@ -60,8 +60,7 @@ func (d *localResolver) deleteRecord(recordKey string) { d.records.Delete(dns.Fqdn(recordKey)) } -func buildRecordKey(name string, class, qtype uint16) string { - key := fmt.Sprintf("%s_%d_%d", name, class, qtype) - log.Debugf("generated the following key: %s", key) +func buildRecordKey(name string, class, qType uint16) string { + key := fmt.Sprintf("%s_%d_%d", name, class, qType) return key } diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index 48a901fde45..eb52059e24f 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -75,12 +75,12 @@ func TestUpdateDNSServer(t *testing.T) { }, }, expectedUpstreamMap: registrationMap{"netbird.io": struct{}{}, "netbird.cloud": struct{}{}, nbdns.RootZone: struct{}{}}, - expectedLocalMap: registrationMap{zoneRecords[0].Name: struct{}{}}, + expectedLocalMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}}, }, { name: "New Config Should Succeed", initLocalMap: registrationMap{"netbird.cloud": struct{}{}}, - initUpstreamMap: registrationMap{zoneRecords[0].Name: struct{}{}}, + initUpstreamMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}}, initSerial: 0, inputSerial: 1, inputUpdate: nbdns.Config{ @@ -99,7 +99,7 @@ func TestUpdateDNSServer(t *testing.T) { }, }, expectedUpstreamMap: registrationMap{"netbird.io": struct{}{}, "netbird.cloud": struct{}{}}, - expectedLocalMap: registrationMap{zoneRecords[0].Name: struct{}{}}, + expectedLocalMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}}, }, { name: "Smaller Config Serial Should Be Skipped", diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 9b291318739..59009c550ee 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -202,7 +202,9 @@ func TestEngine_UpdateNetworkMap(t *testing.T) { }, nbstatus.NewRecorder()) engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", iface.DefaultMTU) engine.routeManager = routemanager.NewManager(ctx, key.PublicKey().String(), engine.wgInterface, engine.statusRecorder) - engine.dnsServer = dns.NewDefaultServer(ctx, engine.wgInterface) + engine.dnsServer = &dns.MockServer{ + UpdateDNSServerFunc: func(serial uint64, update nbdns.Config) error { return nil }, + } type testCase struct { name string From 2e60459da15eb3f728c7cccdd9c76c8a6179b94a Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Mon, 21 Nov 2022 21:32:53 +0100 Subject: [PATCH 40/48] fix docker and lint tests --- client/internal/dns/file_linux.go | 6 ++++-- client/internal/dns/host.go | 4 ---- client/internal/dns/host_linux.go | 4 ++++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/client/internal/dns/file_linux.go b/client/internal/dns/file_linux.go index 096ec5fbc96..48048a30d30 100644 --- a/client/internal/dns/file_linux.go +++ b/client/internal/dns/file_linux.go @@ -54,7 +54,6 @@ func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error { if err != nil { return fmt.Errorf("unable to backup the resolv.conf file") } - backupFileExist = true } default: // todo improve this and maybe restart DNS manager from scratch @@ -84,7 +83,10 @@ func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error { content := fmt.Sprintf(fileGeneratedResolvConfContentFormat, fileDefaultResolvConfBackupLocation, config.serverIP, searchDomains) err = writeDNSConfig(content, defaultResolvConfPath, f.originalPerms) if err != nil { - f.restore() + err = f.restore() + if err != nil { + log.Errorf("attempt to restore default file failed with error: %s", err) + } return err } log.Infof("created a NetBird managed %s file with your DNS settings", defaultResolvConfPath) diff --git a/client/internal/dns/host.go b/client/internal/dns/host.go index 9c6a2135cf1..c077e203241 100644 --- a/client/internal/dns/host.go +++ b/client/internal/dns/host.go @@ -49,10 +49,6 @@ func newNoopHostMocker() hostManager { } } -func isRootZoneDomain(domain string) bool { - return domain == nbdns.RootZone || domain == "" -} - func dnsConfigToHostDNSConfig(dnsConfig nbdns.Config, ip string, port int) hostDNSConfig { config := hostDNSConfig{ routeAll: false, diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index 20f20ae6677..1dd4b72ae0d 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -36,6 +36,7 @@ func newHostManager(wgInterface *iface.WGIface) hostManager { } func getOSDNSManagerType() osManagerType { + file, err := os.Open(defaultResolvConfPath) if err != nil { // todo add proper error handling @@ -46,6 +47,9 @@ func getOSDNSManagerType() osManagerType { scanner := bufio.NewScanner(file) for scanner.Scan() { text := scanner.Text() + if len(text) == 0 { + continue + } if text[0] != '#' { return fileManager } From fe85b591546fc7f8a7a76da0e00a272863af58d0 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Mon, 21 Nov 2022 21:34:50 +0100 Subject: [PATCH 41/48] fix codacy checks --- client/internal/dns/host_darwin.go | 6 +++--- client/internal/dns/network_manager_linux.go | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/client/internal/dns/host_darwin.go b/client/internal/dns/host_darwin.go index f583d4027fd..59ba08e5085 100644 --- a/client/internal/dns/host_darwin.go +++ b/client/internal/dns/host_darwin.go @@ -18,7 +18,7 @@ const ( keySupplementalMatchDomains = "SupplementalMatchDomains" keySupplementalMatchDomainsNoSearch = "SupplementalMatchDomainsNoSearch" keyServerAddresses = "ServerAddresses" - ServerPort = "ServerPort" + keyServerPort = "ServerPort" arraySymbol = "* " digitSymbol = "# " scutilPath = "/usr/sbin/scutil" @@ -162,7 +162,7 @@ func (s *systemConfigurator) addDNSState(state, domains, dnsServer string, port lines := buildAddCommandLine(keySupplementalMatchDomains, arraySymbol+domains) lines += buildAddCommandLine(keySupplementalMatchDomainsNoSearch, digitSymbol+noSearch) lines += buildAddCommandLine(keyServerAddresses, arraySymbol+dnsServer) - lines += buildAddCommandLine(ServerPort, digitSymbol+strconv.Itoa(port)) + lines += buildAddCommandLine(keyServerPort, digitSymbol+strconv.Itoa(port)) addDomainCommand := buildCreateStateWithOperation(state, lines) stdinCommands := wrapCommand(addDomainCommand) @@ -210,7 +210,7 @@ func (s *systemConfigurator) getPrimaryService() string { func (s *systemConfigurator) addDNSSetup(setupKey, dnsServer string, port int) error { lines := buildAddCommandLine(keySupplementalMatchDomainsNoSearch, digitSymbol+strconv.Itoa(0)) lines += buildAddCommandLine(keyServerAddresses, arraySymbol+dnsServer) - lines += buildAddCommandLine(ServerPort, digitSymbol+strconv.Itoa(port)) + lines += buildAddCommandLine(keyServerPort, digitSymbol+strconv.Itoa(port)) addDomainCommand := buildCreateStateWithOperation(setupKey, lines) stdinCommands := wrapCommand(addDomainCommand) _, err := runSystemConfigCommand(stdinCommands) diff --git a/client/internal/dns/network_manager_linux.go b/client/internal/dns/network_manager_linux.go index 9ab5c048b5a..230bc92449b 100644 --- a/client/internal/dns/network_manager_linux.go +++ b/client/internal/dns/network_manager_linux.go @@ -17,11 +17,11 @@ import ( const ( networkManagerDest = "org.freedesktop.NetworkManager" networkManagerDbusObjectNode = "/org/freedesktop/NetworkManager" - networkManagerDbusDnsManagerInterface = "org.freedesktop.NetworkManager.DnsManager" - networkManagerDbusDnsManagerModeProperty = networkManagerDbusDnsManagerInterface + ".Mode" - networkManagerDbusDnsManagerRcManagerProperty = networkManagerDbusDnsManagerInterface + ".RcManager" + networkManagerDbusDNSManagerInterface = "org.freedesktop.NetworkManager.DnsManager" + networkManagerDbusDNSManagerModeProperty = networkManagerDbusDNSManagerInterface + ".Mode" + networkManagerDbusDNSManagerRcManagerProperty = networkManagerDbusDNSManagerInterface + ".RcManager" networkManagerDbusVersionProperty = "org.freedesktop.NetworkManager.Version" - networkManagerDbusGetDeviceByIpIfaceMethod = networkManagerDest + ".GetDeviceByIpIface" + networkManagerDbusGetDeviceByIPIfaceMethod = networkManagerDest + ".GetDeviceByIpIface" networkManagerDbusDeviceInterface = "org.freedesktop.NetworkManager.Device" networkManagerDbusDeviceGetAppliedConnectionMethod = networkManagerDbusDeviceInterface + ".GetAppliedConnection" networkManagerDbusDeviceReapplyMethod = networkManagerDbusDeviceInterface + ".Reapply" @@ -74,7 +74,7 @@ func newNetworkManagerDbusConfigurator(wgInterface *iface.WGIface) hostManager { } defer closeConn() var s string - err = obj.Call(networkManagerDbusGetDeviceByIpIfaceMethod, dbusDefaultFlag, wgInterface.GetName()).Store(&s) + err = obj.Call(networkManagerDbusGetDeviceByIPIfaceMethod, dbusDefaultFlag, wgInterface.GetName()).Store(&s) if err != nil { // todo add proper error handling panic(err) @@ -194,7 +194,7 @@ func isNetworkManagerSupported() bool { func isNetworkManagerSupportedMode() bool { var mode string - err := getNetworkManagerDNSProperty(networkManagerDbusDnsManagerModeProperty, &mode) + err := getNetworkManagerDNSProperty(networkManagerDbusDNSManagerModeProperty, &mode) if err != nil { log.Error(err) return false @@ -204,7 +204,7 @@ func isNetworkManagerSupportedMode() bool { return true default: var rcManager string - err = getNetworkManagerDNSProperty(networkManagerDbusDnsManagerRcManagerProperty, &rcManager) + err = getNetworkManagerDNSProperty(networkManagerDbusDNSManagerRcManagerProperty, &rcManager) if err != nil { log.Error(err) return false From 74948e49b30683739a9652280a8c1b1fae50dbc9 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 22 Nov 2022 03:27:38 +0100 Subject: [PATCH 42/48] use correct object path and remove device on exit --- client/internal/dns/network_manager_linux.go | 28 +++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/client/internal/dns/network_manager_linux.go b/client/internal/dns/network_manager_linux.go index 230bc92449b..7a1b790e158 100644 --- a/client/internal/dns/network_manager_linux.go +++ b/client/internal/dns/network_manager_linux.go @@ -18,6 +18,7 @@ const ( networkManagerDest = "org.freedesktop.NetworkManager" networkManagerDbusObjectNode = "/org/freedesktop/NetworkManager" networkManagerDbusDNSManagerInterface = "org.freedesktop.NetworkManager.DnsManager" + networkManagerDbusDNSManagerObjectNode = networkManagerDbusObjectNode + "/DnsManager" networkManagerDbusDNSManagerModeProperty = networkManagerDbusDNSManagerInterface + ".Mode" networkManagerDbusDNSManagerRcManagerProperty = networkManagerDbusDNSManagerInterface + ".RcManager" networkManagerDbusVersionProperty = "org.freedesktop.NetworkManager.Version" @@ -25,6 +26,7 @@ const ( networkManagerDbusDeviceInterface = "org.freedesktop.NetworkManager.Device" networkManagerDbusDeviceGetAppliedConnectionMethod = networkManagerDbusDeviceInterface + ".GetAppliedConnection" networkManagerDbusDeviceReapplyMethod = networkManagerDbusDeviceInterface + ".Reapply" + networkManagerDbusDeviceDeleteMethod = networkManagerDbusDeviceInterface + ".Delete" networkManagerDbusDefaultBehaviorFlag networkManagerConfigBehavior = 0 networkManagerDbusIPv4Key = "ipv4" networkManagerDbusIPv6Key = "ipv6" @@ -142,7 +144,7 @@ func (n *networkManagerDbusConfigurator) applyDNSConfig(config hostDNSConfig) er func (n *networkManagerDbusConfigurator) restoreHostDNS() error { // once the interface is gone network manager cleans all config associated with it - return nil + return n.deleteConnectionSettings() } func (n *networkManagerDbusConfigurator) getAppliedConnectionSettings() (networkManagerConnSettings, networkManagerConfigVersion, error) { @@ -163,7 +165,7 @@ func (n *networkManagerDbusConfigurator) getAppliedConnectionSettings() (network err = obj.CallWithContext(ctx, networkManagerDbusDeviceGetAppliedConnectionMethod, dbusDefaultFlag, networkManagerDbusDefaultBehaviorFlag).Store(&connSettings, &configVersion) if err != nil { - return nil, 0, fmt.Errorf("got error while calling command with context, err: %s", err) + return nil, 0, fmt.Errorf("got error while calling GetAppliedConnection method with context, err: %s", err) } return connSettings, configVersion, nil @@ -182,7 +184,25 @@ func (n *networkManagerDbusConfigurator) reApplyConnectionSettings(connSettings err = obj.CallWithContext(ctx, networkManagerDbusDeviceReapplyMethod, dbusDefaultFlag, connSettings, configVersion, networkManagerDbusDefaultBehaviorFlag).Store() if err != nil { - return fmt.Errorf("got error while calling command with context, err: %s", err) + return fmt.Errorf("got error while calling ReApply method with context, err: %s", err) + } + + return nil +} + +func (n *networkManagerDbusConfigurator) deleteConnectionSettings() error { + obj, closeConn, err := getDbusObject(networkManagerDest, n.dbusLinkObject) + if err != nil { + return fmt.Errorf("got error while attempting to retrieve the applied connection settings, err: %s", err) + } + defer closeConn() + + ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) + defer cancel() + + err = obj.CallWithContext(ctx, networkManagerDbusDeviceDeleteMethod, dbusDefaultFlag).Store() + if err != nil { + return fmt.Errorf("got error while calling delete method with context, err: %s", err) } return nil @@ -217,7 +237,7 @@ func isNetworkManagerSupportedMode() bool { } func getNetworkManagerDNSProperty(property string, store any) error { - obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusObjectNode) + obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusDNSManagerObjectNode) if err != nil { return fmt.Errorf("got error while attempting to retrieve the network manager dns manager object, error: %s", err) } From 1e7f85dadf731a53c67c740ddcde9bae9dfac0fe Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 22 Nov 2022 03:38:44 +0100 Subject: [PATCH 43/48] check if dns server already exist --- client/internal/engine.go | 4 +++- client/internal/engine_test.go | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index 2e2a11912be..cca592cccef 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -251,7 +251,9 @@ func (e *Engine) Start() error { e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder) - e.dnsServer = dns.NewDefaultServer(e.ctx, e.wgInterface) + if e.dnsServer == nil { + e.dnsServer = dns.NewDefaultServer(e.ctx, e.wgInterface) + } e.receiveSignalEvents() e.receiveManagementEvents() diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 59009c550ee..9e80f144d55 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -801,6 +801,7 @@ func TestEngine_MultiplePeers(t *testing.T) { t.Errorf("unable to create the engine for peer %d with error %v", j, err) return } + engine.dnsServer = &dns.MockServer{} mu.Lock() defer mu.Unlock() err = engine.Start() From e2e68774af05680c6572bea527c2225e2cc1f1be Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 22 Nov 2022 12:29:05 +0100 Subject: [PATCH 44/48] get GUI from WGInterface and handle errors --- client/internal/dns/file_linux.go | 11 ++++++--- client/internal/dns/host_darwin.go | 4 +-- client/internal/dns/host_linux.go | 26 +++++++++++--------- client/internal/dns/host_windows.go | 25 ++++++++----------- client/internal/dns/network_manager_linux.go | 10 +++----- client/internal/dns/server.go | 13 +++++----- client/internal/dns/server_test.go | 10 ++++++-- client/internal/dns/systemd_linux.go | 14 +++++------ client/internal/engine.go | 6 ++++- iface/iface_unix.go | 5 ++++ iface/iface_windows.go | 11 +++++++++ 11 files changed, 79 insertions(+), 56 deletions(-) diff --git a/client/internal/dns/file_linux.go b/client/internal/dns/file_linux.go index 48048a30d30..ad6e3a37f23 100644 --- a/client/internal/dns/file_linux.go +++ b/client/internal/dns/file_linux.go @@ -26,8 +26,8 @@ type fileConfigurator struct { originalPerms os.FileMode } -func newFileConfigurator() hostManager { - return &fileConfigurator{} +func newFileConfigurator() (hostManager, error) { + return &fileConfigurator{}, nil } func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error { @@ -46,8 +46,11 @@ func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error { } return fmt.Errorf("unable to configure DNS for this peer using file manager without a Primary nameserver group") } - - switch getOSDNSManagerType() { + managerType, err := getOSDNSManagerType() + if err != nil { + return err + } + switch managerType { case fileManager, netbirdManager: if !backupFileExist { err = f.backup() diff --git a/client/internal/dns/host_darwin.go b/client/internal/dns/host_darwin.go index 59ba08e5085..546561d8835 100644 --- a/client/internal/dns/host_darwin.go +++ b/client/internal/dns/host_darwin.go @@ -32,10 +32,10 @@ type systemConfigurator struct { createdKeys map[string]struct{} } -func newHostManager(_ *iface.WGIface) hostManager { +func newHostManager(_ *iface.WGIface) (hostManager, error) { return &systemConfigurator{ createdKeys: make(map[string]struct{}), - } + }, nil } func (s *systemConfigurator) applyDNSConfig(config hostDNSConfig) error { diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index 1dd4b72ae0d..8cf015a9f28 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -2,6 +2,7 @@ package dns import ( "bufio" + "fmt" "github.com/netbirdio/netbird/iface" log "github.com/sirupsen/logrus" "os" @@ -22,8 +23,12 @@ const ( type osManagerType int -func newHostManager(wgInterface *iface.WGIface) hostManager { - osManager := getOSDNSManagerType() +func newHostManager(wgInterface *iface.WGIface) (hostManager, error) { + osManager, err := getOSDNSManagerType() + if err != nil { + return nil, err + } + log.Debugf("discovered mode is: %d", osManager) switch osManager { case networkManager: @@ -35,12 +40,11 @@ func newHostManager(wgInterface *iface.WGIface) hostManager { } } -func getOSDNSManagerType() osManagerType { +func getOSDNSManagerType() (osManagerType, error) { file, err := os.Open(defaultResolvConfPath) if err != nil { - // todo add proper error handling - panic(err) + return 0, fmt.Errorf("unable to open %s for checking owner, got error: %s", err) } defer file.Close() @@ -51,21 +55,21 @@ func getOSDNSManagerType() osManagerType { continue } if text[0] != '#' { - return fileManager + return fileManager, nil } if strings.Contains(text, fileGeneratedResolvConfContentHeader) { - return netbirdManager + return netbirdManager, nil } if strings.Contains(text, "NetworkManager") && isDbusListenerRunning(networkManagerDest, networkManagerDbusObjectNode) && isNetworkManagerSupported() { log.Debugf("is nm running on supported v? %t", isNetworkManagerSupportedVersion()) - return networkManager + return networkManager, nil } if strings.Contains(text, "systemd-resolved") && isDbusListenerRunning(systemdResolvedDest, systemdDbusObjectNode) { - return systemdManager + return systemdManager, nil } if strings.Contains(text, "resolvconf") { - return resolvConfManager + return resolvConfManager, nil } } - return fileManager + return fileManager, nil } diff --git a/client/internal/dns/host_windows.go b/client/internal/dns/host_windows.go index 4dbaa0ac0a3..e3f6cf34c64 100644 --- a/client/internal/dns/host_windows.go +++ b/client/internal/dns/host_windows.go @@ -5,8 +5,6 @@ import ( "github.com/netbirdio/netbird/iface" log "github.com/sirupsen/logrus" "golang.org/x/sys/windows/registry" - "golang.zx2c4.com/wireguard/windows/driver" - "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "strings" ) @@ -28,17 +26,19 @@ const ( ) type registryConfigurator struct { - luid winipcfg.LUID + guid string routingAll bool existingSearchDomains []string } -func newHostManager(wgInterface *iface.WGIface) hostManager { - windowsDevice := wgInterface.Interface.(*driver.Adapter) - luid := windowsDevice.LUID() - return ®istryConfigurator{ - luid: luid, +func newHostManager(wgInterface *iface.WGIface) (hostManager, error) { + guid, err := wgInterface.GetInterfaceGUIDString() + if err != nil { + return nil, err } + return ®istryConfigurator{ + guid: guid, + }, nil } func (r *registryConfigurator) applyDNSConfig(config hostDNSConfig) error { @@ -207,14 +207,9 @@ func (r *registryConfigurator) deleteInterfaceRegistryKeyProperty(propertyKey st func (r *registryConfigurator) getInterfaceRegistryKey() (registry.Key, error) { var regKey registry.Key - guid, err := r.luid.GUID() - if err != nil { - return regKey, fmt.Errorf("unable to get interface GUID, error: %s", err) - } - - regKeyPath := interfaceConfigPath + "\\" + guid.String() + regKeyPath := interfaceConfigPath + "\\" + r.guid - regKey, err = registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.SET_VALUE) + regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.SET_VALUE) if err != nil { return regKey, fmt.Errorf("unable to open the interface registry key, key: HKEY_LOCAL_MACHINE\\%s, error: %s", regKeyPath, err) } diff --git a/client/internal/dns/network_manager_linux.go b/client/internal/dns/network_manager_linux.go index 7a1b790e158..955d5492346 100644 --- a/client/internal/dns/network_manager_linux.go +++ b/client/internal/dns/network_manager_linux.go @@ -68,25 +68,23 @@ func (s networkManagerConnSettings) cleanDeprecatedSettings() { } } -func newNetworkManagerDbusConfigurator(wgInterface *iface.WGIface) hostManager { +func newNetworkManagerDbusConfigurator(wgInterface *iface.WGIface) (hostManager, error) { obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusObjectNode) if err != nil { - // todo add proper error handling - panic(err) + return nil, err } defer closeConn() var s string err = obj.Call(networkManagerDbusGetDeviceByIPIfaceMethod, dbusDefaultFlag, wgInterface.GetName()).Store(&s) if err != nil { - // todo add proper error handling - panic(err) + return nil, err } log.Debugf("got network manager dbus Link Object: %s from net interface %s", s, wgInterface.GetName()) return &networkManagerDbusConfigurator{ dbusLinkObject: dbus.ObjectPath(s), - } + }, nil } func (n *networkManagerDbusConfigurator) applyDNSConfig(config hostDNSConfig) error { diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index a972a5b8e9d..91a38cd4ab7 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -52,7 +52,7 @@ type muxUpdate struct { } // NewDefaultServer returns a new dns server -func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface) *DefaultServer { +func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface) (*DefaultServer, error) { mux := dns.NewServeMux() listenIP := defaultIP if runtime.GOOS != "darwin" && wgInterface != nil { @@ -82,13 +82,12 @@ func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface) *DefaultS runtimeIP: listenIP, } - // this should only happen on tests - if wgInterface.Interface == nil { - log.Debugf("returning a server without host manager") - return defaultServer + hostmanager, err := newHostManager(wgInterface) + if err != nil { + return nil, err } - defaultServer.hostManager = newHostManager(wgInterface) - return defaultServer + defaultServer.hostManager = hostmanager + return defaultServer, err } // Start runs the listener in a go routine diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index eb52059e24f..7e0c424bcd6 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -194,7 +194,10 @@ func TestUpdateDNSServer(t *testing.T) { t.Error(err) } ctx := context.Background() - dnsServer := NewDefaultServer(ctx, wgInterface) + dnsServer, err := NewDefaultServer(ctx, wgInterface) + if err != nil { + t.Error(err) + } dnsServer.hostManager = newNoopHostMocker() dnsServer.dnsMuxMap = testCase.initUpstreamMap @@ -243,7 +246,10 @@ func TestDNSServerStartStop(t *testing.T) { t.Error(err) } - dnsServer := NewDefaultServer(ctx, wgInterface) + dnsServer, err := NewDefaultServer(ctx, wgInterface) + if err != nil { + t.Error(err) + } if runtime.GOOS == "windows" && os.Getenv("CI") == "true" { // todo review why this test is not working only on github actions workflows t.Skip("skipping test in Windows CI workflows.") diff --git a/client/internal/dns/systemd_linux.go b/client/internal/dns/systemd_linux.go index d811203c9be..54a73968ae3 100644 --- a/client/internal/dns/systemd_linux.go +++ b/client/internal/dns/systemd_linux.go @@ -47,31 +47,29 @@ type systemdDbusLinkDomainsInput struct { MatchOnly bool } -func newSystemdDbusConfigurator(wgInterface *iface.WGIface) hostManager { +func newSystemdDbusConfigurator(wgInterface *iface.WGIface) (hostManager, error) { iface, err := net.InterfaceByName(wgInterface.GetName()) if err != nil { - // todo add proper error handling - panic(err) + return nil, err } obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode) if err != nil { - // todo add proper error handling - panic(err) + return nil, err } defer closeConn() + var s string err = obj.Call(systemdDbusGetLinkMethod, dbusDefaultFlag, iface.Index).Store(&s) if err != nil { - // todo add proper error handling - panic(err) + return nil, err } log.Debugf("got dbus Link interface: %s from net interface %s and index %d", s, iface.Name, iface.Index) return &systemdDbusConfigurator{ dbusLinkObject: dbus.ObjectPath(s), - } + }, nil } func (s *systemdDbusConfigurator) applyDNSConfig(config hostDNSConfig) error { diff --git a/client/internal/engine.go b/client/internal/engine.go index cca592cccef..62964d66575 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -252,7 +252,11 @@ func (e *Engine) Start() error { e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder) if e.dnsServer == nil { - e.dnsServer = dns.NewDefaultServer(e.ctx, e.wgInterface) + dnsServer, err := dns.NewDefaultServer(e.ctx, e.wgInterface) + if err != nil { + return err + } + e.dnsServer = dnsServer } e.receiveSignalEvents() diff --git a/iface/iface_unix.go b/iface/iface_unix.go index 66d31699793..ebac5d8a156 100644 --- a/iface/iface_unix.go +++ b/iface/iface_unix.go @@ -75,3 +75,8 @@ func (w *WGIface) UpdateAddr(newAddr string) error { w.Address = addr return w.assignAddr() } + +// GetInterfaceGUIDString returns an interface GUID. This is useful on Windows only +func (w *WGIface) GetInterfaceGUIDString() (string, error) { + return "", nil +} diff --git a/iface/iface_windows.go b/iface/iface_windows.go index d38cd3dc426..ab0404401fe 100644 --- a/iface/iface_windows.go +++ b/iface/iface_windows.go @@ -58,6 +58,17 @@ func (w *WGIface) UpdateAddr(newAddr string) error { return w.assignAddr(luid) } +// GetInterfaceGUIDString returns an interface GUID string +func (w *WGIface) GetInterfaceGUIDString() (string, error) { + windowsDevice := w.Interface.(*driver.Adapter) + luid := windowsDevice.LUID() + guid, err := luid.GUID() + if err != nil { + return "", err + } + return guid.String(), nil +} + // WireguardModuleIsLoaded check if we can load wireguard mod (linux only) func WireguardModuleIsLoaded() bool { return false From a6d89a15e7baa423cfca133c23d556ee7ffe29ad Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 22 Nov 2022 12:36:40 +0100 Subject: [PATCH 45/48] add missing fmt argument --- client/internal/dns/host_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/internal/dns/host_linux.go b/client/internal/dns/host_linux.go index 8cf015a9f28..ffb5098c743 100644 --- a/client/internal/dns/host_linux.go +++ b/client/internal/dns/host_linux.go @@ -44,7 +44,7 @@ func getOSDNSManagerType() (osManagerType, error) { file, err := os.Open(defaultResolvConfPath) if err != nil { - return 0, fmt.Errorf("unable to open %s for checking owner, got error: %s", err) + return 0, fmt.Errorf("unable to open %s for checking owner, got error: %s", defaultResolvConfPath, err) } defer file.Close() From 2077675cbf637aaffbfa84a12ba8f49375482581 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 22 Nov 2022 12:47:54 +0100 Subject: [PATCH 46/48] check if interface exist --- iface/iface_windows.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/iface/iface_windows.go b/iface/iface_windows.go index ab0404401fe..5c16916b945 100644 --- a/iface/iface_windows.go +++ b/iface/iface_windows.go @@ -60,6 +60,9 @@ func (w *WGIface) UpdateAddr(newAddr string) error { // GetInterfaceGUIDString returns an interface GUID string func (w *WGIface) GetInterfaceGUIDString() (string, error) { + if w.Interface == nil { + return "", fmt.Errorf("interface has not been initialized yet") + } windowsDevice := w.Interface.(*driver.Adapter) luid := windowsDevice.LUID() guid, err := luid.GUID() From 9c4f08a4365e13d91dd5e56b55e80db67989e836 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 22 Nov 2022 16:42:54 +0100 Subject: [PATCH 47/48] create the interface --- client/internal/dns/server_test.go | 7 +++++-- iface/iface.go | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index 7e0c424bcd6..a878458bd81 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -189,10 +189,13 @@ func TestUpdateDNSServer(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - wgInterface, err := iface.NewWGIFace("utun10000", "127.0.0.1/32", iface.DefaultMTU) + wgInterface, err := iface.NewWGIFace("utun10000", "100.66.0.32/32", iface.DefaultMTU) if err != nil { t.Error(err) } + _ = wgInterface.Create() + defer wgInterface.Close() + ctx := context.Background() dnsServer, err := NewDefaultServer(ctx, wgInterface) if err != nil { @@ -241,7 +244,7 @@ func TestUpdateDNSServer(t *testing.T) { func TestDNSServerStartStop(t *testing.T) { ctx := context.Background() - wgInterface, err := iface.NewWGIFace("utun10000", "127.0.0.1/32", iface.DefaultMTU) + wgInterface, err := iface.NewWGIFace("utun10001", "100.66.0.32/32", iface.DefaultMTU) if err != nil { t.Error(err) } diff --git a/iface/iface.go b/iface/iface.go index 206b9d5aa2c..d75c4db86d7 100644 --- a/iface/iface.go +++ b/iface/iface.go @@ -73,7 +73,9 @@ func parseAddress(address string) (WGAddress, error) { func (w *WGIface) Close() error { w.mu.Lock() defer w.mu.Unlock() - + if w.Interface == nil { + return nil + } err := w.Interface.Close() if err != nil { return err From 3b82aba15db7b4add4b2eaf283df61b8d32afec8 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Wed, 23 Nov 2022 12:46:01 +0100 Subject: [PATCH 48/48] use getDefaultServerWithNoHostManager for tests --- client/internal/dns/server_test.go | 60 ++++++++++++++++++------------ 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index a878458bd81..b0b8cd1eca8 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -3,8 +3,8 @@ package dns import ( "context" "fmt" + "github.com/miekg/dns" nbdns "github.com/netbirdio/netbird/dns" - "github.com/netbirdio/netbird/iface" "net" "net/netip" "os" @@ -189,18 +189,8 @@ func TestUpdateDNSServer(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - wgInterface, err := iface.NewWGIFace("utun10000", "100.66.0.32/32", iface.DefaultMTU) - if err != nil { - t.Error(err) - } - _ = wgInterface.Create() - defer wgInterface.Close() + dnsServer := getDefaultServerWithNoHostManager("127.0.0.1") - ctx := context.Background() - dnsServer, err := NewDefaultServer(ctx, wgInterface) - if err != nil { - t.Error(err) - } dnsServer.hostManager = newNoopHostMocker() dnsServer.dnsMuxMap = testCase.initUpstreamMap @@ -209,7 +199,7 @@ func TestUpdateDNSServer(t *testing.T) { // pretend we are running dnsServer.listenerIsRunning = true - err = dnsServer.UpdateDNSServer(testCase.inputSerial, testCase.inputUpdate) + err := dnsServer.UpdateDNSServer(testCase.inputSerial, testCase.inputUpdate) if err != nil { if testCase.shouldFail { return @@ -243,16 +233,8 @@ func TestUpdateDNSServer(t *testing.T) { } func TestDNSServerStartStop(t *testing.T) { - ctx := context.Background() - wgInterface, err := iface.NewWGIFace("utun10001", "100.66.0.32/32", iface.DefaultMTU) - if err != nil { - t.Error(err) - } + dnsServer := getDefaultServerWithNoHostManager("127.0.0.1") - dnsServer, err := NewDefaultServer(ctx, wgInterface) - if err != nil { - t.Error(err) - } if runtime.GOOS == "windows" && os.Getenv("CI") == "true" { // todo review why this test is not working only on github actions workflows t.Skip("skipping test in Windows CI workflows.") @@ -262,7 +244,7 @@ func TestDNSServerStartStop(t *testing.T) { dnsServer.Start() - err = dnsServer.localResolver.registerRecord(zoneRecords[0]) + err := dnsServer.localResolver.registerRecord(zoneRecords[0]) if err != nil { t.Error(err) } @@ -299,10 +281,40 @@ func TestDNSServerStartStop(t *testing.T) { } dnsServer.Stop() - ctx, cancel := context.WithTimeout(ctx, time.Second*1) + ctx, cancel := context.WithTimeout(context.TODO(), time.Second*1) defer cancel() _, err = resolver.LookupHost(ctx, zoneRecords[0].Name) if err == nil { t.Fatalf("we should encounter an error when querying a stopped server") } } + +func getDefaultServerWithNoHostManager(ip string) *DefaultServer { + mux := dns.NewServeMux() + listenIP := defaultIP + if ip != "" { + listenIP = ip + } + + dnsServer := &dns.Server{ + Addr: fmt.Sprintf("%s:%d", ip, port), + Net: "udp", + Handler: mux, + UDPSize: 65535, + } + + ctx, stop := context.WithCancel(context.TODO()) + + return &DefaultServer{ + ctx: ctx, + stop: stop, + server: dnsServer, + dnsMux: mux, + dnsMuxMap: make(registrationMap), + localResolver: &localResolver{ + registeredMap: make(registrationMap), + }, + runtimePort: port, + runtimeIP: listenIP, + } +}