mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-27 01:51:30 +02:00
226 lines
7.5 KiB
Go
226 lines
7.5 KiB
Go
package internal
|
|
|
|
import (
|
|
"net/netip"
|
|
"testing"
|
|
|
|
"github.com/miekg/dns"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
|
nbdns "github.com/netbirdio/netbird/dns"
|
|
mgmProto "github.com/netbirdio/netbird/shared/management/proto"
|
|
)
|
|
|
|
func TestCreatePTRRecord_IPv4(t *testing.T) {
|
|
record := nbdns.SimpleRecord{
|
|
Name: "peer1.netbird.cloud.",
|
|
Type: int(dns.TypeA),
|
|
Class: nbdns.DefaultClass,
|
|
TTL: 300,
|
|
RData: "100.64.0.5",
|
|
}
|
|
prefix := netip.MustParsePrefix("100.64.0.0/16")
|
|
|
|
ptr, ok := createPTRRecord(record, prefix)
|
|
require.True(t, ok)
|
|
assert.Equal(t, "5.0.64.100.in-addr.arpa.", ptr.Name)
|
|
assert.Equal(t, int(dns.TypePTR), ptr.Type)
|
|
assert.Equal(t, "peer1.netbird.cloud.", ptr.RData)
|
|
}
|
|
|
|
func TestCreatePTRRecord_IPv6(t *testing.T) {
|
|
record := nbdns.SimpleRecord{
|
|
Name: "peer1.netbird.cloud.",
|
|
Type: int(dns.TypeAAAA),
|
|
Class: nbdns.DefaultClass,
|
|
TTL: 300,
|
|
RData: "fd00:1234:5678::1",
|
|
}
|
|
prefix := netip.MustParsePrefix("fd00:1234:5678::/48")
|
|
|
|
ptr, ok := createPTRRecord(record, prefix)
|
|
require.True(t, ok)
|
|
assert.Equal(t, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.7.6.5.4.3.2.1.0.0.d.f.ip6.arpa.", ptr.Name)
|
|
assert.Equal(t, int(dns.TypePTR), ptr.Type)
|
|
assert.Equal(t, "peer1.netbird.cloud.", ptr.RData)
|
|
}
|
|
|
|
func TestCreatePTRRecord_OutOfRange(t *testing.T) {
|
|
record := nbdns.SimpleRecord{
|
|
Name: "peer1.netbird.cloud.",
|
|
Type: int(dns.TypeA),
|
|
RData: "10.0.0.1",
|
|
}
|
|
prefix := netip.MustParsePrefix("100.64.0.0/16")
|
|
|
|
_, ok := createPTRRecord(record, prefix)
|
|
assert.False(t, ok)
|
|
}
|
|
|
|
func TestGenerateReverseZoneName_IPv4(t *testing.T) {
|
|
tests := []struct {
|
|
prefix string
|
|
expected string
|
|
}{
|
|
{"100.64.0.0/16", "64.100.in-addr.arpa."},
|
|
{"10.0.0.0/8", "10.in-addr.arpa."},
|
|
{"192.168.1.0/24", "1.168.192.in-addr.arpa."},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.prefix, func(t *testing.T) {
|
|
zone, err := generateReverseZoneName(netip.MustParsePrefix(tt.prefix))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tt.expected, zone)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateReverseZoneName_IPv6(t *testing.T) {
|
|
tests := []struct {
|
|
prefix string
|
|
expected string
|
|
}{
|
|
{"fd00:1234:5678::/48", "8.7.6.5.4.3.2.1.0.0.d.f.ip6.arpa."},
|
|
{"fd00::/16", "0.0.d.f.ip6.arpa."},
|
|
{"fd12:3456:789a:bcde::/64", "e.d.c.b.a.9.8.7.6.5.4.3.2.1.d.f.ip6.arpa."},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.prefix, func(t *testing.T) {
|
|
zone, err := generateReverseZoneName(netip.MustParsePrefix(tt.prefix))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tt.expected, zone)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCollectPTRRecords_BothFamilies(t *testing.T) {
|
|
config := &nbdns.Config{
|
|
CustomZones: []nbdns.CustomZone{
|
|
{
|
|
Domain: "netbird.cloud.",
|
|
Records: []nbdns.SimpleRecord{
|
|
{Name: "peer1.netbird.cloud.", Type: int(dns.TypeA), RData: "100.64.0.1"},
|
|
{Name: "peer1.netbird.cloud.", Type: int(dns.TypeAAAA), RData: "fd00::1"},
|
|
{Name: "peer2.netbird.cloud.", Type: int(dns.TypeA), RData: "100.64.0.2"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
v4Records := collectPTRRecords(config, netip.MustParsePrefix("100.64.0.0/16"))
|
|
assert.Len(t, v4Records, 2, "should collect 2 A record PTRs for the v4 prefix")
|
|
|
|
v6Records := collectPTRRecords(config, netip.MustParsePrefix("fd00::/64"))
|
|
assert.Len(t, v6Records, 1, "should collect 1 AAAA record PTR for the v6 prefix")
|
|
}
|
|
|
|
func TestAddReverseZone_IPv6(t *testing.T) {
|
|
config := &nbdns.Config{
|
|
CustomZones: []nbdns.CustomZone{
|
|
{
|
|
Domain: "netbird.cloud.",
|
|
Records: []nbdns.SimpleRecord{
|
|
{Name: "peer1.netbird.cloud.", Type: int(dns.TypeAAAA), RData: "fd00:1234:5678::1"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
addReverseZone(config, netip.MustParsePrefix("fd00:1234:5678::/48"))
|
|
|
|
require.Len(t, config.CustomZones, 2)
|
|
reverseZone := config.CustomZones[1]
|
|
assert.Equal(t, "8.7.6.5.4.3.2.1.0.0.d.f.ip6.arpa.", reverseZone.Domain)
|
|
assert.Len(t, reverseZone.Records, 1)
|
|
assert.Equal(t, int(dns.TypePTR), reverseZone.Records[0].Type)
|
|
}
|
|
|
|
// TestToDNSConfig_ZoneFlagsPreserved pins the per-zone NonAuthoritative flag
|
|
// through the legacy DNSConfig path. A non-authoritative zone is match-only:
|
|
// the local resolver falls through to the upstream for an in-zone name it does
|
|
// not define. The built-in peer zone is the authoritative one and must stay
|
|
// that way, so the flag has to travel per zone rather than be derived.
|
|
func TestToDNSConfig_ZoneFlagsPreserved(t *testing.T) {
|
|
config := toDNSConfig(&mgmProto.DNSConfig{
|
|
ServiceEnable: true,
|
|
CustomZones: []*mgmProto.CustomZone{
|
|
{
|
|
Domain: "netbird.cloud.",
|
|
Records: []*mgmProto.SimpleRecord{
|
|
{Name: "peer1.netbird.cloud.", Type: int64(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "100.64.0.1"},
|
|
},
|
|
},
|
|
{
|
|
Domain: "corp.internal.",
|
|
NonAuthoritative: true,
|
|
SearchDomainDisabled: true,
|
|
Records: []*mgmProto.SimpleRecord{
|
|
{Name: "db.corp.internal.", Type: int64(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.10.0.5"},
|
|
},
|
|
},
|
|
},
|
|
}, wgaddr.Address{
|
|
IP: netip.MustParseAddr("100.64.0.1"),
|
|
Network: netip.MustParsePrefix("100.64.0.0/16"),
|
|
})
|
|
|
|
zones := make(map[string]nbdns.CustomZone, len(config.CustomZones))
|
|
for _, zone := range config.CustomZones {
|
|
zones[zone.Domain] = zone
|
|
}
|
|
|
|
peerZone, ok := zones["netbird.cloud."]
|
|
require.True(t, ok, "peer zone must survive")
|
|
assert.False(t, peerZone.NonAuthoritative, "the built-in peer zone owns the account domain and stays authoritative")
|
|
|
|
accountZone, ok := zones["corp.internal."]
|
|
require.True(t, ok, "account zone must survive")
|
|
assert.True(t, accountZone.NonAuthoritative, "an account zone stays match-only, else undefined in-zone names get black-holed")
|
|
assert.True(t, accountZone.SearchDomainDisabled)
|
|
}
|
|
|
|
// TestToDNSConfig_SingleZoneForcedAuthoritative pins the compatibility clause
|
|
// in toDNSConfig: a config carrying exactly one zone is treated as
|
|
// authoritative no matter what the server said, because servers that predate
|
|
// the NonAuthoritative field send only the peer FQDN zone.
|
|
//
|
|
// The clause can only ever downgrade an explicit true to false, so a server
|
|
// that legitimately sends a single non-authoritative zone — an account whose
|
|
// only zone is a custom one, with no peer records to build the built-in zone
|
|
// from — gets that zone's whole apex black-holed on the client. Real accounts
|
|
// always carry the peer zone alongside, which is why this is latent. Narrowing
|
|
// it needs a way to tell "unset" from "false" on the wire, or the account
|
|
// domain passed down here; until then this test states the contract so a
|
|
// change to it is deliberate.
|
|
func TestToDNSConfig_SingleZoneForcedAuthoritative(t *testing.T) {
|
|
config := toDNSConfig(&mgmProto.DNSConfig{
|
|
ServiceEnable: true,
|
|
CustomZones: []*mgmProto.CustomZone{
|
|
{
|
|
Domain: "corp.internal.",
|
|
NonAuthoritative: true,
|
|
Records: []*mgmProto.SimpleRecord{
|
|
{Name: "db.corp.internal.", Type: int64(dns.TypeA), Class: nbdns.DefaultClass, TTL: 300, RData: "10.10.0.5"},
|
|
},
|
|
},
|
|
},
|
|
}, wgaddr.Address{
|
|
IP: netip.MustParseAddr("100.64.0.1"),
|
|
Network: netip.MustParsePrefix("100.64.0.0/16"),
|
|
})
|
|
|
|
require.NotEmpty(t, config.CustomZones)
|
|
assert.Equal(t, "corp.internal.", config.CustomZones[0].Domain)
|
|
assert.False(t, config.CustomZones[0].NonAuthoritative,
|
|
"a lone zone is forced authoritative for pre-NonAuthoritative servers")
|
|
|
|
// The reverse zone the config gains afterwards must not feed back into the
|
|
// decision: the compat gate counts the zones the server sent.
|
|
require.Len(t, config.CustomZones, 2, "a reverse zone is appended for the overlay prefix")
|
|
assert.Equal(t, "64.100.in-addr.arpa.", config.CustomZones[1].Domain)
|
|
}
|