[GH-ISSUE #5335] Support external IDP (e.g. Zitadel) in combined netbird-server container #10370

Closed
opened 2026-08-05 01:25:39 -04:00 by saavagebueno · 1 comment
Owner

Originally created by @alex-s-interexy on GitHub (Feb 15, 2026).
Original GitHub issue: https://github.com/netbirdio/netbird/issues/5335

Feature Request

Problem

The combined netbird-server container (netbirdio/netbird-server) does not support configuring an external Identity Provider (e.g., Zitadel, Keycloak, Auth0) for authentication. The embedded Dex IdP is always enabled and overrides all HTTP auth configuration, making it impossible to use an external IDP.

Current Behavior

When running netbird-server (combined binary):

  1. ToManagementConfig() in combined/cmd/config.go hardcodes EmbeddedIdP.Enabled = true — there is no config.yaml field to change this.
  2. ApplyEmbeddedIdPConfig() in combined/cmd/config.go overwrites HttpConfig.AuthIssuer, AuthKeysLocation, and OIDCConfigEndpoint with embedded Dex values when EmbeddedIdP.Enabled == true.
  3. IdpManager() in management/internals/server/modules.go returns EmbeddedIdPManager (Dex) when EmbeddedIdP.Enabled == true, never falling through to the external IdP manager.
  4. management.json is not loaded by the combined server — only config.yaml is read. The standalone netbirdio/management binary reads management.json, but the combined binary does not call LoadMgmtConfig().
  5. There are no config.yaml fields for IdpManagerConfig, DeviceAuthorizationFlow, or PKCEAuthorizationFlow — all required for external IDP integration.

This means JWT tokens issued by an external IDP (Zitadel in our case) are rejected with "unable to find appropriate key" because the server only validates against embedded Dex JWKS keys.

Expected Behavior

The combined netbird-server should support external IDP configuration, either by:

  1. Adding config.yaml fields for EmbeddedIdP.Enabled, HttpConfig (AuthIssuer, AuthKeysLocation, OIDCConfigEndpoint, AuthAudience), IdpManagerConfig, DeviceAuthorizationFlow, and PKCEAuthorizationFlow — mirroring what management.json supports in standalone mode.

  2. Or allowing management.json to be loaded alongside config.yaml for IDP configuration (the combined server could read IDP settings from management.json if present).

  3. Or honoring EmbeddedIdP.Enabled: false in config.yaml (or via environment variable) so that ApplyEmbeddedIdPConfig() is skipped and external IDP settings take effect.

Use Case

We run a production NetBird deployment with Zitadel as the external IDP (standalone setup per Zitadel Advanced guide). We wanted to migrate from the multi-container setup (separate management, signal, relay) to the combined netbird-server for simplified operations, but the migration failed because the combined server cannot use Zitadel for authentication.

The documentation for disabling embedded IdP describes setting "EmbeddedIdP": {"Enabled": false} in management.json, but this only works with the standalone management binary — not the combined server.

Environment

  • NetBird server version: v0.65.1 (combined netbirdio/netbird-server:latest)
  • External IDP: Zitadel v4.10.1
  • Store: PostgreSQL 16

Workaround

Continue using the multi-container deployment (netbirdio/management + netbirdio/signal + netbirdio/relay) with management.json containing the full Zitadel IDP configuration.

Originally created by @alex-s-interexy on GitHub (Feb 15, 2026). Original GitHub issue: https://github.com/netbirdio/netbird/issues/5335 ## Feature Request ### Problem The combined `netbird-server` container (`netbirdio/netbird-server`) does not support configuring an external Identity Provider (e.g., Zitadel, Keycloak, Auth0) for authentication. The embedded Dex IdP is always enabled and **overrides** all HTTP auth configuration, making it impossible to use an external IDP. ### Current Behavior When running `netbird-server` (combined binary): 1. `ToManagementConfig()` in `combined/cmd/config.go` **hardcodes** `EmbeddedIdP.Enabled = true` — there is no `config.yaml` field to change this. 2. `ApplyEmbeddedIdPConfig()` in `combined/cmd/config.go` overwrites `HttpConfig.AuthIssuer`, `AuthKeysLocation`, and `OIDCConfigEndpoint` with embedded Dex values when `EmbeddedIdP.Enabled == true`. 3. `IdpManager()` in `management/internals/server/modules.go` returns `EmbeddedIdPManager` (Dex) when `EmbeddedIdP.Enabled == true`, never falling through to the external IdP manager. 4. `management.json` is **not loaded** by the combined server — only `config.yaml` is read. The standalone `netbirdio/management` binary reads `management.json`, but the combined binary does not call `LoadMgmtConfig()`. 5. There are **no `config.yaml` fields** for `IdpManagerConfig`, `DeviceAuthorizationFlow`, or `PKCEAuthorizationFlow` — all required for external IDP integration. This means JWT tokens issued by an external IDP (Zitadel in our case) are rejected with `"unable to find appropriate key"` because the server only validates against embedded Dex JWKS keys. ### Expected Behavior The combined `netbird-server` should support external IDP configuration, either by: 1. **Adding `config.yaml` fields** for `EmbeddedIdP.Enabled`, `HttpConfig` (AuthIssuer, AuthKeysLocation, OIDCConfigEndpoint, AuthAudience), `IdpManagerConfig`, `DeviceAuthorizationFlow`, and `PKCEAuthorizationFlow` — mirroring what `management.json` supports in standalone mode. 2. **Or** allowing `management.json` to be loaded alongside `config.yaml` for IDP configuration (the combined server could read IDP settings from `management.json` if present). 3. **Or** honoring `EmbeddedIdP.Enabled: false` in `config.yaml` (or via environment variable) so that `ApplyEmbeddedIdPConfig()` is skipped and external IDP settings take effect. ### Use Case We run a production NetBird deployment with **Zitadel as the external IDP** (standalone setup per [Zitadel Advanced guide](https://docs.netbird.io/selfhosted/identity-providers/advanced/zitadel#standalone-setup-advanced)). We wanted to migrate from the multi-container setup (separate management, signal, relay) to the combined `netbird-server` for simplified operations, but the migration failed because the combined server cannot use Zitadel for authentication. The [documentation for disabling embedded IdP](https://docs.netbird.io/selfhosted/identity-providers/local#disabling-embedded-id-p) describes setting `"EmbeddedIdP": {"Enabled": false}` in `management.json`, but this only works with the standalone management binary — not the combined server. ### Environment - NetBird server version: v0.65.1 (combined `netbirdio/netbird-server:latest`) - External IDP: Zitadel v4.10.1 - Store: PostgreSQL 16 ### Workaround Continue using the multi-container deployment (`netbirdio/management` + `netbirdio/signal` + `netbirdio/relay`) with `management.json` containing the full Zitadel IDP configuration.
Author
Owner

@SuperKali commented on GitHub (Feb 15, 2026):

Duplicated #5322

<!-- gh-comment-id:3904788542 --> @SuperKali commented on GitHub (Feb 15, 2026): Duplicated #5322
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#10370