Fix config of the embedded IdP

This commit is contained in:
braginini
2025-12-28 22:24:55 -05:00
parent d266eda1ac
commit 1f89a156b9
5 changed files with 119 additions and 146 deletions

View File

@@ -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 {

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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")
}