From 8cfc895b24f94e0af0f3508a23df630475176381 Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Sun, 19 Jan 2025 19:59:30 +0100 Subject: [PATCH 1/6] Add reverse dns zone --- client/internal/dns.go | 85 +++++++++++++++++++++++++++++++++++++++ client/internal/engine.go | 9 +++-- 2 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 client/internal/dns.go diff --git a/client/internal/dns.go b/client/internal/dns.go new file mode 100644 index 00000000000..b9b8a1ca93e --- /dev/null +++ b/client/internal/dns.go @@ -0,0 +1,85 @@ +package internal + +import ( + "net" + "slices" + "strings" + + "github.com/miekg/dns" + log "github.com/sirupsen/logrus" + + nbdns "github.com/netbirdio/netbird/dns" +) + +func createPTRRecord(aRecord nbdns.SimpleRecord, ipNet *net.IPNet) (nbdns.SimpleRecord, bool) { + ip := net.ParseIP(aRecord.RData) + if ip == nil || ip.To4() == nil { + return nbdns.SimpleRecord{}, false + } + + if !ipNet.Contains(ip) { + return nbdns.SimpleRecord{}, false + } + + ipOctets := strings.Split(ip.String(), ".") + slices.Reverse(ipOctets) + rdnsName := dns.Fqdn(strings.Join(ipOctets, ".") + ".in-addr.arpa") + + return nbdns.SimpleRecord{ + Name: rdnsName, + Type: int(dns.TypePTR), + Class: aRecord.Class, + TTL: aRecord.TTL, + RData: dns.Fqdn(aRecord.Name), + }, true +} + +func addReverseZone(config *nbdns.Config, ipNet *net.IPNet) { + networkIP := ipNet.IP.Mask(ipNet.Mask) + + maskOnes, _ := ipNet.Mask.Size() + // round up to nearest byte + octetsToUse := (maskOnes + 7) / 8 + + octets := strings.Split(networkIP.String(), ".") + if octetsToUse > len(octets) { + log.Warnf("invalid network mask size for reverse DNS: %d", maskOnes) + return + } + + reverseOctets := make([]string, octetsToUse) + for i := 0; i < octetsToUse; i++ { + reverseOctets[octetsToUse-1-i] = octets[i] + } + + zoneName := dns.Fqdn(strings.Join(reverseOctets, ".") + ".in-addr.arpa") + + for _, zone := range config.CustomZones { + if zone.Domain == zoneName { + log.Debugf("reverse DNS zone %s already exists", zoneName) + return + } + } + + var records []nbdns.SimpleRecord + + for _, zone := range config.CustomZones { + for _, record := range zone.Records { + if record.Type != int(dns.TypeA) { + continue + } + + if ptrRecord, ok := createPTRRecord(record, ipNet); ok { + records = append(records, ptrRecord) + } + } + } + + reverseZone := nbdns.CustomZone{ + Domain: zoneName, + Records: records, + } + + config.CustomZones = append(config.CustomZones, reverseZone) + log.Debugf("added reverse DNS zone: %s with %d records", zoneName, len(records)) +} diff --git a/client/internal/engine.go b/client/internal/engine.go index b3689c91153..dceb08ca583 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -972,7 +972,7 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error { protoDNSConfig = &mgmProto.DNSConfig{} } - if err := e.dnsServer.UpdateDNSServer(serial, toDNSConfig(protoDNSConfig)); err != nil { + if err := e.dnsServer.UpdateDNSServer(serial, toDNSConfig(protoDNSConfig, e.wgInterface.Address().Network)); err != nil { log.Errorf("failed to update dns server, err: %v", err) } @@ -1041,7 +1041,7 @@ func toRouteDomains(myPubKey string, protoRoutes []*mgmProto.Route) []string { return dnsRoutes } -func toDNSConfig(protoDNSConfig *mgmProto.DNSConfig) nbdns.Config { +func toDNSConfig(protoDNSConfig *mgmProto.DNSConfig, network *net.IPNet) nbdns.Config { dnsUpdate := nbdns.Config{ ServiceEnable: protoDNSConfig.GetServiceEnable(), CustomZones: make([]nbdns.CustomZone, 0), @@ -1081,6 +1081,9 @@ func toDNSConfig(protoDNSConfig *mgmProto.DNSConfig) nbdns.Config { } dnsUpdate.NameServerGroups = append(dnsUpdate.NameServerGroups, dnsNSGroup) } + + addReverseZone(&dnsUpdate, network) + return dnsUpdate } @@ -1387,7 +1390,7 @@ func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) { return nil, nil, err } routes := toRoutes(netMap.GetRoutes()) - dnsCfg := toDNSConfig(netMap.GetDNSConfig()) + dnsCfg := toDNSConfig(netMap.GetDNSConfig(), e.wgInterface.Address().Network) return routes, &dnsCfg, nil } From a0ebf7e67ea088c00b522d5969e97a52694ebe8a Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Sun, 19 Jan 2025 20:24:13 +0100 Subject: [PATCH 2/6] Skip records and zones on errors instead of failing --- client/internal/dns/server.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 1fe913fd9c1..408fe8cbc0b 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -425,7 +425,8 @@ func (s *DefaultServer) buildLocalHandlerUpdate(customZones []nbdns.CustomZone) for _, customZone := range customZones { if len(customZone.Records) == 0 { - return nil, nil, fmt.Errorf("received an empty list of records") + log.Warnf("received a custom zone with empty records, skipping domain: %s", customZone.Domain) + continue } muxUpdates = append(muxUpdates, muxUpdate{ @@ -437,7 +438,8 @@ func (s *DefaultServer) buildLocalHandlerUpdate(customZones []nbdns.CustomZone) for _, record := range customZone.Records { var class uint16 = dns.ClassINET if record.Class != nbdns.DefaultClass { - return nil, nil, fmt.Errorf("received an invalid class type: %s", record.Class) + log.Warnf("received an invalid class type: %s", record.Class) + continue } key := buildRecordKey(record.Name, class, uint16(record.Type)) localRecords[key] = record From 21ad8d277abf7094ca2e441528988b025411494d Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Sun, 19 Jan 2025 20:35:14 +0100 Subject: [PATCH 3/6] Improve error messages --- client/internal/dns/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 408fe8cbc0b..f7f6b381c8f 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -380,11 +380,11 @@ func (s *DefaultServer) applyConfiguration(update nbdns.Config) error { localMuxUpdates, localRecords, err := s.buildLocalHandlerUpdate(update.CustomZones) if err != nil { - return fmt.Errorf("not applying dns update, error: %v", err) + return fmt.Errorf("local handler updater: %w", err) } upstreamMuxUpdates, err := s.buildUpstreamHandlerUpdate(update.NameServerGroups) if err != nil { - return fmt.Errorf("not applying dns update, error: %v", err) + return fmt.Errorf("upstream handler updater: %w", err) } muxUpdates := append(localMuxUpdates, upstreamMuxUpdates...) //nolint:gocritic From 27fc95326d7ef95c8b60724f7552b035c5266746 Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Sun, 19 Jan 2025 20:37:25 +0100 Subject: [PATCH 4/6] Fix test --- client/internal/engine_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index ca49eca09f6..a92b5b77228 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -250,6 +250,15 @@ func TestEngine_UpdateNetworkMap(t *testing.T) { RemovePeerFunc: func(peerKey string) error { return nil }, + AddressFunc: func() iface.WGAddress { + return iface.WGAddress{ + IP: net.ParseIP("10.20.0.1"), + Network: &net.IPNet{ + IP: net.ParseIP("10.20.0.0"), + Mask: net.IPv4Mask(255, 255, 255, 0), + }, + } + }, } engine.wgInterface = wgIface engine.routeManager = routemanager.NewManager(routemanager.ManagerConfig{ From 6b1de5158b7aa7b8b7e866c0731e82d40cc786a6 Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Sun, 19 Jan 2025 21:27:02 +0100 Subject: [PATCH 5/6] Fix engine test --- client/internal/engine.go | 4 +++- client/internal/engine_test.go | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index dceb08ca583..a936a844c16 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -1082,7 +1082,9 @@ func toDNSConfig(protoDNSConfig *mgmProto.DNSConfig, network *net.IPNet) nbdns.C dnsUpdate.NameServerGroups = append(dnsUpdate.NameServerGroups, dnsNSGroup) } - addReverseZone(&dnsUpdate, network) + if len(dnsUpdate.CustomZones) > 0 { + addReverseZone(&dnsUpdate, network) + } return dnsUpdate } diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index a92b5b77228..f19abdf7083 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -701,6 +701,9 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) { }, }, }, + { + Domain: "0.66.100.in-addr.arpa.", + }, }, NameServerGroups: []*mgmtProto.NameServerGroup{ { @@ -730,6 +733,9 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) { }, }, }, + { + Domain: "0.66.100.in-addr.arpa.", + }, }, expectedNSGroupsLen: 1, expectedNSGroups: []*nbdns.NameServerGroup{ From ce1c824016238dd0a9da3e2c64b1d86b8e8f9bb8 Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Sun, 19 Jan 2025 22:20:10 +0100 Subject: [PATCH 6/6] Fix DNS test --- client/internal/dns/server_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index c166820c457..e0cb634d3e4 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -220,7 +220,7 @@ func TestUpdateDNSServer(t *testing.T) { shouldFail: true, }, { - name: "Invalid Custom Zone Records list Should Fail", + name: "Invalid Custom Zone Records list Should Skip", initLocalMap: make(registrationMap), initUpstreamMap: make(registeredHandlerMap), initSerial: 0, @@ -239,7 +239,7 @@ func TestUpdateDNSServer(t *testing.T) { }, }, }, - shouldFail: true, + expectedUpstreamMap: registeredHandlerMap{".": dummyHandler}, }, { name: "Empty Config Should Succeed and Clean Maps",