mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-20 12:29:09 +02:00
[management] Add custom dns zones (#4849)
This commit is contained in:
@@ -295,7 +295,7 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
|
||||
return err
|
||||
}
|
||||
|
||||
if err = am.validateSettingsUpdate(ctx, newSettings, oldSettings, userID, accountID); err != nil {
|
||||
if err = am.validateSettingsUpdate(ctx, transaction, newSettings, oldSettings, userID, accountID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -388,7 +388,7 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
|
||||
return newSettings, nil
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) validateSettingsUpdate(ctx context.Context, newSettings, oldSettings *types.Settings, userID, accountID string) error {
|
||||
func (am *DefaultAccountManager) validateSettingsUpdate(ctx context.Context, transaction store.Store, newSettings, oldSettings *types.Settings, userID, accountID string) error {
|
||||
halfYearLimit := 180 * 24 * time.Hour
|
||||
if newSettings.PeerLoginExpiration > halfYearLimit {
|
||||
return status.Errorf(status.InvalidArgument, "peer login expiration can't be larger than 180 days")
|
||||
@@ -402,6 +402,18 @@ func (am *DefaultAccountManager) validateSettingsUpdate(ctx context.Context, new
|
||||
return status.Errorf(status.InvalidArgument, "invalid domain \"%s\" provided for DNS domain", newSettings.DNSDomain)
|
||||
}
|
||||
|
||||
if newSettings.DNSDomain != oldSettings.DNSDomain && newSettings.DNSDomain != "" {
|
||||
existingZone, err := transaction.GetZoneByDomain(ctx, accountID, newSettings.DNSDomain)
|
||||
if err != nil {
|
||||
if sErr, ok := status.FromError(err); !ok || sErr.Type() != status.NotFound {
|
||||
return fmt.Errorf("failed to check existing zone: %w", err)
|
||||
}
|
||||
}
|
||||
if existingZone != nil {
|
||||
return status.Errorf(status.InvalidArgument, "peer DNS domain %s conflicts with existing custom DNS zone", newSettings.DNSDomain)
|
||||
}
|
||||
}
|
||||
|
||||
return am.integratedPeerValidator.ValidateExtraSettings(ctx, newSettings.Extra, oldSettings.Extra, userID, accountID)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/netbirdio/netbird/management/internals/controllers/network_map/update_channel"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/peers"
|
||||
ephemeral_manager "github.com/netbirdio/netbird/management/internals/modules/peers/ephemeral/manager"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
||||
"github.com/netbirdio/netbird/management/internals/server/config"
|
||||
nbAccount "github.com/netbirdio/netbird/management/server/account"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
@@ -397,7 +398,7 @@ func TestAccount_GetPeerNetworkMap(t *testing.T) {
|
||||
}
|
||||
|
||||
customZone := account.GetPeersCustomZone(context.Background(), "netbird.io")
|
||||
networkMap := account.GetPeerNetworkMap(context.Background(), testCase.peerID, customZone, validatedPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil, account.GetActiveGroupUsers())
|
||||
networkMap := account.GetPeerNetworkMap(context.Background(), testCase.peerID, customZone, nil, validatedPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil, account.GetActiveGroupUsers())
|
||||
assert.Len(t, networkMap.Peers, len(testCase.expectedPeers))
|
||||
assert.Len(t, networkMap.OfflinePeers, len(testCase.expectedOfflinePeers))
|
||||
}
|
||||
@@ -1676,7 +1677,7 @@ func TestAccount_GetRoutesToSync(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
routes := account.GetRoutesToSync(context.Background(), "peer-2", []*nbpeer.Peer{{Key: "peer-1"}, {Key: "peer-3"}})
|
||||
routes := account.GetRoutesToSync(context.Background(), "peer-2", []*nbpeer.Peer{{Key: "peer-1"}, {Key: "peer-3"}}, account.GetPeerGroups("peer-2"))
|
||||
|
||||
assert.Len(t, routes, 2)
|
||||
routeIDs := make(map[route.ID]struct{}, 2)
|
||||
@@ -1686,7 +1687,7 @@ func TestAccount_GetRoutesToSync(t *testing.T) {
|
||||
assert.Contains(t, routeIDs, route.ID("route-2"))
|
||||
assert.Contains(t, routeIDs, route.ID("route-3"))
|
||||
|
||||
emptyRoutes := account.GetRoutesToSync(context.Background(), "peer-3", []*nbpeer.Peer{{Key: "peer-1"}, {Key: "peer-2"}})
|
||||
emptyRoutes := account.GetRoutesToSync(context.Background(), "peer-3", []*nbpeer.Peer{{Key: "peer-1"}, {Key: "peer-2"}}, account.GetPeerGroups("peer-3"))
|
||||
|
||||
assert.Len(t, emptyRoutes, 0)
|
||||
}
|
||||
@@ -2095,6 +2096,35 @@ func TestDefaultAccountManager_UpdateAccountSettings_PeerApproval(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultAccountManager_UpdateAccountSettings_DNSDomainConflict(t *testing.T) {
|
||||
manager, _, err := createManager(t)
|
||||
require.NoError(t, err, "unable to create account manager")
|
||||
|
||||
accountID, err := manager.GetAccountIDByUserID(context.Background(), auth.UserAuth{UserId: userID})
|
||||
require.NoError(t, err, "unable to create an account")
|
||||
|
||||
ctx := context.Background()
|
||||
err = manager.Store.CreateZone(ctx, &zones.Zone{
|
||||
ID: "test-zone-id",
|
||||
AccountID: accountID,
|
||||
Name: "Test Zone",
|
||||
Domain: "custom.example.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{},
|
||||
})
|
||||
require.NoError(t, err, "unable to create custom DNS zone")
|
||||
|
||||
_, err = manager.UpdateAccountSettings(ctx, accountID, userID, &types.Settings{
|
||||
DNSDomain: "custom.example.com",
|
||||
PeerLoginExpiration: time.Hour,
|
||||
PeerLoginExpirationEnabled: false,
|
||||
Extra: &types.ExtraSettings{},
|
||||
})
|
||||
require.Error(t, err, "expecting to fail when DNS domain conflicts with custom zone")
|
||||
assert.Contains(t, err.Error(), "conflicts with existing custom DNS zone")
|
||||
}
|
||||
|
||||
func TestAccount_GetExpiredPeers(t *testing.T) {
|
||||
type test struct {
|
||||
name string
|
||||
|
||||
@@ -187,6 +187,14 @@ const (
|
||||
IdentityProviderUpdated Activity = 94
|
||||
IdentityProviderDeleted Activity = 95
|
||||
|
||||
DNSZoneCreated Activity = 96
|
||||
DNSZoneUpdated Activity = 97
|
||||
DNSZoneDeleted Activity = 98
|
||||
|
||||
DNSRecordCreated Activity = 99
|
||||
DNSRecordUpdated Activity = 100
|
||||
DNSRecordDeleted Activity = 101
|
||||
|
||||
AccountDeleted Activity = 99999
|
||||
)
|
||||
|
||||
@@ -303,6 +311,14 @@ var activityMap = map[Activity]Code{
|
||||
IdentityProviderCreated: {"Identity provider created", "identityprovider.create"},
|
||||
IdentityProviderUpdated: {"Identity provider updated", "identityprovider.update"},
|
||||
IdentityProviderDeleted: {"Identity provider deleted", "identityprovider.delete"},
|
||||
|
||||
DNSZoneCreated: {"DNS zone created", "dns.zone.create"},
|
||||
DNSZoneUpdated: {"DNS zone updated", "dns.zone.update"},
|
||||
DNSZoneDeleted: {"DNS zone deleted", "dns.zone.delete"},
|
||||
|
||||
DNSRecordCreated: {"DNS zone record created", "dns.zone.record.create"},
|
||||
DNSRecordUpdated: {"DNS zone record updated", "dns.zone.record.update"},
|
||||
DNSRecordDeleted: {"DNS zone record deleted", "dns.zone.record.delete"},
|
||||
}
|
||||
|
||||
// StringCode returns a string code of the activity
|
||||
|
||||
@@ -15,7 +15,10 @@ import (
|
||||
|
||||
"github.com/netbirdio/management-integrations/integrations"
|
||||
"github.com/netbirdio/netbird/management/internals/controllers/network_map"
|
||||
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
||||
zonesManager "github.com/netbirdio/netbird/management/internals/modules/zones/manager"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones/records"
|
||||
recordsManager "github.com/netbirdio/netbird/management/internals/modules/zones/records/manager"
|
||||
"github.com/netbirdio/netbird/management/server/account"
|
||||
"github.com/netbirdio/netbird/management/server/settings"
|
||||
|
||||
@@ -56,7 +59,7 @@ const (
|
||||
)
|
||||
|
||||
// NewAPIHandler creates the Management service HTTP API handler registering all the available endpoints.
|
||||
func NewAPIHandler(ctx context.Context, accountManager account.Manager, networksManager nbnetworks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager nbgroups.Manager, LocationManager geolocation.Geolocation, authManager auth.Manager, appMetrics telemetry.AppMetrics, integratedValidator integrated_validator.IntegratedValidator, proxyController port_forwarding.Controller, permissionsManager permissions.Manager, peersManager nbpeers.Manager, settingsManager settings.Manager, networkMapController network_map.Controller, idpManager idpmanager.Manager) (http.Handler, error) {
|
||||
func NewAPIHandler(ctx context.Context, accountManager account.Manager, networksManager nbnetworks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager nbgroups.Manager, LocationManager geolocation.Geolocation, authManager auth.Manager, appMetrics telemetry.AppMetrics, integratedValidator integrated_validator.IntegratedValidator, proxyController port_forwarding.Controller, permissionsManager permissions.Manager, peersManager nbpeers.Manager, settingsManager settings.Manager, zManager zones.Manager, rManager records.Manager, networkMapController network_map.Controller, idpManager idpmanager.Manager) (http.Handler, error) {
|
||||
|
||||
// Register bypass paths for unauthenticated endpoints
|
||||
if err := bypass.AddBypassPath("/api/instance"); err != nil {
|
||||
@@ -138,6 +141,8 @@ func NewAPIHandler(ctx context.Context, accountManager account.Manager, networks
|
||||
dns.AddEndpoints(accountManager, router)
|
||||
events.AddEndpoints(accountManager, router)
|
||||
networks.AddEndpoints(networksManager, resourceManager, routerManager, groupsManager, accountManager, router)
|
||||
zonesManager.RegisterEndpoints(router, zManager)
|
||||
recordsManager.RegisterEndpoints(router, rManager)
|
||||
idp.AddEndpoints(accountManager, router)
|
||||
instance.AddEndpoints(instanceManager, router)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/internals/controllers/network_map"
|
||||
"github.com/netbirdio/netbird/management/server/account"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
@@ -298,8 +299,7 @@ func (h *Handler) GetAccessiblePeers(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
dnsDomain := h.networkMapController.GetDNSDomain(account.Settings)
|
||||
|
||||
customZone := account.GetPeersCustomZone(r.Context(), dnsDomain)
|
||||
netMap := account.GetPeerNetworkMap(r.Context(), peerID, customZone, validPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil, account.GetActiveGroupUsers())
|
||||
netMap := account.GetPeerNetworkMap(r.Context(), peerID, dns.CustomZone{}, nil, validPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil, account.GetActiveGroupUsers())
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, toAccessiblePeers(netMap, dnsDomain))
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/netbirdio/management-integrations/integrations"
|
||||
zonesManager "github.com/netbirdio/netbird/management/internals/modules/zones/manager"
|
||||
recordsManager "github.com/netbirdio/netbird/management/internals/modules/zones/records/manager"
|
||||
"github.com/netbirdio/netbird/management/internals/server/config"
|
||||
|
||||
"github.com/netbirdio/netbird/management/internals/controllers/network_map"
|
||||
@@ -93,8 +95,10 @@ func BuildApiBlackBoxWithDBState(t testing_tools.TB, sqlFile string, expectedPee
|
||||
routersManagerMock := routers.NewManagerMock()
|
||||
groupsManagerMock := groups.NewManagerMock()
|
||||
peersManager := peers.NewManager(store, permissionsManager)
|
||||
customZonesManager := zonesManager.NewManager(store, am, permissionsManager, "")
|
||||
zoneRecordsManager := recordsManager.NewManager(store, am, permissionsManager)
|
||||
|
||||
apiHandler, err := http2.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManager, peersManager, settingsManager, networkMapController, nil)
|
||||
apiHandler, err := http2.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManager, peersManager, settingsManager, customZonesManager, zoneRecordsManager, networkMapController, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create API handler: %v", err)
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
"gorm.io/gorm/logger"
|
||||
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones/records"
|
||||
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
|
||||
@@ -123,6 +125,7 @@ func NewSqlStore(ctx context.Context, db *gorm.DB, storeEngine types.Engine, met
|
||||
&types.Account{}, &types.Policy{}, &types.PolicyRule{}, &route.Route{}, &nbdns.NameServerGroup{},
|
||||
&installation{}, &types.ExtraSettings{}, &posture.Checks{}, &nbpeer.NetworkAddress{},
|
||||
&networkTypes.Network{}, &routerTypes.NetworkRouter{}, &resourceTypes.NetworkResource{}, &types.AccountOnboarding{},
|
||||
&zones.Zone{}, &records.Record{},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("auto migratePreAuto: %w", err)
|
||||
@@ -4179,3 +4182,184 @@ func (s *SqlStore) GetUserIDByPeerKey(ctx context.Context, lockStrength LockingS
|
||||
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) CreateZone(ctx context.Context, zone *zones.Zone) error {
|
||||
result := s.db.Create(zone)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to create zone to store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to create zone to store")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) UpdateZone(ctx context.Context, zone *zones.Zone) error {
|
||||
result := s.db.Select("*").Save(zone)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to update zone to store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to update zone to store")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) DeleteZone(ctx context.Context, accountID, zoneID string) error {
|
||||
result := s.db.Delete(&zones.Zone{}, accountAndIDQueryCondition, accountID, zoneID)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to delete zone from store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to delete zone from store")
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return status.NewZoneNotFoundError(zoneID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) GetZoneByID(ctx context.Context, lockStrength LockingStrength, accountID, zoneID string) (*zones.Zone, error) {
|
||||
tx := s.db
|
||||
if lockStrength != LockingStrengthNone {
|
||||
tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
|
||||
}
|
||||
|
||||
var zone *zones.Zone
|
||||
result := tx.Preload("Records").Take(&zone, accountAndIDQueryCondition, accountID, zoneID)
|
||||
if result.Error != nil {
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, status.NewZoneNotFoundError(zoneID)
|
||||
}
|
||||
|
||||
log.WithContext(ctx).Errorf("failed to get zone from store: %v", result.Error)
|
||||
return nil, status.Errorf(status.Internal, "failed to get zone from store")
|
||||
}
|
||||
|
||||
return zone, nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) GetZoneByDomain(ctx context.Context, accountID, domain string) (*zones.Zone, error) {
|
||||
var zone *zones.Zone
|
||||
result := s.db.Where("account_id = ? AND domain = ?", accountID, domain).First(&zone)
|
||||
if result.Error != nil {
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, status.NewZoneNotFoundError(domain)
|
||||
}
|
||||
|
||||
log.WithContext(ctx).Errorf("failed to get zone by domain from store: %v", result.Error)
|
||||
return nil, status.Errorf(status.Internal, "failed to get zone by domain from store")
|
||||
}
|
||||
|
||||
return zone, nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) GetAccountZones(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*zones.Zone, error) {
|
||||
tx := s.db
|
||||
if lockStrength != LockingStrengthNone {
|
||||
tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
|
||||
}
|
||||
|
||||
var zones []*zones.Zone
|
||||
result := tx.Preload("Records").Find(&zones, accountIDCondition, accountID)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to get zones from the store: %s", result.Error)
|
||||
return nil, status.Errorf(status.Internal, "failed to get zones from store")
|
||||
}
|
||||
|
||||
return zones, nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) CreateDNSRecord(ctx context.Context, record *records.Record) error {
|
||||
result := s.db.Create(record)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to create dns record to store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to create dns record to store")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) UpdateDNSRecord(ctx context.Context, record *records.Record) error {
|
||||
result := s.db.Select("*").Save(record)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to update dns record to store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to update dns record to store")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) DeleteDNSRecord(ctx context.Context, accountID, zoneID, recordID string) error {
|
||||
result := s.db.Delete(&records.Record{}, "account_id = ? AND zone_id = ? AND id = ?", accountID, zoneID, recordID)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to delete dns record from store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to delete dns record from store")
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return status.NewDNSRecordNotFoundError(recordID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) GetDNSRecordByID(ctx context.Context, lockStrength LockingStrength, accountID, zoneID, recordID string) (*records.Record, error) {
|
||||
tx := s.db
|
||||
if lockStrength != LockingStrengthNone {
|
||||
tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
|
||||
}
|
||||
|
||||
var record *records.Record
|
||||
result := tx.Where("account_id = ? AND zone_id = ? AND id = ?", accountID, zoneID, recordID).Take(&record)
|
||||
if result.Error != nil {
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, status.NewDNSRecordNotFoundError(recordID)
|
||||
}
|
||||
|
||||
log.WithContext(ctx).Errorf("failed to get dns record from store: %v", result.Error)
|
||||
return nil, status.Errorf(status.Internal, "failed to get dns record from store")
|
||||
}
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) GetZoneDNSRecords(ctx context.Context, lockStrength LockingStrength, accountID, zoneID string) ([]*records.Record, error) {
|
||||
tx := s.db
|
||||
if lockStrength != LockingStrengthNone {
|
||||
tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
|
||||
}
|
||||
|
||||
var recordsList []*records.Record
|
||||
result := tx.Where("account_id = ? AND zone_id = ?", accountID, zoneID).Find(&recordsList)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to get zone dns records from the store: %s", result.Error)
|
||||
return nil, status.Errorf(status.Internal, "failed to get zone dns records from store")
|
||||
}
|
||||
|
||||
return recordsList, nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) GetZoneDNSRecordsByName(ctx context.Context, lockStrength LockingStrength, accountID, zoneID, name string) ([]*records.Record, error) {
|
||||
tx := s.db
|
||||
if lockStrength != LockingStrengthNone {
|
||||
tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
|
||||
}
|
||||
|
||||
var recordsList []*records.Record
|
||||
result := tx.Where("account_id = ? AND zone_id = ? AND name = ?", accountID, zoneID, name).Find(&recordsList)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to get zone dns records by name from the store: %s", result.Error)
|
||||
return nil, status.Errorf(status.Internal, "failed to get zone dns records by name from store")
|
||||
}
|
||||
|
||||
return recordsList, nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) DeleteZoneDNSRecords(ctx context.Context, accountID, zoneID string) error {
|
||||
result := s.db.Delete(&records.Record{}, "account_id = ? AND zone_id = ?", accountID, zoneID)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("failed to delete zone dns records from store: %v", result.Error)
|
||||
return status.Errorf(status.Internal, "failed to delete zone dns records from store")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones/records"
|
||||
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
|
||||
@@ -4025,3 +4027,476 @@ func TestSqlStore_ExecuteInTransaction_Timeout(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "transaction has already been committed or rolled back", "expected transaction rolled back error, got: %v", err)
|
||||
}
|
||||
|
||||
func TestSqlStore_CreateZone(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
|
||||
zone := zones.NewZone(accountID, "Test Zone", "example.com", true, false, []string{"group1"})
|
||||
|
||||
err = store.CreateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
savedZone, err := store.GetZoneByID(context.Background(), LockingStrengthNone, accountID, zone.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, savedZone)
|
||||
assert.Equal(t, zone.ID, savedZone.ID)
|
||||
assert.Equal(t, zone.Name, savedZone.Name)
|
||||
assert.Equal(t, zone.Domain, savedZone.Domain)
|
||||
assert.Equal(t, zone.Enabled, savedZone.Enabled)
|
||||
assert.Equal(t, zone.EnableSearchDomain, savedZone.EnableSearchDomain)
|
||||
assert.Equal(t, zone.DistributionGroups, savedZone.DistributionGroups)
|
||||
}
|
||||
|
||||
func TestSqlStore_GetZoneByID(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
|
||||
zone := zones.NewZone(accountID, "Test Zone", "example.com", true, false, []string{"group1"})
|
||||
err = store.CreateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
accountID string
|
||||
zoneID string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "retrieve existing zone",
|
||||
accountID: accountID,
|
||||
zoneID: zone.ID,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "retrieve non-existing zone",
|
||||
accountID: accountID,
|
||||
zoneID: "non-existing",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "retrieve with empty zone ID",
|
||||
accountID: accountID,
|
||||
zoneID: "",
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
savedZone, err := store.GetZoneByID(context.Background(), LockingStrengthNone, tt.accountID, tt.zoneID)
|
||||
if tt.expectError {
|
||||
require.Error(t, err)
|
||||
sErr, ok := status.FromError(err)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, sErr.Type(), status.NotFound)
|
||||
require.Nil(t, savedZone)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, savedZone)
|
||||
assert.Equal(t, tt.zoneID, savedZone.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSqlStore_GetAccountZones(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
|
||||
zone1 := zones.NewZone(accountID, "Zone 1", "example1.com", true, false, []string{"group1"})
|
||||
err = store.CreateZone(context.Background(), zone1)
|
||||
require.NoError(t, err)
|
||||
|
||||
zone2 := zones.NewZone(accountID, "Zone 2", "example2.com", true, true, []string{"group1", "group2"})
|
||||
err = store.CreateZone(context.Background(), zone2)
|
||||
require.NoError(t, err)
|
||||
|
||||
allZones, err := store.GetAccountZones(context.Background(), LockingStrengthNone, accountID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, allZones)
|
||||
assert.GreaterOrEqual(t, len(allZones), 2)
|
||||
|
||||
zoneIDs := make(map[string]bool)
|
||||
for _, z := range allZones {
|
||||
zoneIDs[z.ID] = true
|
||||
}
|
||||
assert.True(t, zoneIDs[zone1.ID])
|
||||
assert.True(t, zoneIDs[zone2.ID])
|
||||
}
|
||||
|
||||
func TestSqlStore_GetZoneByDomain(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
otherAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3c"
|
||||
|
||||
zone := zones.NewZone(accountID, "Test Zone", "example.com", true, false, []string{"group1"})
|
||||
err = store.CreateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
accountID string
|
||||
domain string
|
||||
expectError bool
|
||||
errorType status.Type
|
||||
}{
|
||||
{
|
||||
name: "retrieve existing zone by domain",
|
||||
accountID: accountID,
|
||||
domain: "example.com",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "retrieve non-existing zone domain",
|
||||
accountID: accountID,
|
||||
domain: "non-existing.com",
|
||||
expectError: true,
|
||||
errorType: status.NotFound,
|
||||
},
|
||||
{
|
||||
name: "retrieve with empty domain",
|
||||
accountID: accountID,
|
||||
domain: "",
|
||||
expectError: true,
|
||||
errorType: status.NotFound,
|
||||
},
|
||||
{
|
||||
name: "retrieve with different account ID",
|
||||
accountID: otherAccountID,
|
||||
domain: "example.com",
|
||||
expectError: true,
|
||||
errorType: status.NotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
savedZone, err := store.GetZoneByDomain(context.Background(), tt.accountID, tt.domain)
|
||||
if tt.expectError {
|
||||
require.Error(t, err)
|
||||
sErr, ok := status.FromError(err)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, tt.errorType, sErr.Type())
|
||||
require.Nil(t, savedZone)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, savedZone)
|
||||
assert.Equal(t, tt.domain, savedZone.Domain)
|
||||
assert.Equal(t, zone.ID, savedZone.ID)
|
||||
assert.Equal(t, zone.Name, savedZone.Name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSqlStore_UpdateZone(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
|
||||
zone := zones.NewZone(accountID, "Test Zone", "example.com", true, false, []string{"group1"})
|
||||
err = store.CreateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
zone.Name = "Updated Zone"
|
||||
zone.Domain = "updated.com"
|
||||
zone.Enabled = false
|
||||
zone.EnableSearchDomain = true
|
||||
zone.DistributionGroups = []string{"group2", "group3"}
|
||||
|
||||
err = store.UpdateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
updatedZone, err := store.GetZoneByID(context.Background(), LockingStrengthNone, accountID, zone.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, updatedZone)
|
||||
assert.Equal(t, "Updated Zone", updatedZone.Name)
|
||||
assert.Equal(t, "updated.com", updatedZone.Domain)
|
||||
assert.False(t, updatedZone.Enabled)
|
||||
assert.True(t, updatedZone.EnableSearchDomain)
|
||||
assert.Equal(t, []string{"group2", "group3"}, updatedZone.DistributionGroups)
|
||||
}
|
||||
|
||||
func TestSqlStore_DeleteZone(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
|
||||
zone := zones.NewZone(accountID, "Test Zone", "example.com", true, false, []string{"group1"})
|
||||
err = store.CreateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = store.DeleteZone(context.Background(), accountID, zone.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
deletedZone, err := store.GetZoneByID(context.Background(), LockingStrengthNone, accountID, zone.ID)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, deletedZone)
|
||||
sErr, ok := status.FromError(err)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, sErr.Type(), status.NotFound)
|
||||
}
|
||||
|
||||
func TestSqlStore_CreateDNSRecord(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
|
||||
zone := zones.NewZone(accountID, "Test Zone", "example.com", true, false, []string{"group1"})
|
||||
err = store.CreateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
record := records.NewRecord(accountID, zone.ID, "www.example.com", records.RecordTypeA, "192.168.1.1", 300)
|
||||
|
||||
err = store.CreateDNSRecord(context.Background(), record)
|
||||
require.NoError(t, err)
|
||||
|
||||
savedRecord, err := store.GetDNSRecordByID(context.Background(), LockingStrengthNone, accountID, zone.ID, record.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, savedRecord)
|
||||
assert.Equal(t, record.ID, savedRecord.ID)
|
||||
assert.Equal(t, record.Name, savedRecord.Name)
|
||||
assert.Equal(t, record.Type, savedRecord.Type)
|
||||
assert.Equal(t, record.Content, savedRecord.Content)
|
||||
assert.Equal(t, record.TTL, savedRecord.TTL)
|
||||
assert.Equal(t, zone.ID, savedRecord.ZoneID)
|
||||
}
|
||||
|
||||
func TestSqlStore_GetDNSRecordByID(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
|
||||
zone := zones.NewZone(accountID, "Test Zone", "example.com", true, false, []string{"group1"})
|
||||
err = store.CreateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
record := records.NewRecord(accountID, zone.ID, "www.example.com", records.RecordTypeA, "192.168.1.1", 300)
|
||||
err = store.CreateDNSRecord(context.Background(), record)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
accountID string
|
||||
zoneID string
|
||||
recordID string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "retrieve existing record",
|
||||
accountID: accountID,
|
||||
zoneID: zone.ID,
|
||||
recordID: record.ID,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "retrieve non-existing record",
|
||||
accountID: accountID,
|
||||
zoneID: zone.ID,
|
||||
recordID: "non-existing",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "retrieve with empty record ID",
|
||||
accountID: accountID,
|
||||
zoneID: zone.ID,
|
||||
recordID: "",
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
savedRecord, err := store.GetDNSRecordByID(context.Background(), LockingStrengthNone, tt.accountID, tt.zoneID, tt.recordID)
|
||||
if tt.expectError {
|
||||
require.Error(t, err)
|
||||
sErr, ok := status.FromError(err)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, sErr.Type(), status.NotFound)
|
||||
require.Nil(t, savedRecord)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, savedRecord)
|
||||
assert.Equal(t, tt.recordID, savedRecord.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSqlStore_GetZoneDNSRecords(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
|
||||
zone := zones.NewZone(accountID, "Test Zone", "example.com", true, false, []string{"group1"})
|
||||
err = store.CreateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
recordA := records.NewRecord(accountID, zone.ID, "www.example.com", records.RecordTypeA, "192.168.1.1", 300)
|
||||
err = store.CreateDNSRecord(context.Background(), recordA)
|
||||
require.NoError(t, err)
|
||||
|
||||
recordAAAA := records.NewRecord(accountID, zone.ID, "ipv6.example.com", records.RecordTypeAAAA, "2001:db8::1", 300)
|
||||
err = store.CreateDNSRecord(context.Background(), recordAAAA)
|
||||
require.NoError(t, err)
|
||||
|
||||
recordCNAME := records.NewRecord(accountID, zone.ID, "alias.example.com", records.RecordTypeCNAME, "www.example.com", 300)
|
||||
err = store.CreateDNSRecord(context.Background(), recordCNAME)
|
||||
require.NoError(t, err)
|
||||
|
||||
allRecords, err := store.GetZoneDNSRecords(context.Background(), LockingStrengthNone, accountID, zone.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, allRecords)
|
||||
assert.Equal(t, 3, len(allRecords))
|
||||
|
||||
recordIDs := make(map[string]bool)
|
||||
for _, r := range allRecords {
|
||||
recordIDs[r.ID] = true
|
||||
}
|
||||
assert.True(t, recordIDs[recordA.ID])
|
||||
assert.True(t, recordIDs[recordAAAA.ID])
|
||||
assert.True(t, recordIDs[recordCNAME.ID])
|
||||
}
|
||||
|
||||
func TestSqlStore_GetZoneDNSRecordsByName(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
|
||||
zone := zones.NewZone(accountID, "Test Zone", "example.com", true, false, []string{"group1"})
|
||||
err = store.CreateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
record1 := records.NewRecord(accountID, zone.ID, "www.example.com", records.RecordTypeA, "192.168.1.1", 300)
|
||||
err = store.CreateDNSRecord(context.Background(), record1)
|
||||
require.NoError(t, err)
|
||||
|
||||
record2 := records.NewRecord(accountID, zone.ID, "www.example.com", records.RecordTypeAAAA, "2001:db8::1", 300)
|
||||
err = store.CreateDNSRecord(context.Background(), record2)
|
||||
require.NoError(t, err)
|
||||
|
||||
record3 := records.NewRecord(accountID, zone.ID, "mail.example.com", records.RecordTypeA, "192.168.1.2", 600)
|
||||
err = store.CreateDNSRecord(context.Background(), record3)
|
||||
require.NoError(t, err)
|
||||
|
||||
recordsByName, err := store.GetZoneDNSRecordsByName(context.Background(), LockingStrengthNone, accountID, zone.ID, "www.example.com")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, recordsByName)
|
||||
assert.Equal(t, 2, len(recordsByName))
|
||||
|
||||
for _, r := range recordsByName {
|
||||
assert.Equal(t, "www.example.com", r.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSqlStore_UpdateDNSRecord(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
|
||||
zone := zones.NewZone(accountID, "Test Zone", "example.com", true, false, []string{"group1"})
|
||||
err = store.CreateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
record := records.NewRecord(accountID, zone.ID, "www.example.com", records.RecordTypeA, "192.168.1.1", 300)
|
||||
err = store.CreateDNSRecord(context.Background(), record)
|
||||
require.NoError(t, err)
|
||||
|
||||
record.Name = "api.example.com"
|
||||
record.Content = "192.168.1.100"
|
||||
record.TTL = 600
|
||||
|
||||
err = store.UpdateDNSRecord(context.Background(), record)
|
||||
require.NoError(t, err)
|
||||
|
||||
updatedRecord, err := store.GetDNSRecordByID(context.Background(), LockingStrengthNone, accountID, zone.ID, record.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, updatedRecord)
|
||||
assert.Equal(t, "api.example.com", updatedRecord.Name)
|
||||
assert.Equal(t, "192.168.1.100", updatedRecord.Content)
|
||||
assert.Equal(t, 600, updatedRecord.TTL)
|
||||
}
|
||||
|
||||
func TestSqlStore_DeleteDNSRecord(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
|
||||
zone := zones.NewZone(accountID, "Test Zone", "example.com", true, false, []string{"group1"})
|
||||
err = store.CreateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
record := records.NewRecord(accountID, zone.ID, "www.example.com", records.RecordTypeA, "192.168.1.1", 300)
|
||||
err = store.CreateDNSRecord(context.Background(), record)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = store.DeleteDNSRecord(context.Background(), accountID, zone.ID, record.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
deletedRecord, err := store.GetDNSRecordByID(context.Background(), LockingStrengthNone, accountID, zone.ID, record.ID)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, deletedRecord)
|
||||
sErr, ok := status.FromError(err)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, sErr.Type(), status.NotFound)
|
||||
}
|
||||
|
||||
func TestSqlStore_DeleteZoneDNSRecords(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
|
||||
zone := zones.NewZone(accountID, "Test Zone", "example.com", true, false, []string{"group1"})
|
||||
err = store.CreateZone(context.Background(), zone)
|
||||
require.NoError(t, err)
|
||||
|
||||
record1 := records.NewRecord(accountID, zone.ID, "www.example.com", records.RecordTypeA, "192.168.1.1", 300)
|
||||
err = store.CreateDNSRecord(context.Background(), record1)
|
||||
require.NoError(t, err)
|
||||
|
||||
record2 := records.NewRecord(accountID, zone.ID, "mail.example.com", records.RecordTypeA, "192.168.1.2", 600)
|
||||
err = store.CreateDNSRecord(context.Background(), record2)
|
||||
require.NoError(t, err)
|
||||
|
||||
allRecords, err := store.GetZoneDNSRecords(context.Background(), LockingStrengthNone, accountID, zone.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, len(allRecords))
|
||||
|
||||
err = store.DeleteZoneDNSRecords(context.Background(), accountID, zone.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
remainingRecords, err := store.GetZoneDNSRecords(context.Background(), LockingStrengthNone, accountID, zone.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(remainingRecords))
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones/records"
|
||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||
"github.com/netbirdio/netbird/management/server/testutil"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
@@ -209,6 +211,21 @@ type Store interface {
|
||||
// SetFieldEncrypt sets the field encryptor for encrypting sensitive user data.
|
||||
SetFieldEncrypt(enc *crypt.FieldEncrypt)
|
||||
GetUserIDByPeerKey(ctx context.Context, lockStrength LockingStrength, peerKey string) (string, error)
|
||||
|
||||
CreateZone(ctx context.Context, zone *zones.Zone) error
|
||||
UpdateZone(ctx context.Context, zone *zones.Zone) error
|
||||
DeleteZone(ctx context.Context, accountID, zoneID string) error
|
||||
GetZoneByID(ctx context.Context, lockStrength LockingStrength, accountID, zoneID string) (*zones.Zone, error)
|
||||
GetZoneByDomain(ctx context.Context, accountID, domain string) (*zones.Zone, error)
|
||||
GetAccountZones(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*zones.Zone, error)
|
||||
|
||||
CreateDNSRecord(ctx context.Context, record *records.Record) error
|
||||
UpdateDNSRecord(ctx context.Context, record *records.Record) error
|
||||
DeleteDNSRecord(ctx context.Context, accountID, zoneID, recordID string) error
|
||||
GetDNSRecordByID(ctx context.Context, lockStrength LockingStrength, accountID, zoneID, recordID string) (*records.Record, error)
|
||||
GetZoneDNSRecords(ctx context.Context, lockStrength LockingStrength, accountID, zoneID string) ([]*records.Record, error)
|
||||
GetZoneDNSRecordsByName(ctx context.Context, lockStrength LockingStrength, accountID, zoneID, name string) ([]*records.Record, error)
|
||||
DeleteZoneDNSRecords(ctx context.Context, accountID, zoneID string) error
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
|
||||
"github.com/netbirdio/netbird/client/ssh/auth"
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones/records"
|
||||
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
|
||||
@@ -150,17 +152,16 @@ func (o AccountOnboarding) IsEqual(onboarding AccountOnboarding) bool {
|
||||
// GetRoutesToSync returns the enabled routes for the peer ID and the routes
|
||||
// from the ACL peers that have distribution groups associated with the peer ID.
|
||||
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
|
||||
func (a *Account) GetRoutesToSync(ctx context.Context, peerID string, aclPeers []*nbpeer.Peer) []*route.Route {
|
||||
func (a *Account) GetRoutesToSync(ctx context.Context, peerID string, aclPeers []*nbpeer.Peer, peerGroups LookupMap) []*route.Route {
|
||||
routes, peerDisabledRoutes := a.getRoutingPeerRoutes(ctx, peerID)
|
||||
peerRoutesMembership := make(LookupMap)
|
||||
for _, r := range append(routes, peerDisabledRoutes...) {
|
||||
peerRoutesMembership[string(r.GetHAUniqueID())] = struct{}{}
|
||||
}
|
||||
|
||||
groupListMap := a.GetPeerGroups(peerID)
|
||||
for _, peer := range aclPeers {
|
||||
activeRoutes, _ := a.getRoutingPeerRoutes(ctx, peer.ID)
|
||||
groupFilteredRoutes := a.filterRoutesByGroups(activeRoutes, groupListMap)
|
||||
groupFilteredRoutes := a.filterRoutesByGroups(activeRoutes, peerGroups)
|
||||
filteredRoutes := a.filterRoutesFromPeersOfSameHAGroup(groupFilteredRoutes, peerRoutesMembership)
|
||||
routes = append(routes, filteredRoutes...)
|
||||
}
|
||||
@@ -274,6 +275,7 @@ func (a *Account) GetPeerNetworkMap(
|
||||
ctx context.Context,
|
||||
peerID string,
|
||||
peersCustomZone nbdns.CustomZone,
|
||||
accountZones []*zones.Zone,
|
||||
validatedPeersMap map[string]struct{},
|
||||
resourcePolicies map[string][]*Policy,
|
||||
routers map[string]map[string]*routerTypes.NetworkRouter,
|
||||
@@ -294,6 +296,8 @@ func (a *Account) GetPeerNetworkMap(
|
||||
}
|
||||
}
|
||||
|
||||
peerGroups := a.GetPeerGroups(peerID)
|
||||
|
||||
aclPeers, firewallRules, authorizedUsers, enableSSH := a.GetPeerConnectionResources(ctx, peer, validatedPeersMap, groupIDToUserIDs)
|
||||
// exclude expired peers
|
||||
var peersToConnect []*nbpeer.Peer
|
||||
@@ -307,7 +311,7 @@ func (a *Account) GetPeerNetworkMap(
|
||||
peersToConnect = append(peersToConnect, p)
|
||||
}
|
||||
|
||||
routesUpdate := a.GetRoutesToSync(ctx, peerID, peersToConnect)
|
||||
routesUpdate := a.GetRoutesToSync(ctx, peerID, peersToConnect, peerGroups)
|
||||
routesFirewallRules := a.GetPeerRoutesFirewallRules(ctx, peerID, validatedPeersMap)
|
||||
isRouter, networkResourcesRoutes, sourcePeers := a.GetNetworkResourcesRoutesToSync(ctx, peerID, resourcePolicies, routers)
|
||||
var networkResourcesFirewallRules []*RouteFirewallRule
|
||||
@@ -323,6 +327,7 @@ func (a *Account) GetPeerNetworkMap(
|
||||
|
||||
if dnsManagementStatus {
|
||||
var zones []nbdns.CustomZone
|
||||
|
||||
if peersCustomZone.Domain != "" {
|
||||
records := filterZoneRecordsForPeers(peer, peersCustomZone, peersToConnectIncludingRouters, expiredPeers)
|
||||
zones = append(zones, nbdns.CustomZone{
|
||||
@@ -330,6 +335,10 @@ func (a *Account) GetPeerNetworkMap(
|
||||
Records: records,
|
||||
})
|
||||
}
|
||||
|
||||
filteredAccountZones := filterPeerAppliedZones(ctx, accountZones, peerGroups)
|
||||
zones = append(zones, filteredAccountZones...)
|
||||
|
||||
dnsUpdate.CustomZones = zones
|
||||
dnsUpdate.NameServerGroups = getPeerNSGroups(a, peerID)
|
||||
}
|
||||
@@ -1881,3 +1890,66 @@ func filterZoneRecordsForPeers(peer *nbpeer.Peer, customZone nbdns.CustomZone, p
|
||||
|
||||
return filteredRecords
|
||||
}
|
||||
|
||||
// filterPeerAppliedZones filters account zones based on the peer's group membership
|
||||
func filterPeerAppliedZones(ctx context.Context, accountZones []*zones.Zone, peerGroups 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,
|
||||
NonAuthoritative: true,
|
||||
})
|
||||
}
|
||||
|
||||
return customZones
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones/records"
|
||||
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
|
||||
@@ -1425,3 +1427,515 @@ func Test_FilterZoneRecordsForPeers(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_filterPeerAppliedZones(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
accountZones []*zones.Zone
|
||||
peerGroups LookupMap
|
||||
expected []nbdns.CustomZone
|
||||
}{
|
||||
{
|
||||
name: "empty peer groups returns empty custom zones",
|
||||
accountZones: []*zones.Zone{},
|
||||
peerGroups: LookupMap{},
|
||||
expected: []nbdns.CustomZone{},
|
||||
},
|
||||
{
|
||||
name: "peer has access to zone with A record",
|
||||
accountZones: []*zones.Zone{
|
||||
{
|
||||
ID: "zone1",
|
||||
Domain: "example.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{"group1"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record1",
|
||||
Name: "www.example.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.1",
|
||||
TTL: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
peerGroups: LookupMap{"group1": struct{}{}},
|
||||
expected: []nbdns.CustomZone{
|
||||
{
|
||||
Domain: "example.com.",
|
||||
Records: []nbdns.SimpleRecord{
|
||||
{
|
||||
Name: "www.example.com.",
|
||||
Type: int(dns.TypeA),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 300,
|
||||
RData: "192.168.1.1",
|
||||
},
|
||||
},
|
||||
SearchDomainDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "peer has access to zone with search domain enabled",
|
||||
accountZones: []*zones.Zone{
|
||||
{
|
||||
ID: "zone1",
|
||||
Domain: "internal.local",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: true,
|
||||
DistributionGroups: []string{"group1"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record1",
|
||||
Name: "api.internal.local",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "10.0.0.1",
|
||||
TTL: 600,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
peerGroups: LookupMap{"group1": struct{}{}},
|
||||
expected: []nbdns.CustomZone{
|
||||
{
|
||||
Domain: "internal.local.",
|
||||
Records: []nbdns.SimpleRecord{
|
||||
{
|
||||
Name: "api.internal.local.",
|
||||
Type: int(dns.TypeA),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 600,
|
||||
RData: "10.0.0.1",
|
||||
},
|
||||
},
|
||||
SearchDomainDisabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "peer has no access to zone",
|
||||
accountZones: []*zones.Zone{
|
||||
{
|
||||
ID: "zone1",
|
||||
Domain: "private.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{"group2"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record1",
|
||||
Name: "secret.private.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.1",
|
||||
TTL: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
peerGroups: LookupMap{"group1": struct{}{}},
|
||||
expected: []nbdns.CustomZone{},
|
||||
},
|
||||
{
|
||||
name: "disabled zone is filtered out",
|
||||
accountZones: []*zones.Zone{
|
||||
{
|
||||
ID: "zone1",
|
||||
Domain: "disabled.com",
|
||||
Enabled: false,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{"group1"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record1",
|
||||
Name: "www.disabled.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.1",
|
||||
TTL: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
peerGroups: LookupMap{"group1": struct{}{}},
|
||||
expected: []nbdns.CustomZone{},
|
||||
},
|
||||
{
|
||||
name: "zone with no records is filtered out",
|
||||
accountZones: []*zones.Zone{
|
||||
{
|
||||
ID: "zone1",
|
||||
Domain: "empty.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{"group1"},
|
||||
Records: []*records.Record{},
|
||||
},
|
||||
},
|
||||
peerGroups: LookupMap{"group1": struct{}{}},
|
||||
expected: []nbdns.CustomZone{},
|
||||
},
|
||||
{
|
||||
name: "peer has access via multiple groups",
|
||||
accountZones: []*zones.Zone{
|
||||
{
|
||||
ID: "zone1",
|
||||
Domain: "multi.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{"group1", "group2", "group3"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record1",
|
||||
Name: "www.multi.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.1",
|
||||
TTL: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
peerGroups: LookupMap{"group2": struct{}{}},
|
||||
expected: []nbdns.CustomZone{
|
||||
{
|
||||
Domain: "multi.com.",
|
||||
Records: []nbdns.SimpleRecord{
|
||||
{
|
||||
Name: "www.multi.com.",
|
||||
Type: int(dns.TypeA),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 300,
|
||||
RData: "192.168.1.1",
|
||||
},
|
||||
},
|
||||
SearchDomainDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple zones with mixed access",
|
||||
accountZones: []*zones.Zone{
|
||||
{
|
||||
ID: "zone1",
|
||||
Domain: "allowed.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{"group1"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record1",
|
||||
Name: "www.allowed.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.1",
|
||||
TTL: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "zone2",
|
||||
Domain: "denied.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{"group2"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record2",
|
||||
Name: "www.denied.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.2",
|
||||
TTL: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
peerGroups: LookupMap{"group1": struct{}{}},
|
||||
expected: []nbdns.CustomZone{
|
||||
{
|
||||
Domain: "allowed.com.",
|
||||
Records: []nbdns.SimpleRecord{
|
||||
{
|
||||
Name: "www.allowed.com.",
|
||||
Type: int(dns.TypeA),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 300,
|
||||
RData: "192.168.1.1",
|
||||
},
|
||||
},
|
||||
SearchDomainDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "zone with multiple record types",
|
||||
accountZones: []*zones.Zone{
|
||||
{
|
||||
ID: "zone1",
|
||||
Domain: "mixed.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{"group1"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record1",
|
||||
Name: "www.mixed.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.1",
|
||||
TTL: 300,
|
||||
},
|
||||
{
|
||||
ID: "record2",
|
||||
Name: "ipv6.mixed.com",
|
||||
Type: records.RecordTypeAAAA,
|
||||
Content: "2001:db8::1",
|
||||
TTL: 600,
|
||||
},
|
||||
{
|
||||
ID: "record3",
|
||||
Name: "alias.mixed.com",
|
||||
Type: records.RecordTypeCNAME,
|
||||
Content: "www.mixed.com",
|
||||
TTL: 900,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
peerGroups: LookupMap{"group1": struct{}{}},
|
||||
expected: []nbdns.CustomZone{
|
||||
{
|
||||
Domain: "mixed.com.",
|
||||
Records: []nbdns.SimpleRecord{
|
||||
{
|
||||
Name: "www.mixed.com.",
|
||||
Type: int(dns.TypeA),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 300,
|
||||
RData: "192.168.1.1",
|
||||
},
|
||||
{
|
||||
Name: "ipv6.mixed.com.",
|
||||
Type: int(dns.TypeAAAA),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 600,
|
||||
RData: "2001:db8::1",
|
||||
},
|
||||
{
|
||||
Name: "alias.mixed.com.",
|
||||
Type: int(dns.TypeCNAME),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 900,
|
||||
RData: "www.mixed.com.",
|
||||
},
|
||||
},
|
||||
SearchDomainDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple zones both accessible",
|
||||
accountZones: []*zones.Zone{
|
||||
{
|
||||
ID: "zone1",
|
||||
Domain: "first.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: true,
|
||||
DistributionGroups: []string{"group1"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record1",
|
||||
Name: "www.first.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.1",
|
||||
TTL: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "zone2",
|
||||
Domain: "second.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{"group1"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record2",
|
||||
Name: "www.second.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.2",
|
||||
TTL: 600,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
peerGroups: LookupMap{"group1": struct{}{}},
|
||||
expected: []nbdns.CustomZone{
|
||||
{
|
||||
Domain: "first.com.",
|
||||
Records: []nbdns.SimpleRecord{
|
||||
{
|
||||
Name: "www.first.com.",
|
||||
Type: int(dns.TypeA),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 300,
|
||||
RData: "192.168.1.1",
|
||||
},
|
||||
},
|
||||
SearchDomainDisabled: false,
|
||||
},
|
||||
{
|
||||
Domain: "second.com.",
|
||||
Records: []nbdns.SimpleRecord{
|
||||
{
|
||||
Name: "www.second.com.",
|
||||
Type: int(dns.TypeA),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 600,
|
||||
RData: "192.168.1.2",
|
||||
},
|
||||
},
|
||||
SearchDomainDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "zone with multiple records of same type",
|
||||
accountZones: []*zones.Zone{
|
||||
{
|
||||
ID: "zone1",
|
||||
Domain: "multi-a.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{"group1"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record1",
|
||||
Name: "www.multi-a.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.1",
|
||||
TTL: 300,
|
||||
},
|
||||
{
|
||||
ID: "record2",
|
||||
Name: "www.multi-a.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.2",
|
||||
TTL: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
peerGroups: LookupMap{"group1": struct{}{}},
|
||||
expected: []nbdns.CustomZone{
|
||||
{
|
||||
Domain: "multi-a.com.",
|
||||
Records: []nbdns.SimpleRecord{
|
||||
{
|
||||
Name: "www.multi-a.com.",
|
||||
Type: int(dns.TypeA),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 300,
|
||||
RData: "192.168.1.1",
|
||||
},
|
||||
{
|
||||
Name: "www.multi-a.com.",
|
||||
Type: int(dns.TypeA),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 300,
|
||||
RData: "192.168.1.2",
|
||||
},
|
||||
},
|
||||
SearchDomainDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "peer in multiple groups accessing different zones",
|
||||
accountZones: []*zones.Zone{
|
||||
{
|
||||
ID: "zone1",
|
||||
Domain: "zone1.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{"group1"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record1",
|
||||
Name: "www.zone1.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.1",
|
||||
TTL: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "zone2",
|
||||
Domain: "zone2.com",
|
||||
Enabled: true,
|
||||
EnableSearchDomain: false,
|
||||
DistributionGroups: []string{"group2"},
|
||||
Records: []*records.Record{
|
||||
{
|
||||
ID: "record2",
|
||||
Name: "www.zone2.com",
|
||||
Type: records.RecordTypeA,
|
||||
Content: "192.168.1.2",
|
||||
TTL: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
peerGroups: LookupMap{"group1": struct{}{}, "group2": struct{}{}},
|
||||
expected: []nbdns.CustomZone{
|
||||
{
|
||||
Domain: "zone1.com.",
|
||||
Records: []nbdns.SimpleRecord{
|
||||
{
|
||||
Name: "www.zone1.com.",
|
||||
Type: int(dns.TypeA),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 300,
|
||||
RData: "192.168.1.1",
|
||||
},
|
||||
},
|
||||
SearchDomainDisabled: true,
|
||||
},
|
||||
{
|
||||
Domain: "zone2.com.",
|
||||
Records: []nbdns.SimpleRecord{
|
||||
{
|
||||
Name: "www.zone2.com.",
|
||||
Type: int(dns.TypeA),
|
||||
Class: nbdns.DefaultClass,
|
||||
TTL: 300,
|
||||
RData: "192.168.1.2",
|
||||
},
|
||||
},
|
||||
SearchDomainDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := filterPeerAppliedZones(ctx, tt.accountZones, tt.peerGroups)
|
||||
require.Equal(t, len(tt.expected), len(result), "number of custom zones should match")
|
||||
|
||||
for i, expectedZone := range tt.expected {
|
||||
assert.Equal(t, expectedZone.Domain, result[i].Domain, "domain should match")
|
||||
assert.Equal(t, expectedZone.SearchDomainDisabled, result[i].SearchDomainDisabled, "search domain disabled flag should match")
|
||||
assert.Equal(t, len(expectedZone.Records), len(result[i].Records), "number of records should match")
|
||||
|
||||
for j, expectedRecord := range expectedZone.Records {
|
||||
assert.Equal(t, expectedRecord.Name, result[i].Records[j].Name, "record name should match")
|
||||
assert.Equal(t, expectedRecord.Type, result[i].Records[j].Type, "record type should match")
|
||||
assert.Equal(t, expectedRecord.Class, result[i].Records[j].Class, "record class should match")
|
||||
assert.Equal(t, expectedRecord.TTL, result[i].Records[j].TTL, "record TTL should match")
|
||||
assert.Equal(t, expectedRecord.RData, result[i].Records[j].RData, "record RData should match")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||
)
|
||||
@@ -25,11 +26,12 @@ func (a *Account) GetPeerNetworkMapExp(
|
||||
ctx context.Context,
|
||||
peerID string,
|
||||
peersCustomZone nbdns.CustomZone,
|
||||
accountZones []*zones.Zone,
|
||||
validatedPeers map[string]struct{},
|
||||
metrics *telemetry.AccountManagerMetrics,
|
||||
) *NetworkMap {
|
||||
a.initNetworkMapBuilder(validatedPeers)
|
||||
return a.NetworkMapCache.GetPeerNetworkMap(ctx, peerID, peersCustomZone, validatedPeers, metrics)
|
||||
return a.NetworkMapCache.GetPeerNetworkMap(ctx, peerID, peersCustomZone, accountZones, validatedPeers, metrics)
|
||||
}
|
||||
|
||||
func (a *Account) OnPeerAddedUpdNetworkMapCache(peerId string) error {
|
||||
|
||||
@@ -70,13 +70,13 @@ func TestGetPeerNetworkMap_Golden(t *testing.T) {
|
||||
resourcePolicies := account.GetResourcePoliciesMap()
|
||||
routers := account.GetResourceRoutersMap()
|
||||
|
||||
legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
|
||||
legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
|
||||
normalizeAndSortNetworkMap(legacyNetworkMap)
|
||||
legacyJSON, err := json.MarshalIndent(toNetworkMapJSON(legacyNetworkMap), "", " ")
|
||||
require.NoError(t, err, "error marshaling legacy network map to JSON")
|
||||
|
||||
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
|
||||
newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
|
||||
newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, nil)
|
||||
normalizeAndSortNetworkMap(newNetworkMap)
|
||||
newJSON, err := json.MarshalIndent(toNetworkMapJSON(newNetworkMap), "", " ")
|
||||
require.NoError(t, err, "error marshaling new network map to JSON")
|
||||
@@ -115,7 +115,7 @@ func BenchmarkGetPeerNetworkMap(b *testing.B) {
|
||||
b.Run("old builder", func(b *testing.B) {
|
||||
for range b.N {
|
||||
for _, peerID := range peerIDs {
|
||||
_ = account.GetPeerNetworkMap(ctx, peerID, dns.CustomZone{}, validatedPeersMap, nil, nil, nil, account.GetActiveGroupUsers())
|
||||
_ = account.GetPeerNetworkMap(ctx, peerID, dns.CustomZone{}, nil, validatedPeersMap, nil, nil, nil, account.GetActiveGroupUsers())
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -124,7 +124,7 @@ func BenchmarkGetPeerNetworkMap(b *testing.B) {
|
||||
for range b.N {
|
||||
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
|
||||
for _, peerID := range peerIDs {
|
||||
_ = builder.GetPeerNetworkMap(ctx, peerID, dns.CustomZone{}, validatedPeersMap, nil)
|
||||
_ = builder.GetPeerNetworkMap(ctx, peerID, dns.CustomZone{}, nil, validatedPeersMap, nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -177,7 +177,7 @@ func TestGetPeerNetworkMap_Golden_WithNewPeer(t *testing.T) {
|
||||
resourcePolicies := account.GetResourcePoliciesMap()
|
||||
routers := account.GetResourceRoutersMap()
|
||||
|
||||
legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
|
||||
legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
|
||||
normalizeAndSortNetworkMap(legacyNetworkMap)
|
||||
legacyJSON, err := json.MarshalIndent(toNetworkMapJSON(legacyNetworkMap), "", " ")
|
||||
require.NoError(t, err, "error marshaling legacy network map to JSON")
|
||||
@@ -185,7 +185,7 @@ func TestGetPeerNetworkMap_Golden_WithNewPeer(t *testing.T) {
|
||||
err = builder.OnPeerAddedIncremental(account, newPeerID)
|
||||
require.NoError(t, err, "error adding peer to cache")
|
||||
|
||||
newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
|
||||
newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, nil)
|
||||
normalizeAndSortNetworkMap(newNetworkMap)
|
||||
newJSON, err := json.MarshalIndent(toNetworkMapJSON(newNetworkMap), "", " ")
|
||||
require.NoError(t, err, "error marshaling new network map to JSON")
|
||||
@@ -240,7 +240,7 @@ func BenchmarkGetPeerNetworkMap_AfterPeerAdded(b *testing.B) {
|
||||
b.Run("old builder after add", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, testingPeerID := range peerIDs {
|
||||
_ = account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil, nil, nil, account.GetActiveGroupUsers())
|
||||
_ = account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, nil, nil, nil, account.GetActiveGroupUsers())
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -250,7 +250,7 @@ func BenchmarkGetPeerNetworkMap_AfterPeerAdded(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = builder.OnPeerAddedIncremental(account, newPeerID)
|
||||
for _, testingPeerID := range peerIDs {
|
||||
_ = builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
|
||||
_ = builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -317,7 +317,7 @@ func TestGetPeerNetworkMap_Golden_WithNewRoutingPeer(t *testing.T) {
|
||||
resourcePolicies := account.GetResourcePoliciesMap()
|
||||
routers := account.GetResourceRoutersMap()
|
||||
|
||||
legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
|
||||
legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
|
||||
normalizeAndSortNetworkMap(legacyNetworkMap)
|
||||
legacyJSON, err := json.MarshalIndent(toNetworkMapJSON(legacyNetworkMap), "", " ")
|
||||
require.NoError(t, err, "error marshaling legacy network map to JSON")
|
||||
@@ -325,7 +325,7 @@ func TestGetPeerNetworkMap_Golden_WithNewRoutingPeer(t *testing.T) {
|
||||
err = builder.OnPeerAddedIncremental(account, newRouterID)
|
||||
require.NoError(t, err, "error adding router to cache")
|
||||
|
||||
newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
|
||||
newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, nil)
|
||||
normalizeAndSortNetworkMap(newNetworkMap)
|
||||
newJSON, err := json.MarshalIndent(toNetworkMapJSON(newNetworkMap), "", " ")
|
||||
require.NoError(t, err, "error marshaling new network map to JSON")
|
||||
@@ -402,7 +402,7 @@ func BenchmarkGetPeerNetworkMap_AfterRouterPeerAdded(b *testing.B) {
|
||||
b.Run("old builder after add", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, testingPeerID := range peerIDs {
|
||||
_ = account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil, nil, nil, account.GetActiveGroupUsers())
|
||||
_ = account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, nil, nil, nil, account.GetActiveGroupUsers())
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -412,7 +412,7 @@ func BenchmarkGetPeerNetworkMap_AfterRouterPeerAdded(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = builder.OnPeerAddedIncremental(account, newRouterID)
|
||||
for _, testingPeerID := range peerIDs {
|
||||
_ = builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
|
||||
_ = builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -458,7 +458,7 @@ func TestGetPeerNetworkMap_Golden_WithDeletedPeer(t *testing.T) {
|
||||
resourcePolicies := account.GetResourcePoliciesMap()
|
||||
routers := account.GetResourceRoutersMap()
|
||||
|
||||
legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
|
||||
legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
|
||||
normalizeAndSortNetworkMap(legacyNetworkMap)
|
||||
legacyJSON, err := json.MarshalIndent(toNetworkMapJSON(legacyNetworkMap), "", " ")
|
||||
require.NoError(t, err, "error marshaling legacy network map to JSON")
|
||||
@@ -466,7 +466,7 @@ func TestGetPeerNetworkMap_Golden_WithDeletedPeer(t *testing.T) {
|
||||
err = builder.OnPeerDeleted(account, deletedPeerID)
|
||||
require.NoError(t, err, "error deleting peer from cache")
|
||||
|
||||
newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
|
||||
newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, nil)
|
||||
normalizeAndSortNetworkMap(newNetworkMap)
|
||||
newJSON, err := json.MarshalIndent(toNetworkMapJSON(newNetworkMap), "", " ")
|
||||
require.NoError(t, err, "error marshaling new network map to JSON")
|
||||
@@ -537,7 +537,7 @@ func TestGetPeerNetworkMap_Golden_WithDeletedRouterPeer(t *testing.T) {
|
||||
resourcePolicies := account.GetResourcePoliciesMap()
|
||||
routers := account.GetResourceRoutersMap()
|
||||
|
||||
legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
|
||||
legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
|
||||
normalizeAndSortNetworkMap(legacyNetworkMap)
|
||||
legacyJSON, err := json.MarshalIndent(toNetworkMapJSON(legacyNetworkMap), "", " ")
|
||||
require.NoError(t, err, "error marshaling legacy network map to JSON")
|
||||
@@ -545,7 +545,7 @@ func TestGetPeerNetworkMap_Golden_WithDeletedRouterPeer(t *testing.T) {
|
||||
err = builder.OnPeerDeleted(account, deletedRouterID)
|
||||
require.NoError(t, err, "error deleting routing peer from cache")
|
||||
|
||||
newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
|
||||
newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, nil)
|
||||
normalizeAndSortNetworkMap(newNetworkMap)
|
||||
newJSON, err := json.MarshalIndent(toNetworkMapJSON(newNetworkMap), "", " ")
|
||||
require.NoError(t, err, "error marshaling new network map to JSON")
|
||||
@@ -597,7 +597,7 @@ func BenchmarkGetPeerNetworkMap_AfterPeerDeleted(b *testing.B) {
|
||||
b.Run("old builder after delete", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, testingPeerID := range peerIDs {
|
||||
_ = account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil, nil, nil, account.GetActiveGroupUsers())
|
||||
_ = account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, nil, nil, nil, account.GetActiveGroupUsers())
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -607,7 +607,7 @@ func BenchmarkGetPeerNetworkMap_AfterPeerDeleted(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = builder.OnPeerDeleted(account, deletedPeerID)
|
||||
for _, testingPeerID := range peerIDs {
|
||||
_ = builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
|
||||
_ = builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -944,7 +944,7 @@ func TestGetPeerNetworkMap_Golden_New_WithOnPeerAddedRouter_Batched(t *testing.T
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
networkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
|
||||
networkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, nil, validatedPeersMap, nil)
|
||||
|
||||
normalizeAndSortNetworkMap(networkMap)
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/netbirdio/netbird/client/ssh/auth"
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
||||
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
@@ -1033,7 +1034,7 @@ func (b *NetworkMapBuilder) updateAccountLocked(account *Account) *Account {
|
||||
}
|
||||
|
||||
func (b *NetworkMapBuilder) GetPeerNetworkMap(
|
||||
ctx context.Context, peerID string, peersCustomZone nbdns.CustomZone,
|
||||
ctx context.Context, peerID string, peersCustomZone nbdns.CustomZone, accountZones []*zones.Zone,
|
||||
validatedPeers map[string]struct{}, metrics *telemetry.AccountManagerMetrics,
|
||||
) *NetworkMap {
|
||||
start := time.Now()
|
||||
@@ -1057,7 +1058,7 @@ func (b *NetworkMapBuilder) GetPeerNetworkMap(
|
||||
return &NetworkMap{Network: account.Network.Copy()}
|
||||
}
|
||||
|
||||
nm := b.assembleNetworkMap(account, peer, aclView, routesView, dnsConfig, sshView, peersCustomZone, validatedPeers)
|
||||
nm := b.assembleNetworkMap(ctx, account, peer, aclView, routesView, dnsConfig, sshView, peersCustomZone, accountZones, validatedPeers)
|
||||
|
||||
if metrics != nil {
|
||||
objectCount := int64(len(nm.Peers) + len(nm.OfflinePeers) + len(nm.Routes) + len(nm.FirewallRules) + len(nm.RoutesFirewallRules))
|
||||
@@ -1074,8 +1075,8 @@ func (b *NetworkMapBuilder) GetPeerNetworkMap(
|
||||
}
|
||||
|
||||
func (b *NetworkMapBuilder) assembleNetworkMap(
|
||||
account *Account, peer *nbpeer.Peer, aclView *PeerACLView, routesView *PeerRoutesView,
|
||||
dnsConfig *nbdns.Config, sshView *PeerSSHView, customZone nbdns.CustomZone, validatedPeers map[string]struct{},
|
||||
ctx context.Context, account *Account, peer *nbpeer.Peer, aclView *PeerACLView, routesView *PeerRoutesView,
|
||||
dnsConfig *nbdns.Config, sshView *PeerSSHView, peersCustomZone nbdns.CustomZone, accountZones []*zones.Zone, validatedPeers map[string]struct{},
|
||||
) *NetworkMap {
|
||||
|
||||
var peersToConnect []*nbpeer.Peer
|
||||
@@ -1125,13 +1126,26 @@ func (b *NetworkMapBuilder) assembleNetworkMap(
|
||||
}
|
||||
|
||||
finalDNSConfig := *dnsConfig
|
||||
if finalDNSConfig.ServiceEnable && customZone.Domain != "" {
|
||||
if finalDNSConfig.ServiceEnable {
|
||||
var zones []nbdns.CustomZone
|
||||
records := filterZoneRecordsForPeers(peer, customZone, peersToConnect, expiredPeers)
|
||||
zones = append(zones, nbdns.CustomZone{
|
||||
Domain: customZone.Domain,
|
||||
Records: records,
|
||||
})
|
||||
|
||||
peerGroupsSlice := b.cache.peerToGroups[peer.ID]
|
||||
peerGroups := make(LookupMap, len(peerGroupsSlice))
|
||||
for _, groupID := range peerGroupsSlice {
|
||||
peerGroups[groupID] = struct{}{}
|
||||
}
|
||||
|
||||
if peersCustomZone.Domain != "" {
|
||||
records := filterZoneRecordsForPeers(peer, peersCustomZone, peersToConnect, expiredPeers)
|
||||
zones = append(zones, nbdns.CustomZone{
|
||||
Domain: peersCustomZone.Domain,
|
||||
Records: records,
|
||||
})
|
||||
}
|
||||
|
||||
filteredAccountZones := filterPeerAppliedZones(ctx, accountZones, peerGroups)
|
||||
zones = append(zones, filteredAccountZones...)
|
||||
|
||||
finalDNSConfig.CustomZones = zones
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package util
|
||||
|
||||
import "regexp"
|
||||
|
||||
var domainRegex = regexp.MustCompile(`^(\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$`)
|
||||
|
||||
// Difference returns the elements in `a` that aren't in `b`.
|
||||
func Difference(a, b []string) []string {
|
||||
mb := make(map[string]struct{}, len(b))
|
||||
@@ -50,3 +54,10 @@ func contains[T comparableObject[T]](slice []T, element T) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsValidDomain(domain string) bool {
|
||||
if domain == "" {
|
||||
return false
|
||||
}
|
||||
return domainRegex.MatchString(domain)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user