Management single account mode (#511)

This commit is contained in:
Misha Bragin 2022-10-19 17:43:28 +02:00 committed by GitHub
parent 04e4407ea7
commit 7218a3d563
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 73 additions and 38 deletions

View File

@ -68,7 +68,7 @@ func startManagement(t *testing.T, config *mgmt.Config) (*grpc.Server, net.Liste
} }
peersUpdateManager := mgmt.NewPeersUpdateManager() peersUpdateManager := mgmt.NewPeersUpdateManager()
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil) accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -761,7 +761,7 @@ func startManagement(port int, dataDir string) (*grpc.Server, error) {
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err) log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
} }
peersUpdateManager := server.NewPeersUpdateManager() peersUpdateManager := server.NewPeersUpdateManager()
accountManager, err := server.BuildManager(store, peersUpdateManager, nil) accountManager, err := server.BuildManager(store, peersUpdateManager, nil, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -10,6 +10,8 @@ NETBIRD_MGMT_API_ENDPOINT=https://$NETBIRD_DOMAIN:$NETBIRD_MGMT_API_PORT
NETBIRD_MGMT_API_CERT_FILE="/etc/letsencrypt/live/$NETBIRD_DOMAIN/fullchain.pem" NETBIRD_MGMT_API_CERT_FILE="/etc/letsencrypt/live/$NETBIRD_DOMAIN/fullchain.pem"
# Management Certficate key file path. # Management Certficate key file path.
NETBIRD_MGMT_API_CERT_KEY_FILE="/etc/letsencrypt/live/$NETBIRD_DOMAIN/privkey.pem" NETBIRD_MGMT_API_CERT_KEY_FILE="/etc/letsencrypt/live/$NETBIRD_DOMAIN/privkey.pem"
# By default Management single account mode is enabled and domain set to $NETBIRD_DOMAIN, you may want to set this to your user's email domain
NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN=$NETBIRD_DOMAIN
# Turn credentials # Turn credentials

View File

@ -48,7 +48,7 @@ services:
# # port and command for Let's Encrypt validation without dashboard container # # port and command for Let's Encrypt validation without dashboard container
# - 443:443 # - 443:443
# command: ["--letsencrypt-domain", "$NETBIRD_DOMAIN", "--log-file", "console"] # command: ["--letsencrypt-domain", "$NETBIRD_DOMAIN", "--log-file", "console"]
command: ["--port", "443", "--log-file", "console", "--disable-anonymous-metrics=$NETBIRD_DISABLE_ANONYMOUS_METRICS"] command: ["--port", "443", "--log-file", "console", "--disable-anonymous-metrics=$NETBIRD_DISABLE_ANONYMOUS_METRICS", "--single-account-mode-domain=$NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN"]
# Coturn # Coturn
coturn: coturn:
image: coturn/coturn image: coturn/coturn

View File

@ -55,7 +55,7 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
} }
peersUpdateManager := mgmt.NewPeersUpdateManager() peersUpdateManager := mgmt.NewPeersUpdateManager()
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil) accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -41,11 +41,12 @@ import (
const ManagementLegacyPort = 33073 const ManagementLegacyPort = 33073
var ( var (
mgmtPort int mgmtPort int
mgmtLetsencryptDomain string mgmtLetsencryptDomain string
certFile string mgmtSingleAccModeDomain string
certKey string certFile string
config *server.Config certKey string
config *server.Config
kaep = keepalive.EnforcementPolicy{ kaep = keepalive.EnforcementPolicy{
MinTime: 15 * time.Second, MinTime: 15 * time.Second,
@ -121,7 +122,10 @@ var (
} }
} }
accountManager, err := server.BuildManager(store, peersUpdateManager, idpManager) if disableSingleAccMode {
mgmtSingleAccModeDomain = ""
}
accountManager, err := server.BuildManager(store, peersUpdateManager, idpManager, mgmtSingleAccModeDomain)
if err != nil { if err != nil {
return fmt.Errorf("failed to build default manager: %v", err) return fmt.Errorf("failed to build default manager: %v", err)
} }

View File

@ -13,21 +13,23 @@ const (
) )
var ( var (
defaultMgmtConfigDir string defaultMgmtConfigDir string
defaultMgmtDataDir string defaultMgmtDataDir string
defaultMgmtConfig string defaultMgmtConfig string
defaultLogDir string defaultSingleAccModeDomain string
defaultLogFile string defaultLogDir string
oldDefaultMgmtConfigDir string defaultLogFile string
oldDefaultMgmtDataDir string oldDefaultMgmtConfigDir string
oldDefaultMgmtConfig string oldDefaultMgmtDataDir string
oldDefaultLogDir string oldDefaultMgmtConfig string
oldDefaultLogFile string oldDefaultLogDir string
mgmtDataDir string oldDefaultLogFile string
mgmtConfig string mgmtDataDir string
logLevel string mgmtConfig string
logFile string logLevel string
disableMetrics bool logFile string
disableMetrics bool
disableSingleAccMode bool
rootCmd = &cobra.Command{ rootCmd = &cobra.Command{
Use: "netbird-mgmt", Use: "netbird-mgmt",
@ -48,6 +50,7 @@ func init() {
stopCh = make(chan int) stopCh = make(chan int)
defaultMgmtDataDir = "/var/lib/netbird/" defaultMgmtDataDir = "/var/lib/netbird/"
defaultSingleAccModeDomain = "netbird.selfhosted"
defaultMgmtConfigDir = "/etc/netbird" defaultMgmtConfigDir = "/etc/netbird"
defaultLogDir = "/var/log/netbird" defaultLogDir = "/var/log/netbird"
@ -65,6 +68,8 @@ func init() {
mgmtCmd.Flags().StringVar(&mgmtDataDir, "datadir", defaultMgmtDataDir, "server data directory location") mgmtCmd.Flags().StringVar(&mgmtDataDir, "datadir", defaultMgmtDataDir, "server data directory location")
mgmtCmd.Flags().StringVar(&mgmtConfig, "config", defaultMgmtConfig, "Netbird config file location. Config params specified via command line (e.g. datadir) have a precedence over configuration from this file") mgmtCmd.Flags().StringVar(&mgmtConfig, "config", defaultMgmtConfig, "Netbird config file location. Config params specified via command line (e.g. datadir) have a precedence over configuration from this file")
mgmtCmd.Flags().StringVar(&mgmtLetsencryptDomain, "letsencrypt-domain", "", "a domain to issue Let's Encrypt certificate for. Enables TLS using Let's Encrypt. Will fetch and renew certificate, and run the server with TLS") mgmtCmd.Flags().StringVar(&mgmtLetsencryptDomain, "letsencrypt-domain", "", "a domain to issue Let's Encrypt certificate for. Enables TLS using Let's Encrypt. Will fetch and renew certificate, and run the server with TLS")
mgmtCmd.Flags().StringVar(&mgmtSingleAccModeDomain, "single-account-mode-domain", defaultSingleAccModeDomain, "Enables single account mode. This means that all the users will be under the same account grouped by the specified domain. If the installation has more than one account, the property is ineffective. Enabled by default with the default domain "+defaultSingleAccModeDomain)
mgmtCmd.Flags().BoolVar(&disableSingleAccMode, "disable-single-account-mode", false, "If set to true, disables single account mode. The --single-account-mode-domain property will be ignored and every new user will have a separate NetBird account.")
mgmtCmd.Flags().StringVar(&certFile, "cert-file", "", "Location of your SSL certificate. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect") mgmtCmd.Flags().StringVar(&certFile, "cert-file", "", "Location of your SSL certificate. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect")
mgmtCmd.Flags().StringVar(&certKey, "cert-key", "", "Location of your SSL certificate private key. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect") mgmtCmd.Flags().StringVar(&certKey, "cert-key", "", "Location of your SSL certificate private key. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect")
mgmtCmd.Flags().BoolVar(&disableMetrics, "disable-anonymous-metrics", false, "disables push of anonymous usage metrics to NetBird") mgmtCmd.Flags().BoolVar(&disableMetrics, "disable-anonymous-metrics", false, "disables push of anonymous usage metrics to NetBird")

View File

@ -106,6 +106,13 @@ type DefaultAccountManager struct {
idpManager idp.Manager idpManager idp.Manager
cacheManager cache.CacheInterface[[]*idp.UserData] cacheManager cache.CacheInterface[[]*idp.UserData]
ctx context.Context ctx context.Context
// singleAccountMode indicates whether the instance has a single account.
// If true, then every new user will end up under the same account.
// This value will be set to false if management service has more than one account.
singleAccountMode bool
// singleAccountModeDomain is a domain to use in singleAccountMode setup
singleAccountModeDomain string
} }
// Account represents a unique account of the system // Account represents a unique account of the system
@ -195,9 +202,8 @@ func (a *Account) GetGroupAll() (*Group, error) {
} }
// BuildManager creates a new DefaultAccountManager with a provided Store // BuildManager creates a new DefaultAccountManager with a provided Store
func BuildManager( func BuildManager(store Store, peersUpdateManager *PeersUpdateManager, idpManager idp.Manager,
store Store, peersUpdateManager *PeersUpdateManager, idpManager idp.Manager, singleAccountModeDomain string) (*DefaultAccountManager, error) {
) (*DefaultAccountManager, error) {
am := &DefaultAccountManager{ am := &DefaultAccountManager{
Store: store, Store: store,
mux: sync.Mutex{}, mux: sync.Mutex{},
@ -207,11 +213,20 @@ func BuildManager(
cacheMux: sync.Mutex{}, cacheMux: sync.Mutex{},
cacheLoading: map[string]chan struct{}{}, cacheLoading: map[string]chan struct{}{},
} }
allAccounts := store.GetAllAccounts()
// enable single account mode only if configured by user and number of existing accounts is not grater than 1
am.singleAccountMode = singleAccountModeDomain != "" && len(allAccounts) <= 1
if am.singleAccountMode {
am.singleAccountModeDomain = singleAccountModeDomain
log.Infof("single account mode enabled, accounts number %d", len(allAccounts))
} else {
log.Infof("single account mode disabled, accounts number %d", len(allAccounts))
}
// if account has not default group // if account doesn't have a default group
// we create 'all' group and add all peers into it // we create 'all' group and add all peers into it
// also we create default rule with source as destination // also we create default rule with source as destination
for _, account := range store.GetAllAccounts() { for _, account := range allAccounts {
_, err := account.GetGroupAll() _, err := account.GetGroupAll()
if err != nil { if err != nil {
addAllGroup(account) addAllGroup(account)
@ -221,10 +236,10 @@ func BuildManager(
} }
} }
gocacheClient := gocache.New(CacheExpirationMax, 30*time.Minute) goCacheClient := gocache.New(CacheExpirationMax, 30*time.Minute)
gocacheStore := cacheStore.NewGoCache(gocacheClient) goCacheStore := cacheStore.NewGoCache(goCacheClient)
am.cacheManager = cache.NewLoadable[[]*idp.UserData](am.loadAccount, cache.New[[]*idp.UserData](gocacheStore)) am.cacheManager = cache.NewLoadable[[]*idp.UserData](am.loadAccount, cache.New[[]*idp.UserData](goCacheStore))
if !isNil(am.idpManager) { if !isNil(am.idpManager) {
go func() { go func() {
@ -604,6 +619,15 @@ func (am *DefaultAccountManager) redeemInvite(account *Account, userID string) e
// GetAccountFromToken returns an account associated with this token // GetAccountFromToken returns an account associated with this token
func (am *DefaultAccountManager) GetAccountFromToken(claims jwtclaims.AuthorizationClaims) (*Account, error) { func (am *DefaultAccountManager) GetAccountFromToken(claims jwtclaims.AuthorizationClaims) (*Account, error) {
if am.singleAccountMode && am.singleAccountModeDomain != "" {
// This section is mostly related to self-hosted installations.
// We override incoming domain claims to group users under a single account.
claims.Domain = am.singleAccountModeDomain
claims.DomainCategory = PrivateCategory
log.Infof("overriding JWT Domain and DomainCategory claims since single account mode is enabled")
}
account, err := am.getAccountWithAuthorizationClaims(claims) account, err := am.getAccountWithAuthorizationClaims(claims)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -962,7 +962,7 @@ func createManager(t *testing.T) (*DefaultAccountManager, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return BuildManager(store, NewPeersUpdateManager(), nil) return BuildManager(store, NewPeersUpdateManager(), nil, "")
} }
func createStore(t *testing.T) (Store, error) { func createStore(t *testing.T) (Store, error) {

View File

@ -403,7 +403,7 @@ func startManagement(t *testing.T, port int, config *Config) (*grpc.Server, erro
return nil, err return nil, err
} }
peersUpdateManager := NewPeersUpdateManager() peersUpdateManager := NewPeersUpdateManager()
accountManager, err := BuildManager(store, peersUpdateManager, nil) accountManager, err := BuildManager(store, peersUpdateManager, nil, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -493,7 +493,7 @@ func startServer(config *server.Config) (*grpc.Server, net.Listener) {
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err) log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
} }
peersUpdateManager := server.NewPeersUpdateManager() peersUpdateManager := server.NewPeersUpdateManager()
accountManager, err := server.BuildManager(store, peersUpdateManager, nil) accountManager, err := server.BuildManager(store, peersUpdateManager, nil, "")
if err != nil { if err != nil {
log.Fatalf("failed creating a manager: %v", err) log.Fatalf("failed creating a manager: %v", err)
} }

View File

@ -865,7 +865,7 @@ func createNSManager(t *testing.T) (*DefaultAccountManager, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return BuildManager(store, NewPeersUpdateManager(), nil) return BuildManager(store, NewPeersUpdateManager(), nil, "")
} }
func createNSStore(t *testing.T) (Store, error) { func createNSStore(t *testing.T) (Store, error) {

View File

@ -778,7 +778,7 @@ func createRouterManager(t *testing.T) (*DefaultAccountManager, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return BuildManager(store, NewPeersUpdateManager(), nil) return BuildManager(store, NewPeersUpdateManager(), nil, "")
} }
func createRouterStore(t *testing.T) (Store, error) { func createRouterStore(t *testing.T) (Store, error) {