Skip to content
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
21a3649
wip: add base struct
bcmmbaga Nov 20, 2025
8a39013
implement dns zone
bcmmbaga Nov 23, 2025
9cb95be
implement dns zone record
bcmmbaga Nov 24, 2025
633449c
update DNS record events and validation for zone records
bcmmbaga Nov 24, 2025
b4bf3a1
Refactor
bcmmbaga Nov 27, 2025
01741e8
add custom zones network map calculation
bcmmbaga Nov 27, 2025
a676a18
fix tests
bcmmbaga Nov 27, 2025
cf94a7a
add DNS Zones API client and integration tests
bcmmbaga Nov 27, 2025
f5db1b0
use transaction
bcmmbaga Nov 27, 2025
82255ba
Merge branch 'main' into feature/custom-dns-zones
bcmmbaga Nov 27, 2025
b0e33bc
Add zone search domain status to proto
bcmmbaga Nov 27, 2025
566a3a6
Skip zone with empty records
bcmmbaga Nov 27, 2025
0e644d2
Fix lint
bcmmbaga Nov 27, 2025
4a946c0
Refactor
bcmmbaga Nov 28, 2025
25c9cb2
Add tests
bcmmbaga Nov 28, 2025
718b713
Enforce domain immutability on zone updates and add unit tests for zo…
bcmmbaga Nov 28, 2025
1782bbe
Update OpenAPI
bcmmbaga Nov 29, 2025
604085d
Add DNS records to zone model and update OpenAPI specs
bcmmbaga Dec 2, 2025
783a120
Merge branch 'main' into feature/custom-dns-zones
bcmmbaga Dec 2, 2025
d4416ed
fix merge
bcmmbaga Dec 2, 2025
e224ec9
Format cname dns zone record as fqdn
bcmmbaga Dec 8, 2025
65e5eba
Add DNS domain conflict validation for zone creation
bcmmbaga Dec 8, 2025
5e5312f
Add DNS domain conflict check when updating account settings
bcmmbaga Dec 9, 2025
e534845
Fix lint
bcmmbaga Dec 10, 2025
aa4ab37
Refactor legacy netmap
bcmmbaga Dec 10, 2025
dbeeb72
Refactor new netmap
bcmmbaga Dec 11, 2025
2b7d831
Merge branch 'main' into feature/custom-dns-zones
bcmmbaga Dec 11, 2025
a98dfb3
fix merge and lint
bcmmbaga Dec 11, 2025
01d752e
Merge branch 'main' into feature/custom-dns-zones
bcmmbaga Dec 12, 2025
176fd64
fix merge
bcmmbaga Dec 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 107 additions & 14 deletions management/internals/controllers/network_map/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"sync/atomic"
"time"

"github.com/miekg/dns"
log "github.com/sirupsen/logrus"
"golang.org/x/exp/maps"
"golang.org/x/mod/semver"
Expand All @@ -20,6 +21,8 @@ import (
"github.com/netbirdio/netbird/management/internals/controllers/network_map"
"github.com/netbirdio/netbird/management/internals/controllers/network_map/controller/cache"
"github.com/netbirdio/netbird/management/internals/modules/peers/ephemeral"
"github.com/netbirdio/netbird/management/internals/modules/zones"
"github.com/netbirdio/netbird/management/internals/modules/zones/records"
"github.com/netbirdio/netbird/management/internals/server/config"
"github.com/netbirdio/netbird/management/internals/shared/grpc"
"github.com/netbirdio/netbird/management/server/account"
Expand Down Expand Up @@ -175,7 +178,7 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin

dnsCache := &cache.DNSConfigCache{}
dnsDomain := c.GetDNSDomain(account.Settings)
customZone := account.GetPeersCustomZone(ctx, dnsDomain)
peersCustomZone := account.GetPeersCustomZone(ctx, dnsDomain)
resourcePolicies := account.GetResourcePoliciesMap()
routers := account.GetResourceRoutersMap()

Expand All @@ -196,6 +199,12 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin

dnsFwdPort := computeForwarderPort(maps.Values(account.Peers), network_map.DnsForwarderPortMinVersion)

accountZones, err := c.repo.GetAccountZones(ctx, account.Id)
if err != nil {
log.WithContext(ctx).Errorf("failed to get account zones: %v", err)
return fmt.Errorf("failed to get account zones: %v", err)
}

for _, peer := range account.Peers {
if !c.peersUpdateManager.HasChannel(peer.ID) {
log.WithContext(ctx).Tracef("peer %s doesn't have a channel, skipping network map update", peer.ID)
Expand All @@ -222,9 +231,9 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin
var remotePeerNetworkMap *types.NetworkMap

if c.experimentalNetworkMap(accountID) {
remotePeerNetworkMap = c.getPeerNetworkMapExp(ctx, p.AccountID, p.ID, approvedPeersMap, customZone, c.accountManagerMetrics)
remotePeerNetworkMap = c.getPeerNetworkMapExp(ctx, p.AccountID, p.ID, approvedPeersMap, peersCustomZone, accountZones, c.accountManagerMetrics)
} else {
remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, p.ID, customZone, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics)
remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, p.ID, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics)
}

c.metrics.CountCalcPeerNetworkMapDuration(time.Since(start))
Expand Down Expand Up @@ -317,7 +326,7 @@ func (c *Controller) UpdateAccountPeer(ctx context.Context, accountId string, pe

dnsCache := &cache.DNSConfigCache{}
dnsDomain := c.GetDNSDomain(account.Settings)
customZone := account.GetPeersCustomZone(ctx, dnsDomain)
peersCustomZone := account.GetPeersCustomZone(ctx, dnsDomain)
resourcePolicies := account.GetResourcePoliciesMap()
routers := account.GetResourceRoutersMap()

Expand All @@ -333,12 +342,18 @@ func (c *Controller) UpdateAccountPeer(ctx context.Context, accountId string, pe
return err
}

accountZones, err := c.repo.GetAccountZones(ctx, account.Id)
if err != nil {
log.WithContext(ctx).Errorf("failed to get account zones: %v", err)
return err
}

var remotePeerNetworkMap *types.NetworkMap

if c.experimentalNetworkMap(accountId) {
remotePeerNetworkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, customZone, c.accountManagerMetrics)
remotePeerNetworkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, peersCustomZone, accountZones, c.accountManagerMetrics)
} else {
remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, peerId, customZone, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics)
remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, peerId, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics)
}

proxyNetworkMap, ok := proxyNetworkMaps[peer.ID]
Expand Down Expand Up @@ -432,7 +447,14 @@ func (c *Controller) GetValidatedPeerWithMap(ctx context.Context, isRequiresAppr
}
log.WithContext(ctx).Debugf("getPeerPostureChecks took %s", time.Since(startPosture))

customZone := account.GetPeersCustomZone(ctx, c.GetDNSDomain(account.Settings))
accountZones, err := c.repo.GetAccountZones(ctx, account.Id)
if err != nil {
log.WithContext(ctx).Errorf("failed to get account zones: %v", err)
return nil, nil, nil, 0, err
}

dnsDomain := c.GetDNSDomain(account.Settings)
peersCustomZone := account.GetPeersCustomZone(ctx, dnsDomain)

proxyNetworkMaps, err := c.proxyController.GetProxyNetworkMaps(ctx, account.Id, peer.ID, account.Peers)
if err != nil {
Expand All @@ -443,9 +465,9 @@ func (c *Controller) GetValidatedPeerWithMap(ctx context.Context, isRequiresAppr
var networkMap *types.NetworkMap

if c.experimentalNetworkMap(accountID) {
networkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, customZone, c.accountManagerMetrics)
networkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, peersCustomZone, accountZones, c.accountManagerMetrics)
} else {
networkMap = account.GetPeerNetworkMap(ctx, peer.ID, customZone, approvedPeersMap, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), c.accountManagerMetrics)
networkMap = account.GetPeerNetworkMap(ctx, peer.ID, peersCustomZone, accountZones, approvedPeersMap, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), c.accountManagerMetrics)
}

proxyNetworkMap, ok := proxyNetworkMaps[peer.ID]
Expand All @@ -468,7 +490,8 @@ func (c *Controller) getPeerNetworkMapExp(
accountId string,
peerId string,
validatedPeers map[string]struct{},
customZone nbdns.CustomZone,
peersCustomZone nbdns.CustomZone,
accountZones []*zones.Zone,
metrics *telemetry.AccountManagerMetrics,
) *types.NetworkMap {
account := c.getAccountFromHolderOrInit(accountId)
Expand All @@ -478,7 +501,8 @@ func (c *Controller) getPeerNetworkMapExp(
Network: &types.Network{},
}
}
return account.GetPeerNetworkMapExp(ctx, peerId, customZone, validatedPeers, metrics)

return account.GetPeerNetworkMapExp(ctx, peerId, peersCustomZone, accountZones, validatedPeers, metrics)
}

func (c *Controller) onPeerAddedUpdNetworkMapCache(account *types.Account, peerId string) error {
Expand Down Expand Up @@ -798,7 +822,15 @@ func (c *Controller) GetNetworkMap(ctx context.Context, peerID string) (*types.N
if err != nil {
return nil, err
}
customZone := account.GetPeersCustomZone(ctx, c.GetDNSDomain(account.Settings))

accountZones, err := c.repo.GetAccountZones(ctx, account.Id)
if err != nil {
log.WithContext(ctx).Errorf("failed to get account zones: %v", err)
return nil, err
}

dnsDomain := c.GetDNSDomain(account.Settings)
peersCustomZone := account.GetPeersCustomZone(ctx, dnsDomain)

proxyNetworkMaps, err := c.proxyController.GetProxyNetworkMaps(ctx, account.Id, peerID, account.Peers)
if err != nil {
Expand All @@ -809,9 +841,9 @@ func (c *Controller) GetNetworkMap(ctx context.Context, peerID string) (*types.N
var networkMap *types.NetworkMap

if c.experimentalNetworkMap(peer.AccountID) {
networkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peerID, validatedPeers, customZone, nil)
networkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peerID, validatedPeers, peersCustomZone, accountZones, nil)
} else {
networkMap = account.GetPeerNetworkMap(ctx, peer.ID, customZone, validatedPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil)
networkMap = account.GetPeerNetworkMap(ctx, peer.ID, peersCustomZone, accountZones, validatedPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil)
}

proxyNetworkMap, ok := proxyNetworkMaps[peer.ID]
Expand All @@ -825,3 +857,64 @@ func (c *Controller) GetNetworkMap(ctx context.Context, peerID string) (*types.N
func (c *Controller) DisconnectPeers(ctx context.Context, accountId string, peerIDs []string) {
c.peersUpdateManager.CloseChannels(ctx, peerIDs)
}

func filterPeerAppliedZones(ctx context.Context, accountZones []*zones.Zone, peerGroups types.LookupMap) []nbdns.CustomZone {
var customZones []nbdns.CustomZone

if len(peerGroups) == 0 {
return customZones
}

for _, zone := range accountZones {
if !zone.Enabled || len(zone.Records) == 0 {
continue
}

hasAccess := false
for _, distGroupID := range zone.DistributionGroups {
if _, found := peerGroups[distGroupID]; found {
hasAccess = true
break
}
}

if !hasAccess {
continue
}

simpleRecords := make([]nbdns.SimpleRecord, 0, len(zone.Records))
for _, record := range zone.Records {
var recordType int
rData := record.Content

switch record.Type {
case records.RecordTypeA:
recordType = int(dns.TypeA)
case records.RecordTypeAAAA:
recordType = int(dns.TypeAAAA)
case records.RecordTypeCNAME:
recordType = int(dns.TypeCNAME)
rData = dns.Fqdn(record.Content)
default:
log.WithContext(ctx).Warnf("unknown DNS record type %s for record %s", record.Type, record.ID)
continue
}

simpleRecords = append(simpleRecords, nbdns.SimpleRecord{
Name: dns.Fqdn(record.Name),
Type: recordType,
Class: nbdns.DefaultClass,
TTL: record.TTL,
RData: rData,
})
}

customZones = append(customZones, nbdns.CustomZone{
Domain: dns.Fqdn(zone.Domain),
Records: simpleRecords,
SearchDomainDisabled: !zone.EnableSearchDomain,
})
}

return customZones
}
Loading