diff --git a/management/cmd/management.go b/management/cmd/management.go index b61c3a8d3..e186322dd 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -16,10 +16,11 @@ import ( "strings" "syscall" - "github.com/netbirdio/netbird/management/server/types" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/netbirdio/netbird/management/server/types" + "github.com/netbirdio/netbird/formatter/hook" "github.com/netbirdio/netbird/management/internals/server" nbconfig "github.com/netbirdio/netbird/management/internals/server/config" diff --git a/management/internals/controllers/network_map/controller/controller.go b/management/internals/controllers/network_map/controller/controller.go index 8a26568a1..6b8045b07 100644 --- a/management/internals/controllers/network_map/controller/controller.go +++ b/management/internals/controllers/network_map/controller/controller.go @@ -174,15 +174,13 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin var wg sync.WaitGroup semaphore := make(chan struct{}, 10) + account.InjectProxyPolicies() dnsCache := &cache.DNSConfigCache{} dnsDomain := c.GetDNSDomain(account.Settings) peersCustomZone := account.GetPeersCustomZone(ctx, dnsDomain) resourcePolicies := account.GetResourcePoliciesMap() routers := account.GetResourceRoutersMap() - resources := account.GetResourcesMap() groupIDToUserIDs := account.GetActiveGroupUsers() - exposedServices := account.GetExposedServicesMap() - proxyPeers := account.GetProxyPeers() if c.experimentalNetworkMap(accountID) { c.initNetworkMapBuilderIfNeeded(account, approvedPeersMap) @@ -235,7 +233,7 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin if c.experimentalNetworkMap(accountID) { remotePeerNetworkMap = c.getPeerNetworkMapExp(ctx, p.AccountID, p.ID, approvedPeersMap, peersCustomZone, accountZones, c.accountManagerMetrics) } else { - remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, p.ID, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, resources, c.accountManagerMetrics, groupIDToUserIDs, exposedServices, proxyPeers) + remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, p.ID, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics, groupIDToUserIDs) } c.metrics.CountCalcPeerNetworkMapDuration(time.Since(start)) @@ -329,12 +327,12 @@ func (c *Controller) UpdateAccountPeer(ctx context.Context, accountId string, pe return fmt.Errorf("failed to get validated peers: %v", err) } + account.InjectProxyPolicies() dnsCache := &cache.DNSConfigCache{} dnsDomain := c.GetDNSDomain(account.Settings) peersCustomZone := account.GetPeersCustomZone(ctx, dnsDomain) resourcePolicies := account.GetResourcePoliciesMap() routers := account.GetResourceRoutersMap() - resources := account.GetResourcesMap() groupIDToUserIDs := account.GetActiveGroupUsers() postureChecks, err := c.getPeerPostureChecks(account, peerId) @@ -360,7 +358,7 @@ func (c *Controller) UpdateAccountPeer(ctx context.Context, accountId string, pe if c.experimentalNetworkMap(accountId) { remotePeerNetworkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, peersCustomZone, accountZones, c.accountManagerMetrics) } else { - remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, peerId, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, resources, c.accountManagerMetrics, groupIDToUserIDs, account.GetExposedServicesMap(), account.GetProxyPeers()) + remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, peerId, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics, groupIDToUserIDs) } proxyNetworkMap, ok := proxyNetworkMaps[peer.ID] @@ -445,6 +443,8 @@ func (c *Controller) GetValidatedPeerWithMap(ctx context.Context, isRequiresAppr } } + account.InjectProxyPolicies() + approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra) if err != nil { return nil, nil, nil, 0, err @@ -479,8 +479,7 @@ func (c *Controller) GetValidatedPeerWithMap(ctx context.Context, isRequiresAppr } else { resourcePolicies := account.GetResourcePoliciesMap() routers := account.GetResourceRoutersMap() - resources := account.GetResourcesMap() - networkMap = account.GetPeerNetworkMap(ctx, peer.ID, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, resources, c.accountManagerMetrics, account.GetActiveGroupUsers(), account.GetExposedServicesMap(), account.GetProxyPeers()) + networkMap = account.GetPeerNetworkMap(ctx, peer.ID, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics, account.GetActiveGroupUsers()) } proxyNetworkMap, ok := proxyNetworkMaps[peer.ID] @@ -852,10 +851,10 @@ func (c *Controller) GetNetworkMap(ctx context.Context, peerID string) (*types.N if c.experimentalNetworkMap(peer.AccountID) { networkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peerID, validatedPeers, peersCustomZone, accountZones, nil) } else { + account.InjectProxyPolicies() resourcePolicies := account.GetResourcePoliciesMap() routers := account.GetResourceRoutersMap() - resources := account.GetResourcesMap() - networkMap = account.GetPeerNetworkMap(ctx, peer.ID, peersCustomZone, accountZones, validatedPeers, resourcePolicies, routers, resources, nil, account.GetActiveGroupUsers(), account.GetExposedServicesMap(), account.GetProxyPeers()) + networkMap = account.GetPeerNetworkMap(ctx, peer.ID, peersCustomZone, accountZones, validatedPeers, resourcePolicies, routers, nil, account.GetActiveGroupUsers()) } proxyNetworkMap, ok := proxyNetworkMaps[peer.ID] diff --git a/management/internals/modules/reverseproxy/manager/manager.go b/management/internals/modules/reverseproxy/manager/manager.go index 487ee463b..8e98738de 100644 --- a/management/internals/modules/reverseproxy/manager/manager.go +++ b/management/internals/modules/reverseproxy/manager/manager.go @@ -227,7 +227,7 @@ func validateTargetReferences(ctx context.Context, transaction store.Store, acco } return fmt.Errorf("look up peer target %q: %w", target.TargetId, err) } - case reverseproxy.TargetTypeResource: + case reverseproxy.TargetTypeHost, reverseproxy.TargetTypeSubnet, reverseproxy.TargetTypeDomain: if _, err := transaction.GetNetworkResourceByID(ctx, store.LockingStrengthShare, accountID, target.TargetId); err != nil { if sErr, ok := status.FromError(err); ok && sErr.Type() == status.NotFound { return status.Errorf(status.InvalidArgument, "resource target %q not found in account", target.TargetId) diff --git a/management/internals/modules/reverseproxy/reverseproxy.go b/management/internals/modules/reverseproxy/reverseproxy.go index 3adb0b982..26828ceee 100644 --- a/management/internals/modules/reverseproxy/reverseproxy.go +++ b/management/internals/modules/reverseproxy/reverseproxy.go @@ -35,8 +35,10 @@ const ( StatusCertificateFailed ProxyStatus = "certificate_failed" StatusError ProxyStatus = "error" - TargetTypePeer = "peer" - TargetTypeResource = "resource" + TargetTypePeer = "peer" + TargetTypeHost = "host" + TargetTypeDomain = "domain" + TargetTypeSubnet = "subnet" ) type Target struct { @@ -346,8 +348,11 @@ func (r *ReverseProxy) Validate() error { } for i, target := range r.Targets { - if target.TargetType != TargetTypePeer && target.TargetType != TargetTypeResource { - return fmt.Errorf("target %d has invalid target_type %q, must be %q or %q", i, target.TargetType, TargetTypePeer, TargetTypeResource) + switch target.TargetType { + case TargetTypePeer, TargetTypeHost, TargetTypeSubnet, TargetTypeDomain: + // valid resource types + default: + return fmt.Errorf("target %d has invalid target_type %q", i, target.TargetType) } if target.TargetId == "" { return fmt.Errorf("target %d has empty target_id", i) diff --git a/management/internals/server/server.go b/management/internals/server/server.go index 03667d419..158651803 100644 --- a/management/internals/server/server.go +++ b/management/internals/server/server.go @@ -11,7 +11,6 @@ import ( "time" "github.com/google/uuid" - "github.com/netbirdio/netbird/management/server/idp" log "github.com/sirupsen/logrus" "go.opentelemetry.io/otel/metric" "golang.org/x/crypto/acme/autocert" @@ -21,6 +20,7 @@ import ( "github.com/netbirdio/netbird/encryption" nbconfig "github.com/netbirdio/netbird/management/internals/server/config" + "github.com/netbirdio/netbird/management/server/idp" "github.com/netbirdio/netbird/management/server/metrics" "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/util/wsproxy" diff --git a/management/server/http/handlers/peers/peers_handler.go b/management/server/http/handlers/peers/peers_handler.go index 2295470ec..783cfe11b 100644 --- a/management/server/http/handlers/peers/peers_handler.go +++ b/management/server/http/handlers/peers/peers_handler.go @@ -404,7 +404,7 @@ func (h *Handler) GetAccessiblePeers(w http.ResponseWriter, r *http.Request) { dnsDomain := h.networkMapController.GetDNSDomain(account.Settings) - netMap := account.GetPeerNetworkMap(r.Context(), peerID, dns.CustomZone{}, nil, validPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), account.GetResourcesMap(), nil, account.GetActiveGroupUsers(), account.GetExposedServicesMap(), account.GetProxyPeers()) + 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)) } diff --git a/management/server/types/account.go b/management/server/types/account.go index 1fe1c5877..ecd3339c2 100644 --- a/management/server/types/account.go +++ b/management/server/types/account.go @@ -283,11 +283,8 @@ func (a *Account) GetPeerNetworkMap( validatedPeersMap map[string]struct{}, resourcePolicies map[string][]*Policy, routers map[string]map[string]*routerTypes.NetworkRouter, - resourcesMap map[string]*resourceTypes.NetworkResource, metrics *telemetry.AccountManagerMetrics, groupIDToUserIDs map[string][]string, - exposedServices map[string][]*reverseproxy.ReverseProxy, - proxyPeers []*nbpeer.Peer, ) *NetworkMap { start := time.Now() peer := a.Peers[peerID] @@ -305,21 +302,10 @@ func (a *Account) GetPeerNetworkMap( peerGroups := a.GetPeerGroups(peerID) - var aclPeers []*nbpeer.Peer - var firewallRules []*FirewallRule - var authorizedUsers map[string]map[string]struct{} - var enableSSH bool - if peer.ProxyEmbedded { - aclPeers = a.GetProxyConnectionResources(ctx, exposedServices) - } else { - aclPeers, firewallRules, authorizedUsers, enableSSH = a.GetPeerConnectionResources(ctx, peer, validatedPeersMap, groupIDToUserIDs) - proxyAclPeers, proxyFirewallRules := a.GetPeerProxyResources(peerID, exposedServices[peerID], proxyPeers) - aclPeers = append(aclPeers, proxyAclPeers...) - firewallRules = append(firewallRules, proxyFirewallRules...) - } - - var peersToConnect, expiredPeers []*nbpeer.Peer + aclPeers, firewallRules, authorizedUsers, enableSSH := a.GetPeerConnectionResources(ctx, peer, validatedPeersMap, groupIDToUserIDs) // exclude expired peers + var peersToConnect []*nbpeer.Peer + var expiredPeers []*nbpeer.Peer for _, p := range aclPeers { expired, _ := p.LoginExpired(a.Settings.PeerLoginExpiration) if a.Settings.PeerLoginExpirationEnabled && expired { @@ -329,34 +315,14 @@ func (a *Account) GetPeerNetworkMap( peersToConnect = append(peersToConnect, p) } - var routes, networksRoutes []*route.Route - var isRouter bool - var sourcePeers map[string]struct{} - var routesFirewallRules []*RouteFirewallRule - if peer.ProxyEmbedded { - routes, routesFirewallRules, aclPeers = a.GetPeerProxyRoutes(ctx, peer, exposedServices, resourcesMap, routers, proxyPeers) - for _, p := range aclPeers { - expired, _ := p.LoginExpired(a.Settings.PeerLoginExpiration) - if a.Settings.PeerLoginExpirationEnabled && expired { - expiredPeers = append(expiredPeers, p) - continue - } - peersToConnect = append(peersToConnect, p) - } - } else { - oldRoutes := a.GetRoutesToSync(ctx, peerID, peersToConnect, peerGroups) - oldRoutesFirewallRules := a.GetPeerRoutesFirewallRules(ctx, peerID, validatedPeersMap) - proxyRoutes, proxyRoutesFirewallRules, _ := a.GetPeerProxyRoutes(ctx, peer, exposedServices, resourcesMap, routers, proxyPeers) - isRouter, networksRoutes, sourcePeers = a.GetNetworkResourcesRoutesToSync(ctx, peerID, resourcePolicies, routers) - var networksFirewallRules []*RouteFirewallRule - if isRouter { - networksFirewallRules = a.GetPeerNetworkResourceFirewallRules(ctx, peer, validatedPeersMap, networksRoutes, resourcePolicies) - } - routes = slices.Concat(networksRoutes, oldRoutes, proxyRoutes) - routesFirewallRules = slices.Concat(networksFirewallRules, oldRoutesFirewallRules, proxyRoutesFirewallRules) + 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 + if isRouter { + networkResourcesFirewallRules = a.GetPeerNetworkResourceFirewallRules(ctx, peer, validatedPeersMap, networkResourcesRoutes, resourcePolicies) } - - peersToConnectIncludingRouters := a.addNetworksRoutingPeers(routes, peer, peersToConnect, expiredPeers, isRouter, sourcePeers) + peersToConnectIncludingRouters := a.addNetworksRoutingPeers(networkResourcesRoutes, peer, peersToConnect, expiredPeers, isRouter, sourcePeers) dnsManagementStatus := a.getPeerDNSManagementStatus(peerID) dnsUpdate := nbdns.Config{ @@ -384,24 +350,24 @@ func (a *Account) GetPeerNetworkMap( nm := &NetworkMap{ Peers: peersToConnectIncludingRouters, Network: a.Network.Copy(), - Routes: routes, + Routes: slices.Concat(networkResourcesRoutes, routesUpdate), DNSConfig: dnsUpdate, OfflinePeers: expiredPeers, FirewallRules: firewallRules, - RoutesFirewallRules: routesFirewallRules, + RoutesFirewallRules: slices.Concat(networkResourcesFirewallRules, routesFirewallRules), AuthorizedUsers: authorizedUsers, EnableSSH: enableSSH, } if metrics != nil { - objectCount := int64(len(peersToConnectIncludingRouters) + len(expiredPeers) + len(routes) + len(firewallRules) + +len(routesFirewallRules)) + objectCount := int64(len(peersToConnectIncludingRouters) + len(expiredPeers) + len(routesUpdate) + len(networkResourcesRoutes) + len(firewallRules) + +len(networkResourcesFirewallRules) + len(routesFirewallRules)) metrics.CountNetworkMapObjects(objectCount) metrics.CountGetPeerNetworkMapDuration(time.Since(start)) if objectCount > 5000 { log.WithContext(ctx).Tracef("account: %s has a total resource count of %d objects, "+ - "peers to connect: %d, expired peers: %d, routes: %d, firewall rules: %d, routes firewall rules: %d", - a.Id, objectCount, len(peersToConnectIncludingRouters), len(expiredPeers), len(routes), len(firewallRules), len(routesFirewallRules)) + "peers to connect: %d, expired peers: %d, routes: %d, firewall rules: %d, network resources routes: %d, network resources firewall rules: %d, routes firewall rules: %d", + a.Id, objectCount, len(peersToConnectIncludingRouters), len(expiredPeers), len(routesUpdate), len(firewallRules), len(networkResourcesRoutes), len(networkResourcesFirewallRules), len(routesFirewallRules)) } } @@ -456,7 +422,7 @@ func (a *Account) GetPeerProxyResources(peerID string, services []*reverseproxy. aclPeers = proxyPeers needsPeerRules := (target.TargetType == reverseproxy.TargetTypePeer && target.TargetId == peerID) || - (target.TargetType == reverseproxy.TargetTypeResource && target.AccessLocal) + ((target.TargetType == reverseproxy.TargetTypeHost || target.TargetType == reverseproxy.TargetTypeSubnet || target.TargetType == reverseproxy.TargetTypeDomain) && target.AccessLocal) if needsPeerRules { for _, proxyPeer := range proxyPeers { @@ -1892,34 +1858,6 @@ func (a *Account) GetProxyPeers() []*nbpeer.Peer { return proxyPeers } -func (a *Account) GetExposedServicesMap() map[string][]*reverseproxy.ReverseProxy { - services := make(map[string][]*reverseproxy.ReverseProxy) - resourcesMap := make(map[string]*resourceTypes.NetworkResource) - for _, resource := range a.NetworkResources { - resourcesMap[resource.ID] = resource - } - routersMap := a.GetResourceRoutersMap() - for _, proxy := range a.ReverseProxies { - for _, target := range proxy.Targets { - switch target.TargetType { - case reverseproxy.TargetTypePeer: - services[target.TargetId] = append(services[target.TargetId], proxy) - case reverseproxy.TargetTypeResource: - resource, ok := resourcesMap[target.TargetId] - if !ok { - log.Warnf("proxy %s target resource %s not found in resources map", proxy.ID, target.TargetId) - continue - } - routers := routersMap[resource.NetworkID] - for peerID := range routers { - services[peerID] = append(services[peerID], proxy) - } - } - } - } - return services -} - func (a *Account) GetPeerProxyRoutes(ctx context.Context, peer *nbpeer.Peer, proxies map[string][]*reverseproxy.ReverseProxy, resourcesMap map[string]*resourceTypes.NetworkResource, routers map[string]map[string]*routerTypes.NetworkRouter, proxyPeers []*nbpeer.Peer) ([]*route.Route, []*RouteFirewallRule, []*nbpeer.Peer) { sourceRanges := make([]string, 0, len(proxyPeers)) for _, proxyPeer := range proxyPeers { @@ -1932,7 +1870,7 @@ func (a *Account) GetPeerProxyRoutes(ctx context.Context, peer *nbpeer.Peer, pro for _, proxyPerResource := range proxies { for _, proxy := range proxyPerResource { for _, target := range proxy.Targets { - if target.TargetType == reverseproxy.TargetTypeResource { + if target.TargetType == reverseproxy.TargetTypeHost || target.TargetType == reverseproxy.TargetTypeSubnet || target.TargetType == reverseproxy.TargetTypeDomain { resource, ok := resourcesMap[target.TargetId] if !ok { log.WithContext(ctx).Warnf("proxy target %s not found in resources map", target.TargetId) @@ -1985,6 +1923,56 @@ func (a *Account) GetResourcesMap() map[string]*resourceTypes.NetworkResource { return resourcesMap } +func (a *Account) InjectProxyPolicies() { + if len(a.ReverseProxies) == 0 { + return + } + + proxyPeers := a.GetProxyPeers() + if len(proxyPeers) == 0 { + return + } + + for _, proxyPeer := range proxyPeers { + for _, service := range a.ReverseProxies { + if !service.Enabled { + continue + } + for _, target := range service.Targets { + if !target.Enabled { + continue + } + + policyID := fmt.Sprintf("proxy-access-%s-%s", service.ID, proxyPeer.ID) + a.Policies = append(a.Policies, &Policy{ + ID: policyID, + Name: fmt.Sprintf("Proxy Access to %s", service.Name), + Enabled: true, + Rules: []*PolicyRule{ + { + ID: policyID, + PolicyID: policyID, + Name: fmt.Sprintf("Allow access to %s", service.Name), + Enabled: true, + SourceResource: Resource{ + ID: proxyPeer.ID, + Type: ResourceTypePeer, + }, + DestinationResource: Resource{ + ID: target.TargetId, + Type: ResourceType(target.TargetType), + }, + Bidirectional: false, + Protocol: PolicyRuleProtocolTCP, + Action: PolicyTrafficActionAccept, + }, + }, + }) + } + } + } +} + // expandPortsAndRanges expands Ports and PortRanges of a rule into individual firewall rules func expandPortsAndRanges(base FirewallRule, rule *PolicyRule, peer *nbpeer.Peer) []*FirewallRule { features := peerSupportedFirewallFeatures(peer.Meta.WtVersion)