mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-03 11:28:38 -04:00
client/dns: register pool-root domains as host-manager match domains
The subdomain-matching handler chain entry only helps when the OS DNS manager routes the query to the daemon's listener. On systems using systemd-resolved (Ubuntu 20.04 / 22.04, most modern Debian/RHEL-family distros), the daemon delegates a *specific set* of match domains to the wt0 link via D-Bus. Any FQDN outside that set is answered from the host's global upstream DNS, never touching the handler chain. With only 'netbird.cloud' and the in-addr.arpa reverse zone advertised as match domains, a relay instance FQDN like streamline-de-fra1-0.relay.netbird.io is resolved globally. When an exit-node default route is active on wt0 but the peer is not yet live, that global lookup times out on the local stub (127.0.0.53) because systemd-resolved's uplink DNS is unreachable through the overlay, and the foreign-relay dial fails exactly the way the ENOKEY path did on file-manager hosts. Bump the pool-root domains (ServerDomains.Relay entries) into extraDomains so applyHostConfig adds them as match-only domains alongside 'netbird.cloud'. systemd-resolved then delegates *.relay.netbird.io to the wt0 link, queries hit the daemon's DNS listener, and the on-demand resolve path via the bypass resolver runs as intended. Tracking map mgmtPoolRoots isolates this refcount from the RegisterHandler path so updates across successive mgmt syncs increment/decrement only the changed set.
This commit is contained in:
@@ -117,6 +117,11 @@ type DefaultServer struct {
|
||||
batchMode bool
|
||||
|
||||
mgmtCacheResolver *mgmt.Resolver
|
||||
// mgmtPoolRoots tracks pool-root domains currently contributed to
|
||||
// extraDomains by the mgmt cache, so the next UpdateServerConfig can
|
||||
// decrement the old set before incrementing the new one without
|
||||
// disturbing unrelated registerHandler callers.
|
||||
mgmtPoolRoots map[domain.Domain]struct{}
|
||||
|
||||
// permanent related properties
|
||||
permanent bool
|
||||
@@ -251,6 +256,7 @@ func newDefaultServer(
|
||||
hostsDNSHolder: newHostsDNSHolder(),
|
||||
hostManager: &noopHostConfigurator{},
|
||||
mgmtCacheResolver: mgmtCacheResolver,
|
||||
mgmtPoolRoots: make(map[domain.Domain]struct{}),
|
||||
currentConfigHash: ^uint64(0), // Initialize to max uint64 to ensure first config is always applied
|
||||
}
|
||||
|
||||
@@ -646,6 +652,39 @@ func (s *DefaultServer) UpdateServerConfig(domains dnsconfig.ServerDomains) erro
|
||||
if len(exactDomains) > 0 {
|
||||
s.registerHandler(exactDomains.ToPunycodeList(), s.mgmtCacheResolver, PriorityMgmtCache)
|
||||
}
|
||||
|
||||
// Reconcile extraDomains with the current pool-root set. Pool
|
||||
// roots registered here are *match* domains for the host DNS
|
||||
// manager (systemd-resolved, NetworkManager, etc.), so that
|
||||
// instance subdomain queries like streamline-* are delegated to
|
||||
// the wt0 link where the daemon's DNS listener sits. Without
|
||||
// this, systemd-resolved answers them from the host's global
|
||||
// upstream, skipping our handler chain entirely.
|
||||
//
|
||||
// Use a dedicated tracking map so that increments/decrements
|
||||
// here don't collide with RegisterHandler's refcounting.
|
||||
newPoolRoots := make(map[domain.Domain]struct{}, len(poolRoots))
|
||||
for _, d := range poolRoots {
|
||||
zone := toZone(d)
|
||||
newPoolRoots[zone] = struct{}{}
|
||||
if _, already := s.mgmtPoolRoots[zone]; !already {
|
||||
s.extraDomains[zone]++
|
||||
}
|
||||
}
|
||||
for zone := range s.mgmtPoolRoots {
|
||||
if _, keep := newPoolRoots[zone]; keep {
|
||||
continue
|
||||
}
|
||||
s.extraDomains[zone]--
|
||||
if s.extraDomains[zone] <= 0 {
|
||||
delete(s.extraDomains, zone)
|
||||
}
|
||||
}
|
||||
s.mgmtPoolRoots = newPoolRoots
|
||||
|
||||
if !s.batchMode {
|
||||
s.applyHostConfig()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user