mirror of
https://github.com/netbirdio/netbird.git
synced 2024-12-04 22:10:56 +01:00
extend example
This commit is contained in:
parent
27c3a4c5d6
commit
0c6c5fdc70
122
management/refactor/api/http/handler.go
Normal file
122
management/refactor/api/http/handler.go
Normal file
@ -0,0 +1,122 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/rs/cors"
|
||||
|
||||
"github.com/netbirdio/management-integrations/integrations"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/peers"
|
||||
s "github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/http/middleware"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||
)
|
||||
|
||||
const apiPrefix = "/api"
|
||||
|
||||
// AuthCfg contains parameters for authentication middleware
|
||||
type AuthCfg struct {
|
||||
Issuer string
|
||||
Audience string
|
||||
UserIDClaim string
|
||||
KeysLocation string
|
||||
}
|
||||
|
||||
type DefaultAPIHandler struct {
|
||||
Router *mux.Router
|
||||
AccountManager s.AccountManager
|
||||
geolocationManager *geolocation.Geolocation
|
||||
AuthCfg AuthCfg
|
||||
}
|
||||
|
||||
// EmptyObject is an empty struct used to return empty JSON object
|
||||
type EmptyObject struct {
|
||||
}
|
||||
|
||||
// NewDefaultAPIHandler creates the Management service HTTP API handler registering all the available endpoints.
|
||||
func NewDefaultAPIHandler(ctx context.Context, jwtValidator jwtclaims.JWTValidator, appMetrics telemetry.AppMetrics, authCfg AuthCfg) (http.Handler, error) {
|
||||
claimsExtractor := jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
||||
)
|
||||
|
||||
authMiddleware := middleware.NewAuthMiddleware(
|
||||
accountManager.GetAccountFromPAT,
|
||||
jwtValidator.ValidateAndParse,
|
||||
accountManager.MarkPATUsed,
|
||||
accountManager.CheckUserAccessByJWTGroups,
|
||||
claimsExtractor,
|
||||
authCfg.Audience,
|
||||
authCfg.UserIDClaim,
|
||||
)
|
||||
|
||||
corsMiddleware := cors.AllowAll()
|
||||
|
||||
acMiddleware := middleware.NewAccessControl(
|
||||
authCfg.Audience,
|
||||
authCfg.UserIDClaim,
|
||||
accountManager.GetUser)
|
||||
|
||||
rootRouter := mux.NewRouter()
|
||||
metricsMiddleware := appMetrics.HTTPMiddleware()
|
||||
|
||||
prefix := apiPrefix
|
||||
router := rootRouter.PathPrefix(prefix).Subrouter()
|
||||
router.Use(metricsMiddleware.Handler, corsMiddleware.Handler, authMiddleware.Handler, acMiddleware.Handler)
|
||||
|
||||
api := DefaultAPIHandler{
|
||||
Router: router,
|
||||
AccountManager: accountManager,
|
||||
geolocationManager: LocationManager,
|
||||
AuthCfg: authCfg,
|
||||
}
|
||||
|
||||
if _, err := integrations.RegisterHandlers(ctx, prefix, api.Router, accountManager, claimsExtractor); err != nil {
|
||||
return nil, fmt.Errorf("register integrations endpoints: %w", err)
|
||||
}
|
||||
|
||||
peers.RegisterPeersEndpoints(api.Router)
|
||||
// api.addAccountsEndpoint()
|
||||
// api.addPeersEndpoint()
|
||||
// api.addUsersEndpoint()
|
||||
// api.addUsersTokensEndpoint()
|
||||
// api.addSetupKeysEndpoint()
|
||||
// api.addRulesEndpoint()
|
||||
// api.addPoliciesEndpoint()
|
||||
// api.addGroupsEndpoint()
|
||||
// api.addRoutesEndpoint()
|
||||
// api.addDNSNameserversEndpoint()
|
||||
// api.addDNSSettingEndpoint()
|
||||
// api.addEventsEndpoint()
|
||||
// api.addPostureCheckEndpoint()
|
||||
// api.addLocationsEndpoint()
|
||||
|
||||
err := api.Router.Walk(func(route *mux.Route, _ *mux.Router, _ []*mux.Route) error {
|
||||
methods, err := route.GetMethods()
|
||||
if err != nil { // we may have wildcard routes from integrations without methods, skip them for now
|
||||
methods = []string{}
|
||||
}
|
||||
for _, method := range methods {
|
||||
template, err := route.GetPathTemplate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = metricsMiddleware.AddHTTPRequestResponseCounter(template, method)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rootRouter, nil
|
||||
}
|
7
management/refactor/api/http/specs/cfg.yaml
Normal file
7
management/refactor/api/http/specs/cfg.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
package: api
|
||||
generate:
|
||||
models: true
|
||||
embedded-spec: false
|
||||
output: types.gen.go
|
||||
compatibility:
|
||||
always-prefix-enum-values: true
|
16
management/refactor/api/http/specs/generate.sh
Executable file
16
management/refactor/api/http/specs/generate.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if ! which realpath > /dev/null 2>&1
|
||||
then
|
||||
echo realpath is not installed
|
||||
echo run: brew install coreutils
|
||||
exit 1
|
||||
fi
|
||||
|
||||
old_pwd=$(pwd)
|
||||
script_path=$(dirname $(realpath "$0"))
|
||||
cd "$script_path"
|
||||
go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@4a1477f6a8ba6ca8115cc23bb2fb67f0b9fca18e
|
||||
oapi-codegen --config cfg.yaml openapi.yml
|
||||
cd "$old_pwd"
|
2870
management/refactor/api/http/specs/openapi.yaml
Normal file
2870
management/refactor/api/http/specs/openapi.yaml
Normal file
File diff suppressed because it is too large
Load Diff
1
management/refactor/api/http/specs/types.gen.go
Normal file
1
management/refactor/api/http/specs/types.gen.go
Normal file
@ -0,0 +1 @@
|
||||
package specs
|
@ -3,11 +3,16 @@ package mesh
|
||||
import (
|
||||
"github.com/netbirdio/management-integrations/integrations"
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/refactor/peers"
|
||||
"github.com/netbirdio/netbird/management/refactor/policies"
|
||||
"github.com/netbirdio/netbird/management/refactor/settings"
|
||||
"github.com/netbirdio/netbird/management/refactor/api/http"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/network"
|
||||
networkTypes "github.com/netbirdio/netbird/management/refactor/resources/network/types"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/peers"
|
||||
peerTypes "github.com/netbirdio/netbird/management/refactor/resources/peers/types"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/policies"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/routes"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/settings"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/users"
|
||||
"github.com/netbirdio/netbird/management/refactor/store"
|
||||
"github.com/netbirdio/netbird/management/refactor/users"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
)
|
||||
@ -23,16 +28,22 @@ type DefaultController struct {
|
||||
userManager users.Manager
|
||||
policiesManager policies.Manager
|
||||
settingsManager settings.Manager
|
||||
networkManager network.Manager
|
||||
routesManager routes.Manager
|
||||
}
|
||||
|
||||
func NewDefaultController() *DefaultController {
|
||||
storeStore, _ := store.NewDefaultStore(store.SqliteStoreEngine, "", nil)
|
||||
settingsManager := settings.NewDefaultManager(storeStore)
|
||||
networkManager := network.NewDefaultManager()
|
||||
peersManager := peers.NewDefaultManager(storeStore, settingsManager)
|
||||
routesManager := routes.NewDefaultManager(storeStore, peersManager)
|
||||
usersManager := users.NewDefaultManager(storeStore, peersManager)
|
||||
policiesManager := policies.NewDefaultManager(storeStore, peersManager)
|
||||
|
||||
peersManager, settingsManager, usersManager, policiesManager, storeStore = integrations.InjectCloud(peersManager, policiesManager, settingsManager, usersManager, storeStore)
|
||||
apiHandler, _ := http.NewDefaultAPIHandler()
|
||||
|
||||
peersManager, settingsManager, usersManager, policiesManager, storeStore, apiHandler = integrations.InjectCloud(peersManager, policiesManager, settingsManager, usersManager, storeStore)
|
||||
|
||||
return &DefaultController{
|
||||
store: storeStore,
|
||||
@ -40,10 +51,12 @@ func NewDefaultController() *DefaultController {
|
||||
userManager: usersManager,
|
||||
policiesManager: policiesManager,
|
||||
settingsManager: settingsManager,
|
||||
networkManager: networkManager,
|
||||
routesManager: routesManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DefaultController) LoginPeer(login peers.PeerLogin) {
|
||||
func (c *DefaultController) LoginPeer(login peerTypes.PeerLogin) (*peerTypes.Peer, *networkTypes.NetworkMap, error) {
|
||||
|
||||
peer, err := c.peersManager.GetPeerByPubKey(login.WireGuardPubKey)
|
||||
if err != nil {
|
||||
@ -60,7 +73,7 @@ func (c *DefaultController) LoginPeer(login peers.PeerLogin) {
|
||||
}
|
||||
}
|
||||
|
||||
account, err := pm.accountManager.GetAccount(peer.GetAccountID())
|
||||
settings, err := c.settingsManager.GetSettings(peer.GetAccountID())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -68,7 +81,7 @@ func (c *DefaultController) LoginPeer(login peers.PeerLogin) {
|
||||
// this flag prevents unnecessary calls to the persistent store.
|
||||
shouldStorePeer := false
|
||||
updateRemotePeers := false
|
||||
if peerLoginExpired(peer, account) {
|
||||
if peerLoginExpired(peer, settings) {
|
||||
err = checkAuth(login.UserID, peer)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -79,7 +92,7 @@ func (c *DefaultController) LoginPeer(login peers.PeerLogin) {
|
||||
updateRemotePeers = true
|
||||
shouldStorePeer = true
|
||||
|
||||
pm.eventsManager.StoreEvent(login.UserID, peer.ID, account.Id, activity.UserLoggedInPeer, peer.EventMeta(pm.accountManager.GetDNSDomain()))
|
||||
pm.eventsManager.StoreEvent(login.UserID, peer.GetID(), peer.GetAccountID(), activity.UserLoggedInPeer, peer.EventMeta(pm.accountManager.GetDNSDomain()))
|
||||
}
|
||||
|
||||
if peer.UpdateMetaIfNew(login.Meta) {
|
||||
@ -107,29 +120,37 @@ func (c *DefaultController) SyncPeer() {
|
||||
|
||||
}
|
||||
|
||||
func (c *DefaultController) GetPeerNetworkMap(peerID, dnsDomain string) *NetworkMap {
|
||||
func (c *DefaultController) GetPeerNetworkMap(accountID, peerID, dnsDomain string) (*networkTypes.NetworkMap, error) {
|
||||
unlock := c.store.AcquireAccountLock(accountID)
|
||||
defer unlock()
|
||||
|
||||
network, err := c.networkManager.GetNetwork(accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peer, err := c.peersManager.GetNetworkPeerByID(peerID)
|
||||
if err != nil {
|
||||
return &NetworkMap{
|
||||
Network: a.Network.Copy(),
|
||||
}
|
||||
return &networkTypes.NetworkMap{
|
||||
Network: network.Copy(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
aclPeers, firewallRules := c.policiesManager.GetAccessiblePeersAndFirewallRules(peerID)
|
||||
// exclude expired peers
|
||||
var peersToConnect []peers.Peer
|
||||
var expiredPeers []peers.Peer
|
||||
var peersToConnect []*peerTypes.Peer
|
||||
var expiredPeers []*peerTypes.Peer
|
||||
accSettings, _ := c.settingsManager.GetSettings(peer.GetAccountID())
|
||||
for _, p := range aclPeers {
|
||||
expired, _ := p.LoginExpired(accSettings.GetPeerLoginExpiration())
|
||||
if accSettings.GetPeerLoginExpirationEnabled() && expired {
|
||||
expiredPeers = append(expiredPeers, p)
|
||||
expiredPeers = append(expiredPeers, &p)
|
||||
continue
|
||||
}
|
||||
peersToConnect = append(peersToConnect, p)
|
||||
peersToConnect = append(peersToConnect, &p)
|
||||
}
|
||||
|
||||
routesUpdate := a.getRoutesToSync(peerID, peersToConnect)
|
||||
routesUpdate := c.routesManager.GetRoutesToSync(peerID, peersToConnect, accountID)
|
||||
|
||||
dnsManagementStatus := a.getPeerDNSManagementStatus(peerID)
|
||||
dnsUpdate := nbdns.Config{
|
||||
@ -146,12 +167,12 @@ func (c *DefaultController) GetPeerNetworkMap(peerID, dnsDomain string) *Network
|
||||
dnsUpdate.NameServerGroups = getPeerNSGroups(a, peerID)
|
||||
}
|
||||
|
||||
return &NetworkMap{
|
||||
return &networkTypes.NetworkMap{
|
||||
Peers: peersToConnect,
|
||||
Network: a.Network.Copy(),
|
||||
Network: network.Copy(),
|
||||
Routes: routesUpdate,
|
||||
DNSConfig: dnsUpdate,
|
||||
OfflinePeers: expiredPeers,
|
||||
FirewallRules: firewallRules,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
package mesh
|
||||
|
||||
import (
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
)
|
||||
|
||||
type NetworkMap struct {
|
||||
Peers []*nbpeer.Peer
|
||||
Network *Network
|
||||
Routes []*route.Route
|
||||
DNSConfig nbdns.Config
|
||||
OfflinePeers []*nbpeer.Peer
|
||||
FirewallRules []*FirewallRule
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package peers
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/refactor/settings"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
GetPeerByPubKey(pubKey string) (Peer, error)
|
||||
GetPeerByID(id string) (Peer, error)
|
||||
GetNetworkPeerByID(id string) (Peer, error)
|
||||
GetNetworkPeersInAccount(id string) ([]Peer, error)
|
||||
}
|
||||
|
||||
type DefaultManager struct {
|
||||
repository Repository
|
||||
settingsManager settings.Manager
|
||||
}
|
||||
|
||||
func NewDefaultManager(repository Repository, settingsManager settings.Manager) *DefaultManager {
|
||||
return &DefaultManager{
|
||||
repository: repository,
|
||||
settingsManager: settingsManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (dm *DefaultManager) GetNetworkPeerByID(id string) (Peer, error) {
|
||||
return dm.repository.FindPeerByID(id)
|
||||
}
|
||||
|
||||
func (dm *DefaultManager) GetNetworkPeersInAccount(id string) ([]Peer, error) {
|
||||
defaultPeers, err := dm.repository.FindAllPeersInAccount(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peers := make([]Peer, len(defaultPeers))
|
||||
for _, dp := range defaultPeers {
|
||||
peers = append(peers, dp)
|
||||
}
|
||||
|
||||
return peers, nil
|
||||
}
|
||||
|
||||
func (dm *DefaultManager) GetPeerByPubKey(pubKey string) (Peer, error) {
|
||||
return dm.repository.FindPeerByPubKey(pubKey)
|
||||
}
|
||||
|
||||
func (dm *DefaultManager) GetPeerByID(id string) (Peer, error) {
|
||||
return dm.repository.FindPeerByID(id)
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package peers
|
||||
|
||||
type Repository interface {
|
||||
FindPeerByPubKey(pubKey string) (Peer, error)
|
||||
FindPeerByID(id string) (Peer, error)
|
||||
FindAllPeersInAccount(id string) ([]Peer, error)
|
||||
UpdatePeer(peer Peer) error
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package policies
|
||||
|
||||
type Policy interface {
|
||||
}
|
||||
|
||||
type DefaultPolicy struct {
|
||||
}
|
1
management/refactor/resources/dns/api.go
Normal file
1
management/refactor/resources/dns/api.go
Normal file
@ -0,0 +1 @@
|
||||
package dns
|
1
management/refactor/resources/dns/manager.go
Normal file
1
management/refactor/resources/dns/manager.go
Normal file
@ -0,0 +1 @@
|
||||
package dns
|
1
management/refactor/resources/dns/repository.go
Normal file
1
management/refactor/resources/dns/repository.go
Normal file
@ -0,0 +1 @@
|
||||
package dns
|
11
management/refactor/resources/dns/types/config.go
Normal file
11
management/refactor/resources/dns/types/config.go
Normal file
@ -0,0 +1,11 @@
|
||||
package types
|
||||
|
||||
// Config represents a dns configuration that is exchanged between management and peers
|
||||
type Config struct {
|
||||
// ServiceEnable indicates if the service should be enabled
|
||||
ServiceEnable bool
|
||||
// NameServerGroups contains a list of nameserver group
|
||||
NameServerGroups []*NameServerGroup
|
||||
// CustomZones contains a list of custom zone
|
||||
CustomZones []CustomZone
|
||||
}
|
9
management/refactor/resources/dns/types/custom_zone.go
Normal file
9
management/refactor/resources/dns/types/custom_zone.go
Normal file
@ -0,0 +1,9 @@
|
||||
package types
|
||||
|
||||
// CustomZone represents a custom zone to be resolved by the dns server
|
||||
type CustomZone struct {
|
||||
// Domain is the zone's domain
|
||||
Domain string
|
||||
// Records custom zone records
|
||||
Records []SimpleRecord
|
||||
}
|
75
management/refactor/resources/dns/types/name_server.go
Normal file
75
management/refactor/resources/dns/types/name_server.go
Normal file
@ -0,0 +1,75 @@
|
||||
package types
|
||||
|
||||
import "net/netip"
|
||||
|
||||
// NameServerType nameserver type
|
||||
type NameServerType int
|
||||
|
||||
// NameServer represents a DNS nameserver
|
||||
type NameServer struct {
|
||||
// IP address of nameserver
|
||||
IP netip.Addr
|
||||
// NSType nameserver type
|
||||
NSType NameServerType
|
||||
// Port nameserver listening port
|
||||
Port int
|
||||
}
|
||||
|
||||
// Copy copies a nameserver object
|
||||
func (n *NameServer) Copy() *NameServer {
|
||||
return &NameServer{
|
||||
IP: n.IP,
|
||||
NSType: n.NSType,
|
||||
Port: n.Port,
|
||||
}
|
||||
}
|
||||
|
||||
// IsEqual compares one nameserver with the other
|
||||
func (n *NameServer) IsEqual(other *NameServer) bool {
|
||||
return other.IP == n.IP &&
|
||||
other.NSType == n.NSType &&
|
||||
other.Port == n.Port
|
||||
}
|
||||
|
||||
func compareNameServerList(list, other []NameServer) bool {
|
||||
if len(list) != len(other) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, ns := range list {
|
||||
if !containsNameServer(ns, other) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func containsNameServer(element NameServer, list []NameServer) bool {
|
||||
for _, ns := range list {
|
||||
if ns.IsEqual(&element) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func compareGroupsList(list, other []string) bool {
|
||||
if len(list) != len(other) {
|
||||
return false
|
||||
}
|
||||
for _, id := range list {
|
||||
match := false
|
||||
for _, otherID := range other {
|
||||
if id == otherID {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
65
management/refactor/resources/dns/types/name_server_group.go
Normal file
65
management/refactor/resources/dns/types/name_server_group.go
Normal file
@ -0,0 +1,65 @@
|
||||
package types
|
||||
|
||||
type NameServerGroup interface {
|
||||
}
|
||||
|
||||
type DefaultNameServerGroup struct {
|
||||
// ID identifier of group
|
||||
ID string `gorm:"primaryKey"`
|
||||
// AccountID is a reference to Account that this object belongs
|
||||
AccountID string `gorm:"index"`
|
||||
// Name group name
|
||||
Name string
|
||||
// Description group description
|
||||
Description string
|
||||
// NameServers list of nameservers
|
||||
NameServers []NameServer `gorm:"serializer:json"`
|
||||
// Groups list of peer group IDs to distribute the nameservers information
|
||||
Groups []string `gorm:"serializer:json"`
|
||||
// Primary indicates that the nameserver group is the primary resolver for any dns query
|
||||
Primary bool
|
||||
// Domains indicate the dns query domains to use with this nameserver group
|
||||
Domains []string `gorm:"serializer:json"`
|
||||
// Enabled group status
|
||||
Enabled bool
|
||||
// SearchDomainsEnabled indicates whether to add match domains to search domains list or not
|
||||
SearchDomainsEnabled bool
|
||||
}
|
||||
|
||||
// EventMeta returns activity event meta related to the nameserver group
|
||||
func (g *DefaultNameServerGroup) EventMeta() map[string]any {
|
||||
return map[string]any{"name": g.Name}
|
||||
}
|
||||
|
||||
// Copy copies a nameserver group object
|
||||
func (g *DefaultNameServerGroup) Copy() *DefaultNameServerGroup {
|
||||
nsGroup := &DefaultNameServerGroup{
|
||||
ID: g.ID,
|
||||
Name: g.Name,
|
||||
Description: g.Description,
|
||||
NameServers: make([]NameServer, len(g.NameServers)),
|
||||
Groups: make([]string, len(g.Groups)),
|
||||
Enabled: g.Enabled,
|
||||
Primary: g.Primary,
|
||||
Domains: make([]string, len(g.Domains)),
|
||||
SearchDomainsEnabled: g.SearchDomainsEnabled,
|
||||
}
|
||||
|
||||
copy(nsGroup.NameServers, g.NameServers)
|
||||
copy(nsGroup.Groups, g.Groups)
|
||||
copy(nsGroup.Domains, g.Domains)
|
||||
|
||||
return nsGroup
|
||||
}
|
||||
|
||||
// IsEqual compares one nameserver group with the other
|
||||
func (g *DefaultNameServerGroup) IsEqual(other *DefaultNameServerGroup) bool {
|
||||
return other.ID == g.ID &&
|
||||
other.Name == g.Name &&
|
||||
other.Description == g.Description &&
|
||||
other.Primary == g.Primary &&
|
||||
other.SearchDomainsEnabled == g.SearchDomainsEnabled &&
|
||||
compareNameServerList(g.NameServers, other.NameServers) &&
|
||||
compareGroupsList(g.Groups, other.Groups) &&
|
||||
compareGroupsList(g.Domains, other.Domains)
|
||||
}
|
7
management/refactor/resources/dns/types/settings.go
Normal file
7
management/refactor/resources/dns/types/settings.go
Normal file
@ -0,0 +1,7 @@
|
||||
package types
|
||||
|
||||
type Settings interface {
|
||||
}
|
||||
|
||||
type DefaultSettings struct {
|
||||
}
|
53
management/refactor/resources/dns/types/simpe_record.go
Normal file
53
management/refactor/resources/dns/types/simpe_record.go
Normal file
@ -0,0 +1,53 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// SimpleRecord provides a simple DNS record specification for CNAME, A and AAAA records
|
||||
type SimpleRecord struct {
|
||||
// Name domain name
|
||||
Name string
|
||||
// Type of record, 1 for A, 5 for CNAME, 28 for AAAA. see https://pkg.go.dev/github.com/miekg/dns@v1.1.41#pkg-constants
|
||||
Type int
|
||||
// Class dns class, currently use the DefaultClass for all records
|
||||
Class string
|
||||
// TTL time-to-live for the record
|
||||
TTL int
|
||||
// RData is the actual value resolved in a dns query
|
||||
RData string
|
||||
}
|
||||
|
||||
// String returns a string of the simple record formatted as:
|
||||
// <Name> <TTL> <Class> <Type> <RDATA>
|
||||
func (s SimpleRecord) String() string {
|
||||
fqdn := dns.Fqdn(s.Name)
|
||||
return fmt.Sprintf("%s %d %s %s %s", fqdn, s.TTL, s.Class, dns.Type(s.Type).String(), s.RData)
|
||||
}
|
||||
|
||||
// Len returns the length of the RData field, based on its type
|
||||
func (s SimpleRecord) Len() uint16 {
|
||||
emptyString := s.RData == ""
|
||||
switch s.Type {
|
||||
case 1:
|
||||
if emptyString {
|
||||
return 0
|
||||
}
|
||||
return net.IPv4len
|
||||
case 5:
|
||||
if emptyString || s.RData == "." {
|
||||
return 1
|
||||
}
|
||||
return uint16(len(s.RData) + 1)
|
||||
case 28:
|
||||
if emptyString {
|
||||
return 0
|
||||
}
|
||||
return net.IPv6len
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
1
management/refactor/resources/groups/api.go
Normal file
1
management/refactor/resources/groups/api.go
Normal file
@ -0,0 +1 @@
|
||||
package groups
|
1
management/refactor/resources/groups/manager.go
Normal file
1
management/refactor/resources/groups/manager.go
Normal file
@ -0,0 +1 @@
|
||||
package groups
|
1
management/refactor/resources/groups/repository.go
Normal file
1
management/refactor/resources/groups/repository.go
Normal file
@ -0,0 +1 @@
|
||||
package groups
|
23
management/refactor/resources/groups/types/group.go
Normal file
23
management/refactor/resources/groups/types/group.go
Normal file
@ -0,0 +1,23 @@
|
||||
package types
|
||||
|
||||
type Group interface {
|
||||
}
|
||||
|
||||
type DefaultGroup struct {
|
||||
// ID of the group
|
||||
ID string
|
||||
|
||||
// AccountID is a reference to Account that this object belongs
|
||||
AccountID string `json:"-" gorm:"index"`
|
||||
|
||||
// Name visible in the UI
|
||||
Name string
|
||||
|
||||
// Issued of the group
|
||||
Issued string
|
||||
|
||||
// Peers list of the group
|
||||
Peers []string `gorm:"serializer:json"`
|
||||
|
||||
IntegrationReference IntegrationReference `gorm:"embedded;embeddedPrefix:integration_ref_"`
|
||||
}
|
1
management/refactor/resources/network/api.go
Normal file
1
management/refactor/resources/network/api.go
Normal file
@ -0,0 +1 @@
|
||||
package network
|
19
management/refactor/resources/network/manager.go
Normal file
19
management/refactor/resources/network/manager.go
Normal file
@ -0,0 +1,19 @@
|
||||
package network
|
||||
|
||||
import "github.com/netbirdio/netbird/management/refactor/resources/network/types"
|
||||
|
||||
type Manager interface {
|
||||
GetNetwork(accountID string) (types.Network, error)
|
||||
}
|
||||
|
||||
type DefaultManager struct {
|
||||
}
|
||||
|
||||
func NewDefaultManager() *DefaultManager {
|
||||
return &DefaultManager{}
|
||||
}
|
||||
|
||||
func (d DefaultManager) GetNetwork(accountID string) (types.Network, error) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
1
management/refactor/resources/network/repository.go
Normal file
1
management/refactor/resources/network/repository.go
Normal file
@ -0,0 +1 @@
|
||||
package network
|
70
management/refactor/resources/network/types/network.go
Normal file
70
management/refactor/resources/network/types/network.go
Normal file
@ -0,0 +1,70 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/c-robinson/iplib"
|
||||
"github.com/rs/xid"
|
||||
)
|
||||
|
||||
const (
|
||||
// SubnetSize is a size of the subnet of the global network, e.g. 100.77.0.0/16
|
||||
SubnetSize = 16
|
||||
// NetSize is a global network size 100.64.0.0/10
|
||||
NetSize = 10
|
||||
)
|
||||
|
||||
type Network struct {
|
||||
Identifier string `json:"id"`
|
||||
Net net.IPNet `gorm:"serializer:gob"`
|
||||
Dns string
|
||||
// Serial is an ID that increments by 1 when any change to the network happened (e.g. new peer has been added).
|
||||
// Used to synchronize state to the client apps.
|
||||
Serial uint64
|
||||
|
||||
mu sync.Mutex `json:"-" gorm:"-"`
|
||||
}
|
||||
|
||||
// NewNetwork creates a new Network initializing it with a Serial=0
|
||||
// It takes a random /16 subnet from 100.64.0.0/10 (64 different subnets)
|
||||
func NewNetwork() *Network {
|
||||
|
||||
n := iplib.NewNet4(net.ParseIP("100.64.0.0"), NetSize)
|
||||
sub, _ := n.Subnet(SubnetSize)
|
||||
|
||||
s := rand.NewSource(time.Now().Unix())
|
||||
r := rand.New(s)
|
||||
intn := r.Intn(len(sub))
|
||||
|
||||
return &Network{
|
||||
Identifier: xid.New().String(),
|
||||
Net: sub[intn].IPNet,
|
||||
Dns: "",
|
||||
Serial: 0}
|
||||
}
|
||||
|
||||
// IncSerial increments Serial by 1 reflecting that the network state has been changed
|
||||
func (n *Network) IncSerial() {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
n.Serial++
|
||||
}
|
||||
|
||||
// CurrentSerial returns the Network.Serial of the network (latest state id)
|
||||
func (n *Network) CurrentSerial() uint64 {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
return n.Serial
|
||||
}
|
||||
|
||||
func (n *Network) Copy() *Network {
|
||||
return &Network{
|
||||
Identifier: n.Identifier,
|
||||
Net: n.Net,
|
||||
Dns: n.Dns,
|
||||
Serial: n.Serial,
|
||||
}
|
||||
}
|
18
management/refactor/resources/network/types/network_map.go
Normal file
18
management/refactor/resources/network/types/network_map.go
Normal file
@ -0,0 +1,18 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
peerTypes "github.com/netbirdio/netbird/management/refactor/resources/peers/types"
|
||||
policyTypes "github.com/netbirdio/netbird/management/refactor/resources/policies/types"
|
||||
routeTypes "github.com/netbirdio/netbird/management/refactor/resources/routes/types"
|
||||
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
)
|
||||
|
||||
type NetworkMap struct {
|
||||
Peers []*peerTypes.Peer
|
||||
Network *Network
|
||||
Routes []*routeTypes.Route
|
||||
DNSConfig nbdns.Config
|
||||
OfflinePeers []*peerTypes.Peer
|
||||
FirewallRules []*policyTypes.FirewallRule
|
||||
}
|
317
management/refactor/resources/peers/api.go
Normal file
317
management/refactor/resources/peers/api.go
Normal file
@ -0,0 +1,317 @@
|
||||
package peers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
http2 "github.com/netbirdio/netbird/management/refactor/api/http"
|
||||
peerTypes "github.com/netbirdio/netbird/management/refactor/resources/peers/types"
|
||||
"github.com/netbirdio/netbird/management/refactor/store"
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
)
|
||||
|
||||
func RegisterPeersEndpoints(manager Manager, router *mux.Router) {
|
||||
peersHandler := NewDefaultPeersHandler(manager, apiHandler.AuthCfg)
|
||||
router.HandleFunc("/peers", peersHandler.GetAllPeers).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/peers/{peerId}", peersHandler.HandlePeer).
|
||||
Methods("GET", "PUT", "DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
// DefaultPeersHandler is a handler that returns peers of the account
|
||||
type DefaultPeersHandler struct {
|
||||
peersManager Manager
|
||||
store store.Store
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewDefaultPeersHandler creates a new PeersHandler HTTP handler
|
||||
func NewDefaultPeersHandler(manager Manager, authCfg AuthCfg) *DefaultPeersHandler {
|
||||
return &DefaultPeersHandler{
|
||||
peersManager: manager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *DefaultPeersHandler) checkPeerStatus(peer *peerTypes.Peer) (*peerTypes.Peer, error) {
|
||||
peerToReturn := peer.Copy()
|
||||
if peer.Status.Connected {
|
||||
// Although we have online status in store we do not yet have an updated channel so have to show it as disconnected
|
||||
// This may happen after server restart when not all peers are yet connected
|
||||
if !h.accountManager.HasConnectedChannel(peer.ID) {
|
||||
peerToReturn.Status.Connected = false
|
||||
}
|
||||
}
|
||||
|
||||
return peerToReturn, nil
|
||||
}
|
||||
|
||||
func (h *DefaultPeersHandler) getPeer(account *server.Account, peerID, userID string, w http.ResponseWriter) {
|
||||
peer, err := h.peersManager.GetPeerByID(account.Id, peerID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(err, w)
|
||||
return
|
||||
}
|
||||
|
||||
peerToReturn, err := h.checkPeerStatus(peer)
|
||||
if err != nil {
|
||||
util.WriteError(err, w)
|
||||
return
|
||||
}
|
||||
dnsDomain := h.accountManager.GetDNSDomain()
|
||||
|
||||
groupsInfo := toGroupsInfo(account.Groups, peer.ID)
|
||||
|
||||
netMap := account.GetPeerNetworkMap(peerID, h.accountManager.GetDNSDomain())
|
||||
accessiblePeers := toAccessiblePeers(netMap, dnsDomain)
|
||||
|
||||
util.WriteJSONObject(w, toSinglePeerResponse(peerToReturn, groupsInfo, dnsDomain, accessiblePeers))
|
||||
}
|
||||
|
||||
func (h *DefaultPeersHandler) updatePeer(account *server.Account, user *server.User, peerID string, w http.ResponseWriter, r *http.Request) {
|
||||
req := &api.PeerRequest{}
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
return
|
||||
}
|
||||
|
||||
update := &nbpeer.Peer{ID: peerID, SSHEnabled: req.SshEnabled, Name: req.Name,
|
||||
LoginExpirationEnabled: req.LoginExpirationEnabled}
|
||||
|
||||
if req.ApprovalRequired != nil {
|
||||
update.Status = &nbpeer.PeerStatus{RequiresApproval: *req.ApprovalRequired}
|
||||
}
|
||||
|
||||
peer, err := h.accountManager.UpdatePeer(account.Id, user.Id, update)
|
||||
if err != nil {
|
||||
util.WriteError(err, w)
|
||||
return
|
||||
}
|
||||
dnsDomain := h.accountManager.GetDNSDomain()
|
||||
|
||||
groupMinimumInfo := toGroupsInfo(account.Groups, peer.ID)
|
||||
|
||||
netMap := account.GetPeerNetworkMap(peerID, h.accountManager.GetDNSDomain())
|
||||
accessiblePeers := toAccessiblePeers(netMap, dnsDomain)
|
||||
|
||||
util.WriteJSONObject(w, toSinglePeerResponse(peer, groupMinimumInfo, dnsDomain, accessiblePeers))
|
||||
}
|
||||
|
||||
func (h *DefaultPeersHandler) deletePeer(accountID, userID string, peerID string, w http.ResponseWriter) {
|
||||
err := h.accountManager.DeletePeer(accountID, peerID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(err, w)
|
||||
return
|
||||
}
|
||||
util.WriteJSONObject(w, http2.EmptyObject{})
|
||||
}
|
||||
|
||||
// HandlePeer handles all peer requests for GET, PUT and DELETE operations
|
||||
func (h *DefaultPeersHandler) HandlePeer(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
||||
if err != nil {
|
||||
util.WriteError(err, w)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
peerID := vars["peerId"]
|
||||
if len(peerID) == 0 {
|
||||
util.WriteError(status.Errorf(status.InvalidArgument, "invalid peer ID"), w)
|
||||
return
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodDelete:
|
||||
h.deletePeer(account.Id, user.Id, peerID, w)
|
||||
return
|
||||
case http.MethodPut:
|
||||
h.updatePeer(account, user, peerID, w, r)
|
||||
return
|
||||
case http.MethodGet:
|
||||
h.getPeer(account, peerID, user.Id, w)
|
||||
return
|
||||
default:
|
||||
util.WriteError(status.Errorf(status.NotFound, "unknown METHOD"), w)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllPeers returns a list of all peers associated with a provided account
|
||||
func (h *DefaultPeersHandler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
||||
if err != nil {
|
||||
util.WriteError(err, w)
|
||||
return
|
||||
}
|
||||
|
||||
peers, err := h.accountManager.GetPeers(account.Id, user.Id)
|
||||
if err != nil {
|
||||
util.WriteError(err, w)
|
||||
return
|
||||
}
|
||||
|
||||
dnsDomain := h.accountManager.GetDNSDomain()
|
||||
|
||||
respBody := make([]*api.PeerBatch, 0, len(peers))
|
||||
for _, peer := range peers {
|
||||
peerToReturn, err := h.checkPeerStatus(peer)
|
||||
if err != nil {
|
||||
util.WriteError(err, w)
|
||||
return
|
||||
}
|
||||
groupMinimumInfo := toGroupsInfo(account.Groups, peer.ID)
|
||||
|
||||
accessiblePeerNumbers := h.accessiblePeersNumber(account, peer.ID)
|
||||
|
||||
respBody = append(respBody, toPeerListItemResponse(peerToReturn, groupMinimumInfo, dnsDomain, accessiblePeerNumbers))
|
||||
}
|
||||
util.WriteJSONObject(w, respBody)
|
||||
return
|
||||
default:
|
||||
util.WriteError(status.Errorf(status.NotFound, "unknown METHOD"), w)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *DefaultPeersHandler) accessiblePeersNumber(account *server.Account, peerID string) int {
|
||||
netMap := account.GetPeerNetworkMap(peerID, h.accountManager.GetDNSDomain())
|
||||
return len(netMap.Peers) + len(netMap.OfflinePeers)
|
||||
}
|
||||
|
||||
func toAccessiblePeers(netMap *server.NetworkMap, dnsDomain string) []api.AccessiblePeer {
|
||||
accessiblePeers := make([]api.AccessiblePeer, 0, len(netMap.Peers)+len(netMap.OfflinePeers))
|
||||
for _, p := range netMap.Peers {
|
||||
ap := api.AccessiblePeer{
|
||||
Id: p.ID,
|
||||
Name: p.Name,
|
||||
Ip: p.IP.String(),
|
||||
DnsLabel: fqdn(p, dnsDomain),
|
||||
UserId: p.UserID,
|
||||
}
|
||||
accessiblePeers = append(accessiblePeers, ap)
|
||||
}
|
||||
|
||||
for _, p := range netMap.OfflinePeers {
|
||||
ap := api.AccessiblePeer{
|
||||
Id: p.ID,
|
||||
Name: p.Name,
|
||||
Ip: p.IP.String(),
|
||||
DnsLabel: fqdn(p, dnsDomain),
|
||||
UserId: p.UserID,
|
||||
}
|
||||
accessiblePeers = append(accessiblePeers, ap)
|
||||
}
|
||||
return accessiblePeers
|
||||
}
|
||||
|
||||
func toGroupsInfo(groups map[string]*server.Group, peerID string) []api.GroupMinimum {
|
||||
var groupsInfo []api.GroupMinimum
|
||||
groupsChecked := make(map[string]struct{})
|
||||
for _, group := range groups {
|
||||
_, ok := groupsChecked[group.ID]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
groupsChecked[group.ID] = struct{}{}
|
||||
for _, pk := range group.Peers {
|
||||
if pk == peerID {
|
||||
info := api.GroupMinimum{
|
||||
Id: group.ID,
|
||||
Name: group.Name,
|
||||
PeersCount: len(group.Peers),
|
||||
}
|
||||
groupsInfo = append(groupsInfo, info)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return groupsInfo
|
||||
}
|
||||
|
||||
func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, accessiblePeer []api.AccessiblePeer) *api.Peer {
|
||||
osVersion := peer.Meta.OSVersion
|
||||
if osVersion == "" {
|
||||
osVersion = peer.Meta.Core
|
||||
}
|
||||
|
||||
return &api.Peer{
|
||||
Id: peer.ID,
|
||||
Name: peer.Name,
|
||||
Ip: peer.IP.String(),
|
||||
ConnectionIp: peer.Location.ConnectionIP.String(),
|
||||
Connected: peer.Status.Connected,
|
||||
LastSeen: peer.Status.LastSeen,
|
||||
Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion),
|
||||
KernelVersion: peer.Meta.KernelVersion,
|
||||
GeonameId: int(peer.Location.GeoNameID),
|
||||
Version: peer.Meta.WtVersion,
|
||||
Groups: groupsInfo,
|
||||
SshEnabled: peer.SSHEnabled,
|
||||
Hostname: peer.Meta.Hostname,
|
||||
UserId: peer.UserID,
|
||||
UiVersion: peer.Meta.UIVersion,
|
||||
DnsLabel: fqdn(peer, dnsDomain),
|
||||
LoginExpirationEnabled: peer.LoginExpirationEnabled,
|
||||
LastLogin: peer.LastLogin,
|
||||
LoginExpired: peer.Status.LoginExpired,
|
||||
AccessiblePeers: accessiblePeer,
|
||||
ApprovalRequired: &peer.Status.RequiresApproval,
|
||||
CountryCode: peer.Location.CountryCode,
|
||||
CityName: peer.Location.CityName,
|
||||
}
|
||||
}
|
||||
|
||||
func toPeerListItemResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, accessiblePeersCount int) *api.PeerBatch {
|
||||
osVersion := peer.Meta.OSVersion
|
||||
if osVersion == "" {
|
||||
osVersion = peer.Meta.Core
|
||||
}
|
||||
|
||||
return &api.PeerBatch{
|
||||
Id: peer.ID,
|
||||
Name: peer.Name,
|
||||
Ip: peer.IP.String(),
|
||||
ConnectionIp: peer.Location.ConnectionIP.String(),
|
||||
Connected: peer.Status.Connected,
|
||||
LastSeen: peer.Status.LastSeen,
|
||||
Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion),
|
||||
KernelVersion: peer.Meta.KernelVersion,
|
||||
GeonameId: int(peer.Location.GeoNameID),
|
||||
Version: peer.Meta.WtVersion,
|
||||
Groups: groupsInfo,
|
||||
SshEnabled: peer.SSHEnabled,
|
||||
Hostname: peer.Meta.Hostname,
|
||||
UserId: peer.UserID,
|
||||
UiVersion: peer.Meta.UIVersion,
|
||||
DnsLabel: fqdn(peer, dnsDomain),
|
||||
LoginExpirationEnabled: peer.LoginExpirationEnabled,
|
||||
LastLogin: peer.LastLogin,
|
||||
LoginExpired: peer.Status.LoginExpired,
|
||||
AccessiblePeersCount: accessiblePeersCount,
|
||||
ApprovalRequired: &peer.Status.RequiresApproval,
|
||||
CountryCode: peer.Location.CountryCode,
|
||||
CityName: peer.Location.CityName,
|
||||
}
|
||||
}
|
||||
|
||||
func fqdn(peer *nbpeer.Peer, dnsDomain string) string {
|
||||
fqdn := peer.FQDN(dnsDomain)
|
||||
if fqdn == "" {
|
||||
return peer.DNSLabel
|
||||
} else {
|
||||
return fqdn
|
||||
}
|
||||
}
|
51
management/refactor/resources/peers/manager.go
Normal file
51
management/refactor/resources/peers/manager.go
Normal file
@ -0,0 +1,51 @@
|
||||
package peers
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/peers/types"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/settings"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
GetPeerByPubKey(pubKey string) (types.Peer, error)
|
||||
GetPeerByID(id string) (types.Peer, error)
|
||||
GetNetworkPeerByID(id string) (types.Peer, error)
|
||||
GetNetworkPeersInAccount(id string) ([]types.Peer, error)
|
||||
}
|
||||
|
||||
type DefaultManager struct {
|
||||
repository Repository
|
||||
settingsManager settings.Manager
|
||||
}
|
||||
|
||||
func NewDefaultManager(repository Repository, settingsManager settings.Manager) *DefaultManager {
|
||||
return &DefaultManager{
|
||||
repository: repository,
|
||||
settingsManager: settingsManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (dm *DefaultManager) GetNetworkPeerByID(id string) (types.Peer, error) {
|
||||
return dm.repository.FindPeerByID(id)
|
||||
}
|
||||
|
||||
func (dm *DefaultManager) GetNetworkPeersInAccount(accountId string) ([]types.Peer, error) {
|
||||
defaultPeers, err := dm.repository.FindAllPeersInAccount(accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peers := make([]types.Peer, len(defaultPeers))
|
||||
for _, dp := range defaultPeers {
|
||||
peers = append(peers, dp)
|
||||
}
|
||||
|
||||
return peers, nil
|
||||
}
|
||||
|
||||
func (dm *DefaultManager) GetPeerByPubKey(pubKey string) (types.Peer, error) {
|
||||
return dm.repository.FindPeerByPubKey(pubKey)
|
||||
}
|
||||
|
||||
func (dm *DefaultManager) GetPeerByID(id string) (types.Peer, error) {
|
||||
return dm.repository.FindPeerByID(id)
|
||||
}
|
10
management/refactor/resources/peers/repository.go
Normal file
10
management/refactor/resources/peers/repository.go
Normal file
@ -0,0 +1,10 @@
|
||||
package peers
|
||||
|
||||
import "github.com/netbirdio/netbird/management/refactor/resources/peers/types"
|
||||
|
||||
type Repository interface {
|
||||
FindPeerByPubKey(pubKey string) (types.Peer, error)
|
||||
FindPeerByID(id string) (types.Peer, error)
|
||||
FindAllPeersInAccount(id string) ([]types.Peer, error)
|
||||
UpdatePeer(peer types.Peer) error
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package peers
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -33,6 +33,7 @@ type Peer interface {
|
||||
FQDN(dnsDomain string) string
|
||||
EventMeta(dnsDomain string) map[string]any
|
||||
LoginExpired(expiresIn time.Duration) (bool, time.Duration)
|
||||
Copy() Peer
|
||||
}
|
||||
|
||||
// Peer represents a machine connected to the network.
|
||||
@ -48,11 +49,15 @@ type DefaultPeer struct {
|
||||
SetupKey string
|
||||
// IP address of the Peer
|
||||
IP net.IP `gorm:"uniqueIndex:idx_peers_account_id_ip"`
|
||||
// Meta is a Peer system meta data
|
||||
Meta PeerSystemMeta `gorm:"embedded;embeddedPrefix:meta_"`
|
||||
// Name is peer's name (machine name)
|
||||
Name string
|
||||
// DNSLabel is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's
|
||||
// domain to the peer label. e.g. peer-dns-label.netbird.cloud
|
||||
DNSLabel string
|
||||
// Status peer's management connection status
|
||||
Status *PeerStatus `gorm:"embedded;embeddedPrefix:peer_status_"`
|
||||
// The user ID that registered the peer
|
||||
UserID string
|
||||
// SSHKey is a public SSH key of the peer
|
||||
@ -64,8 +69,20 @@ type DefaultPeer struct {
|
||||
LoginExpirationEnabled bool
|
||||
// LastLogin the time when peer performed last login operation
|
||||
LastLogin time.Time
|
||||
// CreatedAt records the time the peer was created
|
||||
CreatedAt time.Time
|
||||
// Indicate ephemeral peer attribute
|
||||
Ephemeral bool
|
||||
// Geo location based on connection IP
|
||||
Location Location `gorm:"embedded;embeddedPrefix:location_"`
|
||||
}
|
||||
|
||||
// Location is a geo location information of a Peer based on public connection IP
|
||||
type Location struct {
|
||||
ConnectionIP net.IP // from grpc peer or reverse proxy headers depends on setup
|
||||
CountryCode string
|
||||
CityName string
|
||||
GeoNameID uint // city level geoname id
|
||||
}
|
||||
|
||||
// PeerLogin used as a data object between the gRPC API and AccountManager on Login request.
|
24
management/refactor/resources/peers/types/peer_status.go
Normal file
24
management/refactor/resources/peers/types/peer_status.go
Normal file
@ -0,0 +1,24 @@
|
||||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
// Copy PeerStatus
|
||||
func (p *PeerStatus) Copy() *PeerStatus {
|
||||
return &PeerStatus{
|
||||
LastSeen: p.LastSeen,
|
||||
Connected: p.Connected,
|
||||
LoginExpired: p.LoginExpired,
|
||||
RequiresApproval: p.RequiresApproval,
|
||||
}
|
||||
}
|
||||
|
||||
type PeerStatus struct { //nolint:revive
|
||||
// LastSeen is the last time peer was connected to the management service
|
||||
LastSeen time.Time
|
||||
// Connected indicates whether peer is connected to the management service or not
|
||||
Connected bool
|
||||
// LoginExpired
|
||||
LoginExpired bool
|
||||
// RequiresApproval indicates whether peer requires approval or not
|
||||
RequiresApproval bool
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package types
|
||||
|
||||
import "net/netip"
|
||||
|
||||
// NetworkAddress is the IP address with network and MAC address of a network interface
|
||||
type NetworkAddress struct {
|
||||
NetIP netip.Prefix `gorm:"serializer:json"`
|
||||
Mac string
|
||||
}
|
||||
|
||||
// Environment is a system environment information
|
||||
type Environment struct {
|
||||
Cloud string
|
||||
Platform string
|
||||
}
|
||||
|
||||
// PeerSystemMeta is a metadata of a Peer machine system
|
||||
type PeerSystemMeta struct { //nolint:revive
|
||||
Hostname string
|
||||
GoOS string
|
||||
Kernel string
|
||||
Core string
|
||||
Platform string
|
||||
OS string
|
||||
OSVersion string
|
||||
WtVersion string
|
||||
UIVersion string
|
||||
KernelVersion string
|
||||
NetworkAddresses []NetworkAddress `gorm:"serializer:json"`
|
||||
SystemSerialNumber string
|
||||
SystemProductName string
|
||||
SystemManufacturer string
|
||||
Environment Environment `gorm:"serializer:json"`
|
||||
}
|
||||
|
||||
func (p PeerSystemMeta) isEqual(other PeerSystemMeta) bool {
|
||||
if len(p.NetworkAddresses) != len(other.NetworkAddresses) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, addr := range p.NetworkAddresses {
|
||||
var found bool
|
||||
for _, oAddr := range other.NetworkAddresses {
|
||||
if addr.Mac == oAddr.Mac && addr.NetIP == oAddr.NetIP {
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return p.Hostname == other.Hostname &&
|
||||
p.GoOS == other.GoOS &&
|
||||
p.Kernel == other.Kernel &&
|
||||
p.KernelVersion == other.KernelVersion &&
|
||||
p.Core == other.Core &&
|
||||
p.Platform == other.Platform &&
|
||||
p.OS == other.OS &&
|
||||
p.OSVersion == other.OSVersion &&
|
||||
p.WtVersion == other.WtVersion &&
|
||||
p.UIVersion == other.UIVersion &&
|
||||
p.SystemSerialNumber == other.SystemSerialNumber &&
|
||||
p.SystemProductName == other.SystemProductName &&
|
||||
p.SystemManufacturer == other.SystemManufacturer &&
|
||||
p.Environment.Cloud == other.Environment.Cloud &&
|
||||
p.Environment.Platform == other.Environment.Platform
|
||||
}
|
1
management/refactor/resources/policies/api.go
Normal file
1
management/refactor/resources/policies/api.go
Normal file
@ -0,0 +1 @@
|
||||
package policies
|
@ -1,24 +1,27 @@
|
||||
package policies
|
||||
|
||||
import "github.com/netbirdio/netbird/management/refactor/peers"
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/peers"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/peers/types"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
GetAccessiblePeersAndFirewallRules(peerID string) (peers []peers.Peer, firewallRules []*FirewallRule)
|
||||
GetAccessiblePeersAndFirewallRules(peerID string) (peers []types.Peer, firewallRules []*FirewallRule)
|
||||
}
|
||||
|
||||
type DefaultManager struct {
|
||||
repository repository
|
||||
repository Repository
|
||||
peerManager peers.Manager
|
||||
}
|
||||
|
||||
func NewDefaultManager(repository repository, peerManager peers.Manager) *DefaultManager {
|
||||
func NewDefaultManager(repository Repository, peerManager peers.Manager) *DefaultManager {
|
||||
return &DefaultManager{
|
||||
repository: repository,
|
||||
peerManager: peerManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (dm *DefaultManager) GetAccessiblePeersAndFirewallRules(peerID string) (peers []peers.Peer, firewallRules []*FirewallRule) {
|
||||
func (dm *DefaultManager) GetAccessiblePeersAndFirewallRules(peerID string) (peers []types.Peer, firewallRules []*FirewallRule) {
|
||||
peer, err := dm.peerManager.GetPeerByID(peerID)
|
||||
if err != nil {
|
||||
return nil, nil
|
1
management/refactor/resources/policies/posture/api.go
Normal file
1
management/refactor/resources/policies/posture/api.go
Normal file
@ -0,0 +1 @@
|
||||
package posture
|
@ -0,0 +1 @@
|
||||
package posture
|
@ -0,0 +1 @@
|
||||
package posture
|
@ -0,0 +1,19 @@
|
||||
package types
|
||||
|
||||
// FirewallRule is a rule of the firewall.
|
||||
type FirewallRule struct {
|
||||
// PeerIP of the peer
|
||||
PeerIP string
|
||||
|
||||
// Direction of the traffic
|
||||
Direction int
|
||||
|
||||
// Action of the traffic
|
||||
Action string
|
||||
|
||||
// Protocol of the traffic
|
||||
Protocol string
|
||||
|
||||
// Port of the traffic
|
||||
Port string
|
||||
}
|
13
management/refactor/resources/policies/types/policy.go
Normal file
13
management/refactor/resources/policies/types/policy.go
Normal file
@ -0,0 +1,13 @@
|
||||
package types
|
||||
|
||||
type Policy interface {
|
||||
GetID() string
|
||||
}
|
||||
|
||||
type DefaultPolicy struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (dp *DefaultPolicy) GetID() string {
|
||||
return dp.ID
|
||||
}
|
13
management/refactor/resources/policies/types/policy_rule.go
Normal file
13
management/refactor/resources/policies/types/policy_rule.go
Normal file
@ -0,0 +1,13 @@
|
||||
package types
|
||||
|
||||
type PolicyRule interface {
|
||||
GetID() string
|
||||
}
|
||||
|
||||
type DefaultPolicyRule struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (dpr *DefaultPolicyRule) GetID() string {
|
||||
return dpr.ID
|
||||
}
|
1
management/refactor/resources/routes/api.go
Normal file
1
management/refactor/resources/routes/api.go
Normal file
@ -0,0 +1 @@
|
||||
package routes
|
100
management/refactor/resources/routes/manager.go
Normal file
100
management/refactor/resources/routes/manager.go
Normal file
@ -0,0 +1,100 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/peers"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/peers/types"
|
||||
routeTypes "github.com/netbirdio/netbird/management/refactor/resources/routes/types"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
GetRoutesToSync(peerID string, peersToConnect []*types.Peer, accountID string) []*routeTypes.Route
|
||||
}
|
||||
|
||||
type DefaultManager struct {
|
||||
repository Repository
|
||||
peersManager peers.Manager
|
||||
}
|
||||
|
||||
func NewDefaultManager(repository Repository, peersManager peers.Manager) *DefaultManager {
|
||||
return &DefaultManager{
|
||||
repository: repository,
|
||||
peersManager: peersManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (d DefaultManager) GetRoutesToSync(peerID string, peersToConnect []*types.Peer) []*routeTypes.Route {
|
||||
routes, peerDisabledRoutes := d.getRoutingPeerRoutes(peerID)
|
||||
peerRoutesMembership := make(lookupMap)
|
||||
for _, r := range append(routes, peerDisabledRoutes...) {
|
||||
peerRoutesMembership[route.GetHAUniqueID(r)] = struct{}{}
|
||||
}
|
||||
|
||||
groupListMap := a.getPeerGroups(peerID)
|
||||
for _, peer := range aclPeers {
|
||||
activeRoutes, _ := a.getRoutingPeerRoutes(peer.ID)
|
||||
groupFilteredRoutes := a.filterRoutesByGroups(activeRoutes, groupListMap)
|
||||
filteredRoutes := a.filterRoutesFromPeersOfSameHAGroup(groupFilteredRoutes, peerRoutesMembership)
|
||||
routes = append(routes, filteredRoutes...)
|
||||
}
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
func (d DefaultManager) getRoutingPeerRoutes(accountID, peerID string) (enabledRoutes []routeTypes.Route, disabledRoutes []routeTypes.Route) {
|
||||
peer, err := d.peersManager.GetPeerByID(peerID)
|
||||
if err != nil {
|
||||
log.Errorf("peer %s that doesn't exist under account %s", peerID, accountID)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// currently we support only linux routing peers
|
||||
if peer.Meta.GoOS != "linux" {
|
||||
return enabledRoutes, disabledRoutes
|
||||
}
|
||||
|
||||
seenRoute := make(map[string]struct{})
|
||||
|
||||
takeRoute := func(r routeTypes.Route, id string) {
|
||||
if _, ok := seenRoute[r.GetID()]; ok {
|
||||
return
|
||||
}
|
||||
seenRoute[r.GetID()] = struct{}{}
|
||||
|
||||
if r.IsEnabled() {
|
||||
r.SetPeer(peer.GetKey())
|
||||
enabledRoutes = append(enabledRoutes, r)
|
||||
return
|
||||
}
|
||||
disabledRoutes = append(disabledRoutes, r)
|
||||
}
|
||||
|
||||
for _, r := range a.Routes {
|
||||
for _, groupID := range r.PeerGroups {
|
||||
group := a.GetGroup(groupID)
|
||||
if group == nil {
|
||||
log.Errorf("route %s has peers group %s that doesn't exist under account %s", r.ID, groupID, a.Id)
|
||||
continue
|
||||
}
|
||||
for _, id := range group.Peers {
|
||||
if id != peerID {
|
||||
continue
|
||||
}
|
||||
|
||||
newPeerRoute := r.Copy()
|
||||
newPeerRoute.Peer = id
|
||||
newPeerRoute.PeerGroups = nil
|
||||
newPeerRoute.ID = r.ID + ":" + id // we have to provide unique route id when distribute network map
|
||||
takeRoute(newPeerRoute, id)
|
||||
break
|
||||
}
|
||||
}
|
||||
if r.Peer == peerID {
|
||||
takeRoute(r.Copy(), peerID)
|
||||
}
|
||||
}
|
||||
|
||||
return enabledRoutes, disabledRoutes
|
||||
}
|
4
management/refactor/resources/routes/repository.go
Normal file
4
management/refactor/resources/routes/repository.go
Normal file
@ -0,0 +1,4 @@
|
||||
package routes
|
||||
|
||||
type Repository interface {
|
||||
}
|
54
management/refactor/resources/routes/types/route.go
Normal file
54
management/refactor/resources/routes/types/route.go
Normal file
@ -0,0 +1,54 @@
|
||||
package types
|
||||
|
||||
import "net/netip"
|
||||
|
||||
const (
|
||||
// InvalidNetwork invalid network type
|
||||
InvalidNetwork NetworkType = iota
|
||||
// IPv4Network IPv4 network type
|
||||
IPv4Network
|
||||
// IPv6Network IPv6 network type
|
||||
IPv6Network
|
||||
)
|
||||
|
||||
// NetworkType route network type
|
||||
type NetworkType int
|
||||
|
||||
type Route interface {
|
||||
GetID() string
|
||||
IsEnabled() bool
|
||||
GetPeer() string
|
||||
SetPeer(string)
|
||||
}
|
||||
|
||||
type DefaultRoute struct {
|
||||
ID string `gorm:"primaryKey"`
|
||||
// AccountID is a reference to Account that this object belongs
|
||||
AccountID string `gorm:"index"`
|
||||
Network netip.Prefix `gorm:"serializer:gob"`
|
||||
NetID string
|
||||
Description string
|
||||
Peer string
|
||||
PeerGroups []string `gorm:"serializer:gob"`
|
||||
NetworkType NetworkType
|
||||
Masquerade bool
|
||||
Metric int
|
||||
Enabled bool
|
||||
Groups []string `gorm:"serializer:json"`
|
||||
}
|
||||
|
||||
func (r *DefaultRoute) GetID() string {
|
||||
return r.ID
|
||||
}
|
||||
|
||||
func (r *DefaultRoute) IsEnabled() bool {
|
||||
return r.Enabled
|
||||
}
|
||||
|
||||
func (r *DefaultRoute) GetPeer() string {
|
||||
return r.Peer
|
||||
}
|
||||
|
||||
func (r *DefaultRoute) SetPeer(peer string) {
|
||||
r.Peer = peer
|
||||
}
|
1
management/refactor/resources/settings/api.go
Normal file
1
management/refactor/resources/settings/api.go
Normal file
@ -0,0 +1 @@
|
||||
package settings
|
21
management/refactor/resources/settings/manager.go
Normal file
21
management/refactor/resources/settings/manager.go
Normal file
@ -0,0 +1,21 @@
|
||||
package settings
|
||||
|
||||
import "github.com/netbirdio/netbird/management/refactor/resources/settings/types"
|
||||
|
||||
type Manager interface {
|
||||
GetSettings(accountID string) (types.Settings, error)
|
||||
}
|
||||
|
||||
type DefaultManager struct {
|
||||
repository Repository
|
||||
}
|
||||
|
||||
func NewDefaultManager(repository Repository) *DefaultManager {
|
||||
return &DefaultManager{
|
||||
repository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
func (dm *DefaultManager) GetSettings(accountID string) (types.Settings, error) {
|
||||
return dm.repository.FindSettings(accountID)
|
||||
}
|
7
management/refactor/resources/settings/repository.go
Normal file
7
management/refactor/resources/settings/repository.go
Normal file
@ -0,0 +1,7 @@
|
||||
package settings
|
||||
|
||||
import "github.com/netbirdio/netbird/management/refactor/resources/settings/types"
|
||||
|
||||
type Repository interface {
|
||||
FindSettings(accountID string) (types.Settings, error)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package settings
|
||||
package types
|
||||
|
||||
import "time"
|
||||
|
1
management/refactor/resources/setup_keys/api.go
Normal file
1
management/refactor/resources/setup_keys/api.go
Normal file
@ -0,0 +1 @@
|
||||
package setup_keys
|
1
management/refactor/resources/setup_keys/manager.go
Normal file
1
management/refactor/resources/setup_keys/manager.go
Normal file
@ -0,0 +1 @@
|
||||
package setup_keys
|
1
management/refactor/resources/setup_keys/repository.go
Normal file
1
management/refactor/resources/setup_keys/repository.go
Normal file
@ -0,0 +1 @@
|
||||
package setup_keys
|
@ -0,0 +1,7 @@
|
||||
package types
|
||||
|
||||
type SetupKey interface {
|
||||
}
|
||||
|
||||
type DefaultSetupKey struct {
|
||||
}
|
1
management/refactor/resources/users/api.go
Normal file
1
management/refactor/resources/users/api.go
Normal file
@ -0,0 +1 @@
|
||||
package users
|
27
management/refactor/resources/users/manager.go
Normal file
27
management/refactor/resources/users/manager.go
Normal file
@ -0,0 +1,27 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/peers"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/users/types"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
GetUser(id string) (types.User, error)
|
||||
}
|
||||
|
||||
type DefaultManager struct {
|
||||
repository Repository
|
||||
peerManager peers.Manager
|
||||
}
|
||||
|
||||
func NewDefaultManager(repository Repository, peerManager peers.Manager) *DefaultManager {
|
||||
return &DefaultManager{
|
||||
repository: repository,
|
||||
peerManager: peerManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (d DefaultManager) GetUser(id string) (types.User, error) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
@ -0,0 +1 @@
|
||||
package personal_access_tokens
|
@ -0,0 +1 @@
|
||||
package personal_access_tokens
|
@ -0,0 +1 @@
|
||||
package personal_access_tokens
|
@ -0,0 +1 @@
|
||||
package types
|
@ -1,4 +1,4 @@
|
||||
package users
|
||||
package types
|
||||
|
||||
import "time"
|
||||
|
@ -1,19 +0,0 @@
|
||||
package settings
|
||||
|
||||
type Manager interface {
|
||||
GetSettings(accountID string) (Settings, error)
|
||||
}
|
||||
|
||||
type DefaultManager struct {
|
||||
repository repository
|
||||
}
|
||||
|
||||
func NewDefaultManager(repository repository) *DefaultManager {
|
||||
return &DefaultManager{
|
||||
repository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
func (dm *DefaultManager) GetSettings(accountID string) (Settings, error) {
|
||||
return dm.repository.FindSettings(accountID)
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package settings
|
||||
|
||||
type Repository interface {
|
||||
FindSettings(accountID string) (Settings, error)
|
||||
}
|
50
management/refactor/store/account.go
Normal file
50
management/refactor/store/account.go
Normal file
@ -0,0 +1,50 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
dnsTypes "github.com/netbirdio/netbird/management/refactor/resources/dns/types"
|
||||
groupTypes "github.com/netbirdio/netbird/management/refactor/resources/groups/types"
|
||||
networkTypes "github.com/netbirdio/netbird/management/refactor/resources/network/types"
|
||||
peerTypes "github.com/netbirdio/netbird/management/refactor/resources/peers/types"
|
||||
policyTypes "github.com/netbirdio/netbird/management/refactor/resources/policies/types"
|
||||
routeTypes "github.com/netbirdio/netbird/management/refactor/resources/routes/types"
|
||||
settingsTypes "github.com/netbirdio/netbird/management/refactor/resources/settings/types"
|
||||
setupKeyTypes "github.com/netbirdio/netbird/management/refactor/resources/setup_keys/types"
|
||||
userTypes "github.com/netbirdio/netbird/management/refactor/resources/users/types"
|
||||
"github.com/netbirdio/netbird/management/server/posture"
|
||||
)
|
||||
|
||||
// Account represents a unique account of the system
|
||||
type DefaultAccount struct {
|
||||
// we have to name column to aid as it collides with Network.Id when work with associations
|
||||
Id string `gorm:"primaryKey"`
|
||||
|
||||
// User.Id it was created by
|
||||
CreatedBy string
|
||||
CreatedAt time.Time
|
||||
Domain string `gorm:"index"`
|
||||
DomainCategory string
|
||||
IsDomainPrimaryAccount bool
|
||||
SetupKeys map[string]*setupKeyTypes.DefaultSetupKey `gorm:"-"`
|
||||
SetupKeysG []setupKeyTypes.DefaultSetupKey `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||
Network *networkTypes.Network `gorm:"embedded;embeddedPrefix:network_"`
|
||||
Peers map[string]*peerTypes.DefaultPeer `gorm:"-"`
|
||||
PeersG []peerTypes.DefaultPeer `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||
Users map[string]*userTypes.DefaultUser `gorm:"-"`
|
||||
UsersG []userTypes.DefaultUser `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||
Groups map[string]*groupTypes.DefaultGroup `gorm:"-"`
|
||||
GroupsG []groupTypes.DefaultGroup `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||
Policies []*policyTypes.DefaultPolicy `gorm:"foreignKey:AccountID;references:id"`
|
||||
Routes map[string]*routeTypes.DefaultRoute `gorm:"-"`
|
||||
RoutesG []routeTypes.DefaultRoute `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||
NameServerGroups map[string]*dnsTypes.DefaultNameServerGroup `gorm:"-"`
|
||||
NameServerGroupsG []dnsTypes.DefaultNameServerGroup `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||
DNSSettings dnsTypes.DefaultSettings `gorm:"embedded;embeddedPrefix:dns_settings_"`
|
||||
PostureChecks []*posture.Checks `gorm:"foreignKey:AccountID;references:id"`
|
||||
// Settings is a dictionary of Account settings
|
||||
Settings *settingsTypes.DefaultSettings `gorm:"embedded;embeddedPrefix:settings_"`
|
||||
// deprecated on store and api level
|
||||
Rules map[string]*Rule `json:"-" gorm:"-"`
|
||||
RulesG []Rule `json:"-" gorm:"-"`
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/refactor/peers"
|
||||
"github.com/netbirdio/netbird/management/refactor/settings"
|
||||
peerTypes "github.com/netbirdio/netbird/management/refactor/resources/peers/types"
|
||||
settingsTypes "github.com/netbirdio/netbird/management/refactor/resources/settings/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -12,27 +12,27 @@ const (
|
||||
type DefaultPostgresStore struct {
|
||||
}
|
||||
|
||||
func (s *DefaultPostgresStore) FindSettings(accountID string) (settings.Settings, error) {
|
||||
func (s *DefaultPostgresStore) FindSettings(accountID string) (settingsTypes.Settings, error) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *DefaultPostgresStore) FindPeerByPubKey(pubKey string) (peers.Peer, error) {
|
||||
func (s *DefaultPostgresStore) FindPeerByPubKey(pubKey string) (peerTypes.Peer, error) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *DefaultPostgresStore) FindPeerByID(id string) (peers.Peer, error) {
|
||||
func (s *DefaultPostgresStore) FindPeerByID(id string) (peerTypes.Peer, error) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *DefaultPostgresStore) FindAllPeersInAccount(id string) ([]peers.Peer, error) {
|
||||
func (s *DefaultPostgresStore) FindAllPeersInAccount(id string) ([]peerTypes.Peer, error) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *DefaultPostgresStore) UpdatePeer(peer peers.Peer) error {
|
||||
func (s *DefaultPostgresStore) UpdatePeer(peer peerTypes.Peer) error {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
@ -12,8 +13,17 @@ import (
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/logger"
|
||||
|
||||
"github.com/netbirdio/netbird/management/refactor/peers"
|
||||
"github.com/netbirdio/netbird/management/refactor/settings"
|
||||
dnsTypes "github.com/netbirdio/netbird/management/refactor/resources/dns/types"
|
||||
groupTypes "github.com/netbirdio/netbird/management/refactor/resources/groups/types"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/peers"
|
||||
policyTypes "github.com/netbirdio/netbird/management/refactor/resources/policies/types"
|
||||
routeTypes "github.com/netbirdio/netbird/management/refactor/resources/routes/types"
|
||||
"github.com/netbirdio/netbird/management/refactor/resources/settings"
|
||||
setupKeyTypes "github.com/netbirdio/netbird/management/refactor/resources/setup_keys/types"
|
||||
userTypes "github.com/netbirdio/netbird/management/refactor/resources/users/types"
|
||||
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||
)
|
||||
|
||||
@ -23,37 +33,13 @@ const (
|
||||
|
||||
// SqliteStore represents an account storage backed by a Sqlite DB persisted to disk
|
||||
type DefaultSqliteStore struct {
|
||||
db *gorm.DB
|
||||
DB *gorm.DB
|
||||
storeFile string
|
||||
accountLocks sync.Map
|
||||
globalAccountLock sync.Mutex
|
||||
metrics telemetry.AppMetrics
|
||||
installationPK int
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) FindSettings(accountID string) (settings.Settings, error) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) FindPeerByPubKey(pubKey string) (peers.Peer, error) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) FindPeerByID(id string) (peers.Peer, error) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) FindAllPeersInAccount(id string) ([]peers.Peer, error) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) UpdatePeer(peer peers.Peer) error {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
accounts map[string]*DefaultAccount
|
||||
}
|
||||
|
||||
type installation struct {
|
||||
@ -63,10 +49,10 @@ type installation struct {
|
||||
|
||||
// NewSqliteStore restores a store from the file located in the datadir
|
||||
func NewDefaultSqliteStore(dataDir string, metrics telemetry.AppMetrics) (*DefaultSqliteStore, error) {
|
||||
storeStr := "store.db?cache=shared"
|
||||
storeStr := "store.DB?cache=shared"
|
||||
if runtime.GOOS == "windows" {
|
||||
// Vo avoid `The process cannot access the file because it is being used by another process` on Windows
|
||||
storeStr = "store.db"
|
||||
storeStr = "store.DB"
|
||||
}
|
||||
|
||||
file := filepath.Join(dataDir, storeStr)
|
||||
@ -85,7 +71,7 @@ func NewDefaultSqliteStore(dataDir string, metrics telemetry.AppMetrics) (*Defau
|
||||
conns := runtime.NumCPU()
|
||||
sql.SetMaxOpenConns(conns) // TODO: make it configurable
|
||||
|
||||
// err = db.AutoMigrate(
|
||||
// err = DB.AutoMigrate(
|
||||
// &SetupKey{}, &Peer{}, &User{}, &PersonalAccessToken{}, &Group{}, &Rule{},
|
||||
// &Account{}, &Policy{}, &PolicyRule{}, &route.Route{}, &nbdns.NameServerGroup{},
|
||||
// &installation{},
|
||||
@ -94,12 +80,7 @@ func NewDefaultSqliteStore(dataDir string, metrics telemetry.AppMetrics) (*Defau
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &DefaultSqliteStore{db: db, storeFile: file, metrics: metrics, installationPK: 1}, nil
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) GetLicense() string {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
return &DefaultSqliteStore{DB: db, storeFile: file, metrics: metrics, installationPK: 1}, nil
|
||||
}
|
||||
|
||||
// AcquireGlobalLock acquires global lock across all the accounts and returns a function that releases the lock
|
||||
@ -138,17 +119,161 @@ func (s *DefaultSqliteStore) AcquireAccountLock(accountID string) (unlock func()
|
||||
return unlock
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) LoadAccount(accountID string) error {
|
||||
var account DefaultAccount
|
||||
result := s.DB.Model(&account).
|
||||
Preload("UsersG.PATsG"). // have to be specifies as this is nester reference
|
||||
Preload(clause.Associations).
|
||||
First(&account, "id = ?", accountID)
|
||||
if result.Error != nil {
|
||||
log.Errorf("error when getting account from the store: %s", result.Error)
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return status.Errorf(status.NotFound, "account not found")
|
||||
}
|
||||
return status.Errorf(status.Internal, "issue getting account from store")
|
||||
}
|
||||
|
||||
// we have to manually preload policy rules as it seems that gorm preloading doesn't do it for us
|
||||
for i, policy := range account.Policies {
|
||||
var rules []*policyTypes.DefaultPolicyRule
|
||||
err := s.DB.Model(&policyTypes.DefaultPolicyRule{}).Find(&rules, "policy_id = ?", policy.ID).Error
|
||||
if err != nil {
|
||||
return status.Errorf(status.NotFound, "rule not found")
|
||||
}
|
||||
account.Policies[i].Rules = rules
|
||||
}
|
||||
|
||||
account.SetupKeys = make(map[string]*setupKeyTypes.DefaultSetupKey, len(account.SetupKeysG))
|
||||
for _, key := range account.SetupKeysG {
|
||||
account.SetupKeys[key.Key] = key.Copy()
|
||||
}
|
||||
account.SetupKeysG = nil
|
||||
|
||||
account.Peers = make(map[string]*nbpeer.Peer, len(account.PeersG))
|
||||
for _, peer := range account.PeersG {
|
||||
account.Peers[peer.ID] = peer.Copy()
|
||||
}
|
||||
account.PeersG = nil
|
||||
|
||||
account.Users = make(map[string]*userTypes.DefaultUser, len(account.UsersG))
|
||||
for _, user := range account.UsersG {
|
||||
user.PATs = make(map[string]*PersonalAccessToken, len(user.PATs))
|
||||
for _, pat := range user.PATsG {
|
||||
user.PATs[pat.ID] = pat.Copy()
|
||||
}
|
||||
account.Users[user.Id] = user.Copy()
|
||||
}
|
||||
account.UsersG = nil
|
||||
|
||||
account.Groups = make(map[string]*groupTypes.DefaultGroup, len(account.GroupsG))
|
||||
for _, group := range account.GroupsG {
|
||||
account.Groups[group.ID] = group.Copy()
|
||||
}
|
||||
account.GroupsG = nil
|
||||
|
||||
account.Routes = make(map[string]*routeTypes.DefaultRoute, len(account.RoutesG))
|
||||
for _, route := range account.RoutesG {
|
||||
account.Routes[route.ID] = route.Copy()
|
||||
}
|
||||
account.RoutesG = nil
|
||||
|
||||
account.NameServerGroups = make(map[string]*dnsTypes.DefaultNameServerGroup, len(account.NameServerGroupsG))
|
||||
for _, ns := range account.NameServerGroupsG {
|
||||
account.NameServerGroups[ns.ID] = ns.Copy()
|
||||
}
|
||||
account.NameServerGroupsG = nil
|
||||
|
||||
s.accounts[account.Id] = &account
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) WriteAccount(accountID string) error {
|
||||
start := time.Now()
|
||||
|
||||
account, ok := s.accounts[accountID]
|
||||
if !ok {
|
||||
return status.Errorf(status.NotFound, "account not found")
|
||||
}
|
||||
|
||||
for _, key := range account.SetupKeys {
|
||||
account.SetupKeysG = append(account.SetupKeysG, *key)
|
||||
}
|
||||
|
||||
for id, peer := range account.Peers {
|
||||
peer.ID = id
|
||||
account.PeersG = append(account.PeersG, *peer)
|
||||
}
|
||||
|
||||
for id, user := range account.Users {
|
||||
user.Id = id
|
||||
for id, pat := range user.PATs {
|
||||
pat.ID = id
|
||||
user.PATsG = append(user.PATsG, *pat)
|
||||
}
|
||||
account.UsersG = append(account.UsersG, *user)
|
||||
}
|
||||
|
||||
for id, group := range account.Groups {
|
||||
group.ID = id
|
||||
account.GroupsG = append(account.GroupsG, *group)
|
||||
}
|
||||
|
||||
for id, route := range account.Routes {
|
||||
route.ID = id
|
||||
account.RoutesG = append(account.RoutesG, *route)
|
||||
}
|
||||
|
||||
for id, ns := range account.NameServerGroups {
|
||||
ns.ID = id
|
||||
account.NameServerGroupsG = append(account.NameServerGroupsG, *ns)
|
||||
}
|
||||
|
||||
err := s.DB.Transaction(func(tx *gorm.DB) error {
|
||||
result := tx.Select(clause.Associations).Delete(account.Policies, "account_id = ?", account.Id)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
result = tx.Select(clause.Associations).Delete(account.UsersG, "account_id = ?", account.Id)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
result = tx.Select(clause.Associations).Delete(account)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
result = tx.
|
||||
Session(&gorm.Session{FullSaveAssociations: true}).
|
||||
Clauses(clause.OnConflict{UpdateAll: true}).Create(account)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
took := time.Since(start)
|
||||
if s.metrics != nil {
|
||||
s.metrics.StoreMetrics().CountPersistenceDuration(took)
|
||||
}
|
||||
log.Debugf("took %d ms to persist an account to the SQLite", took.Milliseconds())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) SaveInstallationID(ID string) error {
|
||||
installation := installation{InstallationIDValue: ID}
|
||||
installation.ID = uint(s.installationPK)
|
||||
|
||||
return s.db.Clauses(clause.OnConflict{UpdateAll: true}).Create(&installation).Error
|
||||
return s.DB.Clauses(clause.OnConflict{UpdateAll: true}).Create(&installation).Error
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) GetInstallationID() string {
|
||||
var installation installation
|
||||
|
||||
if result := s.db.First(&installation, "id = ?", s.installationPK); result.Error != nil {
|
||||
if result := s.DB.First(&installation, "id = ?", s.installationPK); result.Error != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
@ -164,3 +289,57 @@ func (s *DefaultSqliteStore) Close() error {
|
||||
func (s *DefaultSqliteStore) GetStoreEngine() StoreEngine {
|
||||
return SqliteStoreEngine
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) GetLicense() string {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) FindSettings(accountID string) (settings.Settings, error) {
|
||||
account, ok := s.accounts[accountID]
|
||||
if !ok {
|
||||
return nil, status.Errorf(status.NotFound, "account not found")
|
||||
}
|
||||
return account.Settings, nil
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) FindPeerByPubKey(accountID string, pubKey string) (peers.Peer, error) {
|
||||
a, ok := s.accounts[accountID]
|
||||
if !ok {
|
||||
return nil, status.Errorf(status.NotFound, "account not found")
|
||||
}
|
||||
for _, peer := range a.Peers {
|
||||
if peer.Key == pubKey {
|
||||
return peer.Copy(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, status.Errorf(status.NotFound, "peer with the public key %s not found", pubKey)
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) FindPeerByID(accountID string, id string) (peers.Peer, error) {
|
||||
a, ok := s.accounts[accountID]
|
||||
if !ok {
|
||||
return nil, status.Errorf(status.NotFound, "account not found")
|
||||
}
|
||||
for _, peer := range a.Peers {
|
||||
if peer.ID == id {
|
||||
return peer.Copy(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, status.Errorf(status.NotFound, "peer with the ID %s not found", id)
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) FindAllPeersInAccount(accountId string) ([]peers.Peer, error) {
|
||||
a, ok := s.accounts[accountID]
|
||||
if !ok {
|
||||
return nil, status.Errorf(status.NotFound, "account not found")
|
||||
}
|
||||
return a.Peers, nil
|
||||
}
|
||||
|
||||
func (s *DefaultSqliteStore) UpdatePeer(peer peers.Peer) error {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
@ -7,27 +7,26 @@ import (
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/refactor/peers"
|
||||
"github.com/netbirdio/netbird/management/refactor/settings"
|
||||
peerTypes "github.com/netbirdio/netbird/management/refactor/resources/peers/types"
|
||||
settingsTypes "github.com/netbirdio/netbird/management/refactor/resources/settings/types"
|
||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||
)
|
||||
|
||||
type Store interface {
|
||||
AcquireAccountLock(id string) func()
|
||||
AcquireGlobalLock() func()
|
||||
LoadAccount(id string) error
|
||||
WriteAccount(id string) error
|
||||
GetLicense() string
|
||||
FindPeerByPubKey(pubKey string) (peers.Peer, error)
|
||||
FindPeerByID(id string) (peers.Peer, error)
|
||||
FindAllPeersInAccount(id string) ([]peers.Peer, error)
|
||||
UpdatePeer(peer peers.Peer) error
|
||||
FindSettings(accountID string) (settings.Settings, error)
|
||||
FindPeerByPubKey(pubKey string) (peerTypes.Peer, error)
|
||||
FindPeerByID(id string) (peerTypes.Peer, error)
|
||||
FindAllPeersInAccount(id string) ([]peerTypes.Peer, error)
|
||||
UpdatePeer(peer peerTypes.Peer) error
|
||||
FindSettings(accountID string) (settingsTypes.Settings, error)
|
||||
}
|
||||
|
||||
type DefaultStore interface {
|
||||
GetLicense() string
|
||||
FindPeerByPubKey(pubKey string) (peers.Peer, error)
|
||||
FindPeerByID(id string) (peers.Peer, error)
|
||||
FindAllPeersInAccount(id string) ([]peers.Peer, error)
|
||||
UpdatePeer(peer peers.Peer) error
|
||||
FindSettings(accountID string) (settings.Settings, error)
|
||||
Store
|
||||
}
|
||||
|
||||
type StoreEngine string
|
||||
|
@ -1,24 +0,0 @@
|
||||
package users
|
||||
|
||||
import "github.com/netbirdio/netbird/management/refactor/peers"
|
||||
|
||||
type Manager interface {
|
||||
GetUser(id string) (User, error)
|
||||
}
|
||||
|
||||
type DefaultManager struct {
|
||||
repository repository
|
||||
peerManager peers.Manager
|
||||
}
|
||||
|
||||
func NewDefaultManager(repository repository, peerManager peers.Manager) *DefaultManager {
|
||||
return &DefaultManager{
|
||||
repository: repository,
|
||||
peerManager: peerManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (d DefaultManager) GetUser(id string) (User, error) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
Loading…
Reference in New Issue
Block a user