mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-16 19:49:56 +00:00
* [agent-network] Shared proto, OpenAPI schema, and generated types * [agent-network] Management: store, manager, synthesizer, policy engine, provider catalog, HTTP/gRPC API Adds the account-scoped agent-network module: provider/policy/budget CRUD and store, the reverse-proxy service synthesizer, policy selection + limit enforcement, the provider catalog (incl. Vertex AI and AWS Bedrock entries), and the management HTTP + proxy gRPC surfaces. * [management] Fix agent-network proxy-peer fan-out on affected-peer recompute The affected-peers resolver loaded only persisted reverse-proxy services, but agent-network services are synthesized on demand and never persisted. As a result the embedded proxy peer was never folded into the affected set when a client's group changed, so the proxy received no network-map update for a newly authorised client and rejected its handshake until a full resync (restart). loadProxyServices now merges the synthesized agent-network services (injected via a registration hook to avoid an import cycle), so proxy peers learn newly authorised clients immediately. * [proxy] Reverse-proxy middleware framework, chain, and request plumbing The per-target middleware chain (slots, dispatcher, mutation gate, metadata merger), body capture, access-log terminal sink, and the proxy wiring that builds + runs chains for synthesized agent-network services. * [proxy] LLM parsers, pricing, and builtin middlewares (OpenAI, Anthropic, Vertex AI, AWS Bedrock) Request/response parsers and SSE/event-stream metering, the embedded pricing table, and the builtin middleware set: request parser, router, policy limit-check/record, cost meter, guardrail, identity inject, response parser. Includes the path-routed providers — Google Vertex AI (keyfile:: service-account OAuth minting) and AWS Bedrock (bearer auth, invoke/converse/streaming, optional /bedrock prefix) — plus the Models allowlist and unmeterable-publisher deny. * [proxy] IPv6 in-place apply and TCP accept-loop hardening on netstack listeners * [agent-network] End-to-end test suite, module docs, and deployment preset * [agent-network] Fix codespell typos and exclude false positives - labelgen word pool: vermillion -> vermilion, racoon -> raccoon. - codespell ignore list: add flate (Go compress/flate package), recordin (a test-local identifier), and unparseable (a valid alternative spelling used consistently across identifiers + a metadata-value constant). * [management] Set LastSeen on injected proxy peer in realstack test (MySQL strict-mode) The injected embedded proxy peer had a PeerStatus with a zero LastSeen, which serializes to '0000-00-00' and is rejected by MySQL in strict mode (SQLite tolerates it). Set LastSeen to a valid time so SaveAccount succeeds on both engines. * [agent-network] Remove e2e shell-script suite from this branch The end-to-end shell scripts under scripts/e2e/ are maintained in a separate testing suite and are not part of this change set. * [agent-network] Polish module docs: remove internal review scaffolding, fix links, verify diagrams Strip PR-review framing, commit references, absolute paths, and stale internal references from the agent-network module docs; fix broken relative links; verify all diagrams against the current architecture. Remove the internal AI-reviewer prompt file. * [management] Refine session expiration handling to support 3-state encoding for SSO deadlines * [agent-network] Relocate agentnetwork package to internals/modules Move management/server/agentnetwork (and its catalog/, labelgen/, types/ subpackages) to management/internals/modules/agentnetwork, alongside the reverse-proxy module, and rewrite all importers. Pure relocation: package names, the synthesizer + affectedpeers registration hook, and store access (shared store.Store) are unchanged, so no import cycle is introduced (affectedpeers still depends only on the agentnetwork/types leaf). * [agent-network] Co-locate HTTP handlers in the module (RegisterEndpoints) Move the agent-network HTTP handlers from server/http/handlers/agentnetwork into the module at internals/modules/agentnetwork/handlers (package handlers) and rename the entrypoint AddEndpoints -> RegisterEndpoints, matching the reverse-proxy module convention. Wiring in http/handler.go updated accordingly.
254 lines
8.6 KiB
Go
254 lines
8.6 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"github.com/netbirdio/management-integrations/integrations"
|
|
|
|
"github.com/netbirdio/netbird/management/internals/modules/peers"
|
|
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy/domain/manager"
|
|
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy/proxy"
|
|
proxymanager "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/proxy/manager"
|
|
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"
|
|
nbreverseproxy "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service/manager"
|
|
"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"
|
|
"github.com/netbirdio/netbird/management/server/account"
|
|
"github.com/netbirdio/netbird/management/internals/modules/agentnetwork"
|
|
"github.com/netbirdio/netbird/management/server/geolocation"
|
|
"github.com/netbirdio/netbird/management/server/groups"
|
|
"github.com/netbirdio/netbird/management/server/idp"
|
|
"github.com/netbirdio/netbird/management/server/networks"
|
|
"github.com/netbirdio/netbird/management/server/networks/resources"
|
|
"github.com/netbirdio/netbird/management/server/networks/routers"
|
|
"github.com/netbirdio/netbird/management/server/types"
|
|
|
|
"github.com/netbirdio/netbird/management/server/permissions"
|
|
"github.com/netbirdio/netbird/management/server/settings"
|
|
"github.com/netbirdio/netbird/management/server/users"
|
|
)
|
|
|
|
const (
|
|
geolocationDisabledKey = "NB_DISABLE_GEOLOCATION"
|
|
)
|
|
|
|
func (s *BaseServer) GeoLocationManager() geolocation.Geolocation {
|
|
if os.Getenv(geolocationDisabledKey) == "true" {
|
|
log.Info("geolocation service is disabled, skipping initialization")
|
|
return nil
|
|
}
|
|
|
|
return Create(s, func() geolocation.Geolocation {
|
|
geo, err := geolocation.NewGeolocation(context.Background(), s.Config.Datadir, !s.disableGeoliteUpdate)
|
|
if err != nil {
|
|
log.Fatalf("could not initialize geolocation service: %v", err)
|
|
}
|
|
|
|
log.Infof("geolocation service has been initialized from %s", s.Config.Datadir)
|
|
|
|
return geo
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) PermissionsManager() permissions.Manager {
|
|
return Create(s, func() permissions.Manager {
|
|
return permissions.NewManager(s.Store())
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) UsersManager() users.Manager {
|
|
return Create(s, func() users.Manager {
|
|
return users.NewManager(s.Store())
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) SettingsManager() settings.Manager {
|
|
return Create(s, func() settings.Manager {
|
|
extraSettingsManager := integrations.NewManager(s.EventStore())
|
|
|
|
idpConfig := settings.IdpConfig{}
|
|
if s.Config.EmbeddedIdP != nil && s.Config.EmbeddedIdP.Enabled {
|
|
idpConfig.EmbeddedIdpEnabled = true
|
|
idpConfig.LocalAuthDisabled = s.Config.EmbeddedIdP.LocalAuthDisabled
|
|
}
|
|
|
|
return settings.NewManager(s.Store(), s.UsersManager(), extraSettingsManager, s.PermissionsManager(), idpConfig)
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) PeersManager() peers.Manager {
|
|
return Create(s, func() peers.Manager {
|
|
manager := peers.NewManager(s.Store(), s.PermissionsManager())
|
|
s.AfterInit(func(s *BaseServer) {
|
|
manager.SetNetworkMapController(s.NetworkMapController())
|
|
manager.SetIntegratedPeerValidator(s.IntegratedValidator())
|
|
manager.SetAccountManager(s.AccountManager())
|
|
})
|
|
return manager
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) AccountManager() account.Manager {
|
|
return Create(s, func() account.Manager {
|
|
accountManager, err := server.BuildManager(context.Background(), s.Config, s.Store(), s.NetworkMapController(), s.JobManager(), s.IdpManager(), s.mgmtSingleAccModeDomain, s.EventStore(), s.GeoLocationManager(), s.userDeleteFromIDPEnabled, s.IntegratedValidator(), s.Metrics(), s.ProxyController(), s.SettingsManager(), s.PermissionsManager(), s.Config.DisableDefaultPolicy, s.CacheStore())
|
|
if err != nil {
|
|
log.Fatalf("failed to create account service: %v", err)
|
|
}
|
|
|
|
s.AfterInit(func(s *BaseServer) {
|
|
accountManager.SetServiceManager(s.ServiceManager())
|
|
})
|
|
|
|
return accountManager
|
|
})
|
|
}
|
|
|
|
func isMFAEnabledForAccount(accounts []*types.Account) bool {
|
|
if len(accounts) != 1 {
|
|
return false
|
|
}
|
|
|
|
settings := accounts[0].Settings
|
|
return settings != nil && settings.LocalMfaEnabled
|
|
}
|
|
|
|
func (s *BaseServer) IdpManager() idp.Manager {
|
|
return Create(s, func() idp.Manager {
|
|
// Use embedded IdP service if embedded Dex is configured and enabled.
|
|
// Legacy IdpManager won't be used anymore even if configured.
|
|
embeddedEnabled := s.Config.EmbeddedIdP != nil && s.Config.EmbeddedIdP.Enabled
|
|
if embeddedEnabled {
|
|
embeddedMgr, err := idp.NewEmbeddedIdPManager(context.Background(), s.Config.EmbeddedIdP, s.Metrics())
|
|
if err != nil {
|
|
log.Fatalf("failed to create embedded IDP service: %v", err)
|
|
}
|
|
|
|
if val := isMFAEnabledForAccount(s.Store().GetAllAccounts(context.Background())); val {
|
|
if err := embeddedMgr.SetMFAEnabled(context.Background(), val); err != nil {
|
|
log.Errorf("failed to set MFA enabled on embedded IDP: %v", err)
|
|
}
|
|
}
|
|
|
|
return embeddedMgr
|
|
}
|
|
|
|
// Fall back to external IdP service
|
|
if s.Config.IdpManagerConfig != nil {
|
|
idpManager, err := idp.NewManager(context.Background(), *s.Config.IdpManagerConfig, s.Metrics())
|
|
if err != nil {
|
|
log.Fatalf("failed to create IDP service: %v", err)
|
|
}
|
|
|
|
return idpManager
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// OAuthConfigProvider is only relevant when we have an embedded IdP service. Otherwise must be nil
|
|
func (s *BaseServer) OAuthConfigProvider() idp.OAuthConfigProvider {
|
|
if s.Config.EmbeddedIdP == nil || !s.Config.EmbeddedIdP.Enabled {
|
|
return nil
|
|
}
|
|
|
|
idpManager := s.IdpManager()
|
|
if idpManager == nil {
|
|
return nil
|
|
}
|
|
|
|
// Reuse the EmbeddedIdPManager instance from IdpManager
|
|
// EmbeddedIdPManager implements both idp.Manager and idp.OAuthConfigProvider
|
|
if provider, ok := idpManager.(idp.OAuthConfigProvider); ok {
|
|
return provider
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *BaseServer) GroupsManager() groups.Manager {
|
|
return Create(s, func() groups.Manager {
|
|
return groups.NewManager(s.Store(), s.PermissionsManager(), s.AccountManager())
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) ResourcesManager() resources.Manager {
|
|
return Create(s, func() resources.Manager {
|
|
return resources.NewManager(s.Store(), s.PermissionsManager(), s.GroupsManager(), s.AccountManager(), s.ServiceManager())
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) RoutesManager() routers.Manager {
|
|
return Create(s, func() routers.Manager {
|
|
return routers.NewManager(s.Store(), s.PermissionsManager(), s.AccountManager())
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) NetworksManager() networks.Manager {
|
|
return Create(s, func() networks.Manager {
|
|
return networks.NewManager(s.Store(), s.PermissionsManager(), s.ResourcesManager(), s.RoutesManager(), s.AccountManager())
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) AgentNetworkManager() agentnetwork.Manager {
|
|
return Create(s, func() agentnetwork.Manager {
|
|
mgr := agentnetwork.NewManager(
|
|
s.Store(),
|
|
s.PermissionsManager(),
|
|
s.AccountManager(),
|
|
s.ServiceProxyController(),
|
|
)
|
|
// Sweep expired agent-network access logs per account retention,
|
|
// reusing the reverse-proxy cleanup interval config.
|
|
mgr.StartAccessLogCleanup(
|
|
context.Background(),
|
|
s.Config.ReverseProxy.AccessLogCleanupIntervalHours,
|
|
)
|
|
return mgr
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) ZonesManager() zones.Manager {
|
|
return Create(s, func() zones.Manager {
|
|
return zonesManager.NewManager(s.Store(), s.AccountManager(), s.PermissionsManager(), s.DNSDomain())
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) RecordsManager() records.Manager {
|
|
return Create(s, func() records.Manager {
|
|
return recordsManager.NewManager(s.Store(), s.AccountManager(), s.PermissionsManager())
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) ServiceManager() service.Manager {
|
|
return Create(s, func() service.Manager {
|
|
return nbreverseproxy.NewManager(s.Store(), s.AccountManager(), s.PermissionsManager(), s.ServiceProxyController(), s.ProxyManager(), s.ReverseProxyDomainManager())
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) ProxyManager() proxy.Manager {
|
|
return Create(s, func() proxy.Manager {
|
|
manager, err := proxymanager.NewManager(s.Store(), s.Metrics().GetMeter())
|
|
if err != nil {
|
|
log.Fatalf("failed to create proxy manager: %v", err)
|
|
}
|
|
return manager
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) ReverseProxyDomainManager() *manager.Manager {
|
|
return Create(s, func() *manager.Manager {
|
|
m := manager.NewManager(s.Store(), s.ProxyManager(), s.PermissionsManager(), s.AccountManager())
|
|
return &m
|
|
})
|
|
}
|
|
|
|
func (s *BaseServer) IsValidChildAccount(_ context.Context, _, _, _ string) bool {
|
|
return false
|
|
}
|