mirror of
https://github.com/netbirdio/netbird
synced 2026-04-22 17:44:57 +02:00
Fix config of the embedded IdP
This commit is contained in:
@@ -152,7 +152,7 @@ func loadMgmtConfig(ctx context.Context, mgmtConfigPath string) (*nbconfig.Confi
|
||||
}
|
||||
|
||||
oidcEndpoint := loadedConfig.HttpConfig.OIDCConfigEndpoint
|
||||
if oidcEndpoint != "" && loadedConfig.EmbeddedIdp == nil {
|
||||
if oidcEndpoint != "" && loadedConfig.EmbeddedIdP == nil {
|
||||
// if OIDCConfigEndpoint is specified, we can load DeviceAuthEndpoint and TokenEndpoint automatically
|
||||
log.WithContext(ctx).Infof("loading OIDC configuration from the provided IDP configuration endpoint %s", oidcEndpoint)
|
||||
oidcConfig, err := fetchOIDCConfig(ctx, oidcEndpoint)
|
||||
@@ -200,8 +200,8 @@ func loadMgmtConfig(ctx context.Context, mgmtConfigPath string) (*nbconfig.Confi
|
||||
}
|
||||
}
|
||||
|
||||
if loadedConfig.EmbeddedIdp != nil {
|
||||
log.Infof("running with the embedded IdP: %v", loadedConfig.EmbeddedIdp.Issuer)
|
||||
if loadedConfig.EmbeddedIdP != nil {
|
||||
log.Infof("running with the embedded IdP: %v", loadedConfig.EmbeddedIdP.Issuer)
|
||||
}
|
||||
|
||||
if loadedConfig.Relay != nil {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
"github.com/dexidp/dex/storage"
|
||||
"github.com/google/uuid"
|
||||
"github.com/netbirdio/netbird/idp/dex"
|
||||
"github.com/netbirdio/netbird/management/server/idp"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/shared/management/client/common"
|
||||
@@ -58,17 +62,51 @@ type Config struct {
|
||||
// disable default all-to-all policy
|
||||
DisableDefaultPolicy bool
|
||||
|
||||
// EmbeddedIdp contains configuration for the embedded Dex OIDC provider.
|
||||
// EmbeddedIdP contains configuration for the embedded Dex OIDC provider.
|
||||
// When set, Dex will be embedded in the management server and serve requests at /oauth2/
|
||||
EmbeddedIdp *EmbeddedIdpConfig
|
||||
EmbeddedIdP *EmbeddedIdPConfig
|
||||
}
|
||||
|
||||
// EmbeddedIdpConfig contains configuration for the embedded Dex OIDC identity provider
|
||||
type EmbeddedIdpConfig struct {
|
||||
// EmbeddedIdPConfig contains configuration for the embedded Dex OIDC identity provider
|
||||
type EmbeddedIdPConfig struct {
|
||||
// Enabled indicates whether the embedded IDP is enabled
|
||||
Enabled bool
|
||||
// Config contains the embedded IdP configuration
|
||||
idp.EmbeddedIdPConfig
|
||||
// Issuer is the OIDC issuer URL (e.g., "http://localhost:3002/oauth2")
|
||||
Issuer string
|
||||
// Storage configuration for the IdP database
|
||||
Storage EmbeddedStorageConfig
|
||||
// DashboardRedirectURIs are the OAuth2 redirect URIs for the dashboard client
|
||||
DashboardRedirectURIs []string
|
||||
// DashboardRedirectURIs are the OAuth2 redirect URIs for the dashboard client
|
||||
CLIRedirectURIs []string
|
||||
// Owner is the initial owner/admin user (optional, can be nil)
|
||||
Owner *OwnerConfig
|
||||
// SignKeyRefreshEnabled enables automatic key rotation for signing keys
|
||||
SignKeyRefreshEnabled bool
|
||||
}
|
||||
|
||||
// EmbeddedStorageConfig holds storage configuration for the embedded IdP.
|
||||
type EmbeddedStorageConfig struct {
|
||||
// Type is the storage type (currently only "sqlite3" is supported)
|
||||
Type string
|
||||
// Config contains type-specific configuration
|
||||
Config EmbeddedStorageTypeConfig
|
||||
}
|
||||
|
||||
// EmbeddedStorageTypeConfig contains type-specific storage configuration.
|
||||
type EmbeddedStorageTypeConfig struct {
|
||||
// File is the path to the SQLite database file (for sqlite3 type)
|
||||
File string
|
||||
}
|
||||
|
||||
// OwnerConfig represents the initial owner/admin user for the embedded IdP.
|
||||
type OwnerConfig struct {
|
||||
// Email is the user's email address (required)
|
||||
Email string
|
||||
// Hash is the bcrypt hash of the user's password (required)
|
||||
Hash string
|
||||
// Username is the display name for the user (optional, defaults to email)
|
||||
Username string
|
||||
}
|
||||
|
||||
// GetAuthAudiences returns the audience from the http config and device authorization flow config
|
||||
@@ -86,6 +124,73 @@ func (c Config) GetAuthAudiences() []string {
|
||||
return audiences
|
||||
}
|
||||
|
||||
// ToYAMLConfig converts EmbeddedIdPConfig to dex.YAMLConfig.
|
||||
func (c *EmbeddedIdPConfig) ToYAMLConfig() (*dex.YAMLConfig, error) {
|
||||
if c.Issuer == "" {
|
||||
return nil, fmt.Errorf("issuer is required")
|
||||
}
|
||||
if c.Storage.Type == "" {
|
||||
c.Storage.Type = "sqlite3"
|
||||
}
|
||||
if c.Storage.Type == "sqlite3" && c.Storage.Config.File == "" {
|
||||
return nil, fmt.Errorf("storage file is required for sqlite3")
|
||||
}
|
||||
|
||||
cfg := &dex.YAMLConfig{
|
||||
Issuer: c.Issuer,
|
||||
Storage: dex.Storage{
|
||||
Type: c.Storage.Type,
|
||||
Config: map[string]interface{}{
|
||||
"file": c.Storage.Config.File,
|
||||
},
|
||||
},
|
||||
Web: dex.Web{
|
||||
AllowedOrigins: []string{"*"},
|
||||
AllowedHeaders: []string{"Authorization", "Content-Type"},
|
||||
},
|
||||
OAuth2: dex.OAuth2{
|
||||
SkipApprovalScreen: true,
|
||||
},
|
||||
Frontend: dex.Frontend{
|
||||
Issuer: "NetBird",
|
||||
Theme: "light",
|
||||
},
|
||||
EnablePasswordDB: true,
|
||||
StaticClients: []storage.Client{
|
||||
{
|
||||
ID: "netbird-dashboard",
|
||||
Name: "NetBird Dashboard",
|
||||
Public: true,
|
||||
RedirectURIs: c.DashboardRedirectURIs,
|
||||
},
|
||||
{
|
||||
ID: "netbird-cli",
|
||||
Name: "NetBird CLI",
|
||||
Public: true,
|
||||
RedirectURIs: c.CLIRedirectURIs,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Add owner user if provided
|
||||
if c.Owner != nil && c.Owner.Email != "" && c.Owner.Hash != "" {
|
||||
username := c.Owner.Username
|
||||
if username == "" {
|
||||
username = c.Owner.Email
|
||||
}
|
||||
cfg.StaticPasswords = []dex.Password{
|
||||
{
|
||||
Email: c.Owner.Email,
|
||||
Hash: []byte(c.Owner.Hash),
|
||||
Username: username,
|
||||
UserID: uuid.New().String(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// TURNConfig is a config of the TURNCredentialsManager
|
||||
type TURNConfig struct {
|
||||
TimeBasedCredentials bool
|
||||
|
||||
@@ -99,7 +99,7 @@ func (s *BaseServer) IdpManager() idp.Manager {
|
||||
// Use embedded IdP manager if embedded Dex is configured.
|
||||
// Legacy IdpManager won't be used anymore even if configured.
|
||||
if s.embeddedIdp != nil {
|
||||
idpManager, err = idp.NewEmbeddedIdPManagerFromProvider(s.embeddedIdp, s.Metrics())
|
||||
idpManager, err = idp.NewEmbeddedIdPManager(s.embeddedIdp, s.Metrics())
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create embedded IDP manager: %v", err)
|
||||
}
|
||||
|
||||
@@ -138,8 +138,8 @@ func (s *BaseServer) Start(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// Initialize embedded IDP if configured
|
||||
if s.Config.EmbeddedIdp != nil && s.Config.EmbeddedIdp.Enabled {
|
||||
yamlConfig, err := s.Config.EmbeddedIdp.EmbeddedIdPConfig.ToYAMLConfig()
|
||||
if s.Config.EmbeddedIdP != nil && s.Config.EmbeddedIdP.Enabled {
|
||||
yamlConfig, err := s.Config.EmbeddedIdP.ToYAMLConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create embedded IDP config: %v", err)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/dexidp/dex/storage"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/idp/dex"
|
||||
@@ -22,139 +21,8 @@ type EmbeddedIdPManager struct {
|
||||
appMetrics telemetry.AppMetrics
|
||||
}
|
||||
|
||||
// EmbeddedStorageConfig holds storage configuration for the embedded IdP.
|
||||
type EmbeddedStorageConfig struct {
|
||||
// Type is the storage type (currently only "sqlite3" is supported)
|
||||
Type string
|
||||
// Config contains type-specific configuration
|
||||
Config EmbeddedStorageTypeConfig
|
||||
}
|
||||
|
||||
// EmbeddedStorageTypeConfig contains type-specific storage configuration.
|
||||
type EmbeddedStorageTypeConfig struct {
|
||||
// File is the path to the SQLite database file (for sqlite3 type)
|
||||
File string
|
||||
}
|
||||
|
||||
// OwnerConfig represents the initial owner/admin user for the embedded IdP.
|
||||
type OwnerConfig struct {
|
||||
// Email is the user's email address (required)
|
||||
Email string
|
||||
// Hash is the bcrypt hash of the user's password (required)
|
||||
Hash string
|
||||
// Username is the display name for the user (optional, defaults to email)
|
||||
Username string
|
||||
}
|
||||
|
||||
// EmbeddedIdPConfig holds configuration for the embedded IdP manager.
|
||||
type EmbeddedIdPConfig struct {
|
||||
// Issuer is the OIDC issuer URL (e.g., "http://localhost:3002/oauth2")
|
||||
Issuer string
|
||||
// Storage configuration for the IdP database
|
||||
Storage EmbeddedStorageConfig
|
||||
// DashboardRedirectURIs are the OAuth2 redirect URIs for the dashboard client
|
||||
DashboardRedirectURIs []string
|
||||
// Owner is the initial owner/admin user (optional, can be nil)
|
||||
Owner *OwnerConfig
|
||||
// SignKeyRefreshEnabled enables automatic key rotation for signing keys
|
||||
SignKeyRefreshEnabled bool
|
||||
}
|
||||
|
||||
// DefaultCLIRedirectURIs returns the default redirect URIs for the CLI client.
|
||||
func DefaultCLIRedirectURIs() []string {
|
||||
return []string{
|
||||
"http://localhost:53000/",
|
||||
"http://localhost:54000/",
|
||||
}
|
||||
}
|
||||
|
||||
// ToYAMLConfig converts EmbeddedIdPConfig to dex.YAMLConfig.
|
||||
func (c *EmbeddedIdPConfig) ToYAMLConfig() (*dex.YAMLConfig, error) {
|
||||
if c.Issuer == "" {
|
||||
return nil, fmt.Errorf("issuer is required")
|
||||
}
|
||||
if c.Storage.Type == "" {
|
||||
c.Storage.Type = "sqlite3"
|
||||
}
|
||||
if c.Storage.Type == "sqlite3" && c.Storage.Config.File == "" {
|
||||
return nil, fmt.Errorf("storage file is required for sqlite3")
|
||||
}
|
||||
|
||||
cfg := &dex.YAMLConfig{
|
||||
Issuer: c.Issuer,
|
||||
Storage: dex.Storage{
|
||||
Type: c.Storage.Type,
|
||||
Config: map[string]interface{}{
|
||||
"file": c.Storage.Config.File,
|
||||
},
|
||||
},
|
||||
Web: dex.Web{
|
||||
AllowedOrigins: []string{"*"},
|
||||
AllowedHeaders: []string{"Authorization", "Content-Type"},
|
||||
},
|
||||
OAuth2: dex.OAuth2{
|
||||
SkipApprovalScreen: true,
|
||||
},
|
||||
Frontend: dex.Frontend{
|
||||
Issuer: "NetBird",
|
||||
Theme: "light",
|
||||
},
|
||||
EnablePasswordDB: true,
|
||||
StaticClients: []storage.Client{
|
||||
{
|
||||
ID: "netbird-dashboard",
|
||||
Name: "NetBird Dashboard",
|
||||
Public: true,
|
||||
RedirectURIs: c.DashboardRedirectURIs,
|
||||
},
|
||||
{
|
||||
ID: "netbird-cli",
|
||||
Name: "NetBird CLI",
|
||||
Public: true,
|
||||
RedirectURIs: DefaultCLIRedirectURIs(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Add owner user if provided
|
||||
if c.Owner != nil && c.Owner.Email != "" && c.Owner.Hash != "" {
|
||||
username := c.Owner.Username
|
||||
if username == "" {
|
||||
username = c.Owner.Email
|
||||
}
|
||||
cfg.StaticPasswords = []dex.Password{
|
||||
{
|
||||
Email: c.Owner.Email,
|
||||
Hash: []byte(c.Owner.Hash),
|
||||
Username: username,
|
||||
UserID: uuid.New().String(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// NewEmbeddedIdPManager creates a new instance of EmbeddedIdPManager from configuration.
|
||||
func NewEmbeddedIdPManager(ctx context.Context, config EmbeddedIdPConfig, appMetrics telemetry.AppMetrics) (*EmbeddedIdPManager, error) {
|
||||
yamlConfig, err := config.ToYAMLConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create YAML config: %w", err)
|
||||
}
|
||||
|
||||
provider, err := dex.NewProviderFromYAML(ctx, yamlConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create embedded IdP provider: %w", err)
|
||||
}
|
||||
|
||||
return &EmbeddedIdPManager{
|
||||
provider: provider,
|
||||
appMetrics: appMetrics,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewEmbeddedIdPManagerFromProvider creates a new instance of EmbeddedIdPManager from an existing provider.
|
||||
func NewEmbeddedIdPManagerFromProvider(provider *dex.Provider, appMetrics telemetry.AppMetrics) (*EmbeddedIdPManager, error) {
|
||||
// NewEmbeddedIdPManager creates a new instance of EmbeddedIdPManager with an existing provider.
|
||||
func NewEmbeddedIdPManager(provider *dex.Provider, appMetrics telemetry.AppMetrics) (*EmbeddedIdPManager, error) {
|
||||
if provider == nil {
|
||||
return nil, fmt.Errorf("embedded IdP provider is required")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user