include NonAuthoritative zone in tests

This commit is contained in:
pascal
2026-08-21 16:05:51 +02:00
parent a4900ae6ca
commit 1cef517a97
7 changed files with 176 additions and 1 deletions

View File

@@ -8,7 +8,9 @@ import (
"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) {
@@ -136,3 +138,88 @@ func TestAddReverseZone_IPv6(t *testing.T) {
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)
}

View File

@@ -47,8 +47,14 @@ insert into zones (id, account_id, domain, enabled, enable_search_domain, distri
VALUES('zone-331','account-33','test-331.com',true,true,'["33-group-one-resource-id"]');
insert into zones (id, account_id, domain, enabled, enable_search_domain, distribution_groups)
VALUES('zone-332','account-33','disabled-331.com',false,true,'["33-group-one-resource-id"]');
insert into zones (id, account_id, domain, enabled, enable_search_domain, distribution_groups)
VALUES('zone-333','account-33','search-off-331.com',true,false,'["33-group-two-resources-id"]');
insert into records (id, account_id, zone_id, name, type, ttl, content)
VALUES('record-333','account-33','zone-332','test.disabled-331.com','A',1800,'1.1.1.9');
insert into records (id, account_id, zone_id, name, type, ttl, content)
VALUES('record-334','account-33','zone-333','test.search-off-331.com','A',1800,'1.1.1.3');
insert into records (id, account_id, zone_id, name, type, ttl, content)
VALUES('record-335','account-33','zone-333','alias.search-off-331.com','CNAME',1800,'test.search-off-331.com');
insert into records (id, account_id, zone_id, name, type, ttl, content)
VALUES('record-331','account-33','zone-331','test.test-331.com','A',1800,'1.1.1.1');
insert into records (id, account_id, zone_id, name, type, ttl, content)

View File

@@ -513,6 +513,32 @@
"SearchDomainDisabled": false,
"NonAuthoritative": true
}
},
{
"DistributionGroups": [
"33-group-two-resources-id"
],
"Zone": {
"Domain": "search-off-331.com.",
"Records": [
{
"Name": "test.search-off-331.com.",
"Type": 1,
"Class": "IN",
"TTL": 1800,
"RData": "1.1.1.3"
},
{
"Name": "alias.search-off-331.com.",
"Type": 5,
"Class": "IN",
"TTL": 1800,
"RData": "test.search-off-331.com."
}
],
"SearchDomainDisabled": true,
"NonAuthoritative": true
}
}
],
"PrivateServiceCandidates": null,

View File

@@ -1,5 +1,5 @@
{
"description": "Nameserver group and applied custom zone distributed to grp-dev; peer-a (with an extra DNS label) receives them, peer-c outside the group receives neither.",
"description": "Nameserver group and applied custom zones distributed to grp-dev; peer-a (with an extra DNS label) receives them, peer-c is outside that group and receives only the zone distributed to grp-ops. Zone flags travel per zone: both grp-dev zones are match-only (NonAuthoritative), only search-off.internal. disables the search domain, and the built-in peer zone stays authoritative.",
"peers": [
"peer-a",
"peer-c"

View File

@@ -45,6 +45,27 @@
}
]
},
{
"Domain": "search-off.internal.",
"SearchDomainDisabled": true,
"NonAuthoritative": true,
"Records": [
{
"Name": "alias.search-off.internal.",
"Type": "5",
"Class": "IN",
"TTL": "300",
"RData": "app.search-off.internal."
},
{
"Name": "app.search-off.internal.",
"Type": "1",
"Class": "IN",
"TTL": "300",
"RData": "10.10.0.6"
}
]
},
{
"Domain": "netbird.test.",
"Records": [

View File

@@ -22,6 +22,19 @@
"RData": "100.64.0.3"
}
]
},
{
"Domain": "ops-only.internal.",
"NonAuthoritative": true,
"Records": [
{
"Name": "tool.ops-only.internal.",
"Type": "1",
"Class": "IN",
"TTL": "300",
"RData": "10.10.0.7"
}
]
}
],
"ForwarderPort": "22054"

View File

@@ -47,6 +47,28 @@
{"Name": "db.corp.internal.", "Type": 1, "Class": "IN", "TTL": 300, "RData": "10.10.0.5"}
]
}
},
{
"DistributionGroups": ["grp-dev"],
"Zone": {
"Domain": "search-off.internal.",
"NonAuthoritative": true,
"SearchDomainDisabled": true,
"Records": [
{"Name": "app.search-off.internal.", "Type": 1, "Class": "IN", "TTL": 300, "RData": "10.10.0.6"},
{"Name": "alias.search-off.internal.", "Type": 5, "Class": "IN", "TTL": 300, "RData": "app.search-off.internal."}
]
}
},
{
"DistributionGroups": ["grp-ops"],
"Zone": {
"Domain": "ops-only.internal.",
"NonAuthoritative": true,
"Records": [
{"Name": "tool.ops-only.internal.", "Type": 1, "Class": "IN", "TTL": 300, "RData": "10.10.0.7"}
]
}
}
]
}