From a7f9011334ea934d5948259d219564906e3b4562 Mon Sep 17 00:00:00 2001 From: Jack Carter <128555021+SunsetDrifter@users.noreply.github.com> Date: Fri, 13 Nov 2026 09:55:13 +0100 Subject: [PATCH 1/9] Self-hosted: show Traefik v3.7.14 with aliasHeadersStrategy, and fix the multi-container embedded IdP config --- .../selfhosted/enterprise/getting-started.mdx | 2 +- .../selfhosted/external-reverse-proxy.mdx | 6 ++-- .../selfhosted/identity-providers/local.mdx | 28 +++++++++++-------- .../maintenance/configuration-files.mdx | 10 +++++-- .../scaling/multiple-proxy-instances.mdx | 2 +- .../migration/combined-container.mdx | 2 +- 6 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/pages/selfhosted/enterprise/getting-started.mdx b/src/pages/selfhosted/enterprise/getting-started.mdx index ff1cc59e5..085cae7b6 100644 --- a/src/pages/selfhosted/enterprise/getting-started.mdx +++ b/src/pages/selfhosted/enterprise/getting-started.mdx @@ -94,7 +94,7 @@ The combined stack: | Service | Image | Notes | | --- | --- | --- | -| `traefik` | `traefik:v3.6` | TLS termination, reverse proxy, HTTP→HTTPS redirect | +| `traefik` | `traefik:v3.7.14` | TLS termination, reverse proxy, HTTP→HTTPS redirect | | `dashboard` | `ghcr.io/netbirdio/dashboard-cloud:latest` | UI | | `postgres` | `postgres:17` | Datastore for management, embedded IdP, traffic events | | `netbird-server` | `ghcr.io/netbirdio/netbird-server-cloud:latest` | Management + signal + relay + embedded STUN on UDP/3478 | diff --git a/src/pages/selfhosted/external-reverse-proxy.mdx b/src/pages/selfhosted/external-reverse-proxy.mdx index e3d2d0e57..575d4652c 100644 --- a/src/pages/selfhosted/external-reverse-proxy.mdx +++ b/src/pages/selfhosted/external-reverse-proxy.mdx @@ -811,22 +811,22 @@ If you don't have Traefik set up yet, here's a complete example: ```yaml services: traefik: - image: traefik:v3.2 + image: traefik:v3.7.14 command: - - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedByDefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - "--entrypoints.websecure.address=:443" + - "--entrypoints.web.http.aliasHeadersStrategy=delete" + - "--entrypoints.websecure.http.aliasHeadersStrategy=delete" - "--certificatesresolvers.letsencrypt.acme.email=your-email@example.com" - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web" ports: - "80:80" - "443:443" - - "8080:8080" # Traefik dashboard (optional) volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./letsencrypt:/letsencrypt diff --git a/src/pages/selfhosted/identity-providers/local.mdx b/src/pages/selfhosted/identity-providers/local.mdx index 02c87c1f3..28a9198c8 100644 --- a/src/pages/selfhosted/identity-providers/local.mdx +++ b/src/pages/selfhosted/identity-providers/local.mdx @@ -73,26 +73,32 @@ For a full list of available configuration options, see the [config.yaml.example This section applies to deployments using the older multi-container architecture. If you deployed using `getting-started.sh`, you are on the combined setup and should use the `config.yaml` instructions above. See the [migration guide](/selfhosted/migration/combined-container) to upgrade. -Update your `management.json`: +Update your `management.json`, replacing `netbird.example.com` with your domain: ```json { + "Datadir": "/var/lib/netbird", + "DataStoreEncryptionKey": "", "EmbeddedIdP": { "Enabled": true, - "DataDir": "/var/lib/netbird/idp" - }, - "EncryptionKey": "" + "Issuer": "https://netbird.example.com/oauth2", + "DashboardRedirectURIs": [ + "https://netbird.example.com/nb-auth", + "https://netbird.example.com/nb-silent-auth" + ], + "SessionCookieEncryptionKey": "" + } } ``` -Or configure via environment variables on the `management` container: +- `Issuer` is required. Management fails to start without it. +- The embedded IdP stores its data in SQLite at `/idp.db` unless `EmbeddedIdP.Storage` sets another location. +- If `DataStoreEncryptionKey` is empty, Management generates a key and writes it to `management.json` on startup. Back up that file. +- `SessionCookieEncryptionKey` encrypts the embedded IdP's session cookies. It must be 16, 24, or 32 bytes, raw or base64-encoded. Without it, the session cookies are not encrypted. Generate one with `openssl rand -base64 32`. +- The embedded IdP requires single account mode. Do not pass `--disable-single-account-mode` or an empty `--single-account-mode-domain` to the `management` container; the default domain is `netbird.selfhosted`. +- When the embedded IdP is enabled, Management derives the `HttpConfig` authentication settings from `Issuer` and ignores the `HttpConfig` section. -```yaml -environment: - - NETBIRD_EMBEDDED_IDP_ENABLED=true - - NETBIRD_EMBEDDED_IDP_DATA_DIR=/var/lib/netbird/idp - - NETBIRD_ENCRYPTION_KEY=${ENCRYPTION_KEY} -``` +Set these values in `management.json`. The only one with an environment-variable alternative is `SessionCookieEncryptionKey`, which Management also reads from `NB_IDP_SESSION_COOKIE_ENCRYPTION_KEY`. ### Generating an Encryption Key diff --git a/src/pages/selfhosted/maintenance/configuration-files.mdx b/src/pages/selfhosted/maintenance/configuration-files.mdx index dd269ffcb..d3a0c9f0b 100644 --- a/src/pages/selfhosted/maintenance/configuration-files.mdx +++ b/src/pages/selfhosted/maintenance/configuration-files.mdx @@ -43,7 +43,7 @@ The Docker Compose file defines all NetBird services, their dependencies, networ | Service | Image | Internal Port | External (Exposed) | Description | |---------|-------|---------------|-------------------|-------------| -| `traefik` | `traefik:v3.6` | 80, 443 | 80:80, 443:443 | Handles TLS termination via Let's Encrypt and routes incoming HTTPS requests to the appropriate NetBird services. Only included when using the built-in Traefik option (option 0). | +| `traefik` | `traefik:v3.7.14` | 80, 443 | 80:80, 443:443 | Handles TLS termination via Let's Encrypt and routes incoming HTTPS requests to the appropriate NetBird services. Only included when using the built-in Traefik option (option 0). | | `dashboard` | `netbirdio/dashboard` | 80 | 8080:80 | The web-based management console where administrators configure networks, manage peers, create access policies, and view activity logs. Includes an embedded nginx server for serving the UI. | | `netbird-server` | `netbirdio/netbird-server` | 80, 3478/udp | 8081:80, 3478:3478/udp | Combined server that includes management, signal, relay, and embedded STUN in a single container. Configured via `config.yaml`. | @@ -61,16 +61,18 @@ The Traefik service is only present when using the built-in reverse proxy option ```yaml traefik: - image: traefik:v3.6 + image: traefik:v3.7.14 container_name: netbird-traefik restart: unless-stopped networks: [netbird] command: - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - - "--providers.docker.network=netbird" + - "--providers.docker.network=${COMPOSE_PROJECT_NAME:-}_netbird" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" + - "--entrypoints.web.http.aliasHeadersStrategy=delete" + - "--entrypoints.websecure.http.aliasHeadersStrategy=delete" - "--entrypoints.websecure.transport.respondingTimeouts.readTimeout=0" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" @@ -91,6 +93,8 @@ traefik: The `readTimeout=0` setting on the websecure entrypoint disables read timeouts, which is required for long-lived gRPC and WebSocket connections used by the signal and relay services. + +The `aliasHeadersStrategy=delete` settings need Traefik v3.7.12 or later, and the installers require v3.7.13 or later. Older Traefik releases are affected by header spoofing that lets a client set the forwarded headers NetBird trusts from Traefik. See [Traefik header handling](/selfhosted/maintenance/upgrade#traefik-header-handling). ### Dashboard Service diff --git a/src/pages/selfhosted/maintenance/scaling/multiple-proxy-instances.mdx b/src/pages/selfhosted/maintenance/scaling/multiple-proxy-instances.mdx index b65484c87..6b55914b9 100644 --- a/src/pages/selfhosted/maintenance/scaling/multiple-proxy-instances.mdx +++ b/src/pages/selfhosted/maintenance/scaling/multiple-proxy-instances.mdx @@ -163,7 +163,7 @@ Create a `docker-compose.yml` on the new server: ```yaml services: traefik: - image: traefik:v3.6 + image: traefik:v3.7.14 container_name: traefik restart: unless-stopped command: diff --git a/src/pages/selfhosted/migration/combined-container.mdx b/src/pages/selfhosted/migration/combined-container.mdx index 66b515972..60afa2a49 100644 --- a/src/pages/selfhosted/migration/combined-container.mdx +++ b/src/pages/selfhosted/migration/combined-container.mdx @@ -22,7 +22,7 @@ The migration script **exits with an error** if it detects an external identity ### What's new - **Combined `netbird-server` container** - management, signal, relay, and the embedded STUN server all run in a single container, replacing four separate services -- **Traefik reverse proxy** (for Caddy-based setups) - replaces the embedded Caddy with Traefik v3.6, handling TLS termination via Let's Encrypt and routing for gRPC, WebSocket, and HTTP traffic +- **Traefik reverse proxy** (for Caddy-based setups) - replaces the embedded Caddy with Traefik v3.7.14, handling TLS termination via Let's Encrypt and routing for gRPC, WebSocket, and HTTP traffic - **Unified `config.yaml`** - a single configuration file replaces the combination of `management.json`, `setup.env`, and multiple environment files - **Simplified Docker Compose** - the stack goes from 5+ services down to 2 (Traefik + `netbird-server`) or 3 (with dashboard) From 7d29ad2c1e622afb59bf5048c4916cea6cfc13ac Mon Sep 17 00:00:00 2001 From: Jack Carter <128555021+SunsetDrifter@users.noreply.github.com> Date: Fri, 13 Nov 2026 10:00:54 +0100 Subject: [PATCH 2/9] Limit to the installer Traefik version references --- .../selfhosted/external-reverse-proxy.mdx | 6 ++-- .../selfhosted/identity-providers/local.mdx | 28 ++++++++----------- .../maintenance/configuration-files.mdx | 8 ++---- .../scaling/multiple-proxy-instances.mdx | 2 +- 4 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/pages/selfhosted/external-reverse-proxy.mdx b/src/pages/selfhosted/external-reverse-proxy.mdx index 575d4652c..e3d2d0e57 100644 --- a/src/pages/selfhosted/external-reverse-proxy.mdx +++ b/src/pages/selfhosted/external-reverse-proxy.mdx @@ -811,22 +811,22 @@ If you don't have Traefik set up yet, here's a complete example: ```yaml services: traefik: - image: traefik:v3.7.14 + image: traefik:v3.2 command: + - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedByDefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - "--entrypoints.websecure.address=:443" - - "--entrypoints.web.http.aliasHeadersStrategy=delete" - - "--entrypoints.websecure.http.aliasHeadersStrategy=delete" - "--certificatesresolvers.letsencrypt.acme.email=your-email@example.com" - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web" ports: - "80:80" - "443:443" + - "8080:8080" # Traefik dashboard (optional) volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./letsencrypt:/letsencrypt diff --git a/src/pages/selfhosted/identity-providers/local.mdx b/src/pages/selfhosted/identity-providers/local.mdx index 28a9198c8..02c87c1f3 100644 --- a/src/pages/selfhosted/identity-providers/local.mdx +++ b/src/pages/selfhosted/identity-providers/local.mdx @@ -73,32 +73,26 @@ For a full list of available configuration options, see the [config.yaml.example This section applies to deployments using the older multi-container architecture. If you deployed using `getting-started.sh`, you are on the combined setup and should use the `config.yaml` instructions above. See the [migration guide](/selfhosted/migration/combined-container) to upgrade. -Update your `management.json`, replacing `netbird.example.com` with your domain: +Update your `management.json`: ```json { - "Datadir": "/var/lib/netbird", - "DataStoreEncryptionKey": "", "EmbeddedIdP": { "Enabled": true, - "Issuer": "https://netbird.example.com/oauth2", - "DashboardRedirectURIs": [ - "https://netbird.example.com/nb-auth", - "https://netbird.example.com/nb-silent-auth" - ], - "SessionCookieEncryptionKey": "" - } + "DataDir": "/var/lib/netbird/idp" + }, + "EncryptionKey": "" } ``` -- `Issuer` is required. Management fails to start without it. -- The embedded IdP stores its data in SQLite at `/idp.db` unless `EmbeddedIdP.Storage` sets another location. -- If `DataStoreEncryptionKey` is empty, Management generates a key and writes it to `management.json` on startup. Back up that file. -- `SessionCookieEncryptionKey` encrypts the embedded IdP's session cookies. It must be 16, 24, or 32 bytes, raw or base64-encoded. Without it, the session cookies are not encrypted. Generate one with `openssl rand -base64 32`. -- The embedded IdP requires single account mode. Do not pass `--disable-single-account-mode` or an empty `--single-account-mode-domain` to the `management` container; the default domain is `netbird.selfhosted`. -- When the embedded IdP is enabled, Management derives the `HttpConfig` authentication settings from `Issuer` and ignores the `HttpConfig` section. +Or configure via environment variables on the `management` container: -Set these values in `management.json`. The only one with an environment-variable alternative is `SessionCookieEncryptionKey`, which Management also reads from `NB_IDP_SESSION_COOKIE_ENCRYPTION_KEY`. +```yaml +environment: + - NETBIRD_EMBEDDED_IDP_ENABLED=true + - NETBIRD_EMBEDDED_IDP_DATA_DIR=/var/lib/netbird/idp + - NETBIRD_ENCRYPTION_KEY=${ENCRYPTION_KEY} +``` ### Generating an Encryption Key diff --git a/src/pages/selfhosted/maintenance/configuration-files.mdx b/src/pages/selfhosted/maintenance/configuration-files.mdx index d3a0c9f0b..5fac0c222 100644 --- a/src/pages/selfhosted/maintenance/configuration-files.mdx +++ b/src/pages/selfhosted/maintenance/configuration-files.mdx @@ -61,18 +61,16 @@ The Traefik service is only present when using the built-in reverse proxy option ```yaml traefik: - image: traefik:v3.7.14 + image: traefik:v3.6 container_name: netbird-traefik restart: unless-stopped networks: [netbird] command: - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - - "--providers.docker.network=${COMPOSE_PROJECT_NAME:-}_netbird" + - "--providers.docker.network=netbird" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - - "--entrypoints.web.http.aliasHeadersStrategy=delete" - - "--entrypoints.websecure.http.aliasHeadersStrategy=delete" - "--entrypoints.websecure.transport.respondingTimeouts.readTimeout=0" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" @@ -93,8 +91,6 @@ traefik: The `readTimeout=0` setting on the websecure entrypoint disables read timeouts, which is required for long-lived gRPC and WebSocket connections used by the signal and relay services. - -The `aliasHeadersStrategy=delete` settings need Traefik v3.7.12 or later, and the installers require v3.7.13 or later. Older Traefik releases are affected by header spoofing that lets a client set the forwarded headers NetBird trusts from Traefik. See [Traefik header handling](/selfhosted/maintenance/upgrade#traefik-header-handling). ### Dashboard Service diff --git a/src/pages/selfhosted/maintenance/scaling/multiple-proxy-instances.mdx b/src/pages/selfhosted/maintenance/scaling/multiple-proxy-instances.mdx index 6b55914b9..b65484c87 100644 --- a/src/pages/selfhosted/maintenance/scaling/multiple-proxy-instances.mdx +++ b/src/pages/selfhosted/maintenance/scaling/multiple-proxy-instances.mdx @@ -163,7 +163,7 @@ Create a `docker-compose.yml` on the new server: ```yaml services: traefik: - image: traefik:v3.7.14 + image: traefik:v3.6 container_name: traefik restart: unless-stopped command: From 5201250aa9664e96fbc3b6af5149dede519a06be Mon Sep 17 00:00:00 2001 From: Jack Carter <128555021+SunsetDrifter@users.noreply.github.com> Date: Fri, 13 Nov 2026 12:39:16 +0100 Subject: [PATCH 3/9] Self-hosted: fix the multi-container Traefik example and the embedded IdP management.json block --- .../selfhosted/external-reverse-proxy.mdx | 28 ++++++++++++++++--- .../selfhosted/identity-providers/local.mdx | 28 +++++++++++-------- .../maintenance/configuration-files.mdx | 18 ++++++++---- 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/pages/selfhosted/external-reverse-proxy.mdx b/src/pages/selfhosted/external-reverse-proxy.mdx index e3d2d0e57..038b20b69 100644 --- a/src/pages/selfhosted/external-reverse-proxy.mdx +++ b/src/pages/selfhosted/external-reverse-proxy.mdx @@ -804,29 +804,47 @@ entryPoints: scheme: https ``` +**5. No timeouts on the `websecure` entrypoint.** Peers keep long-lived connections to Signal, the service they use to set up peer-to-peer connections, and to Management. Without these settings, Traefik resets each peer's Signal connection every 60 seconds and the peer has to reconnect: +```yaml +# As command args +- "--entrypoints.websecure.transport.respondingTimeouts.readTimeout=0" +- "--entrypoints.websecure.transport.respondingTimeouts.writeTimeout=0" +- "--entrypoints.websecure.transport.respondingTimeouts.idleTimeout=0" +``` + +**6. Header handling** (Traefik v3.7.13 or later). These settings make Traefik drop request headers whose names contain characters other than letters, digits and hyphens, such as `X_Forwarded_For`, which some backends read as `X-Forwarded-For`. NetBird relies on the forwarded headers Traefik sets to learn each client's address. Traefik releases older than v3.7.12 fail to start with these settings, and v3.7.13 also includes the fix for CVE-2026-88004. See [Traefik header handling](/selfhosted/maintenance/upgrade#traefik-header-handling): +```yaml +# As command args +- "--entrypoints.web.http.aliasHeadersStrategy=delete" +- "--entrypoints.websecure.http.aliasHeadersStrategy=delete" +``` + ##### Example Traefik Docker Compose -If you don't have Traefik set up yet, here's a complete example: +If you don't have Traefik set up yet, use this example: ```yaml services: traefik: - image: traefik:v3.2 + image: traefik:v3.7.14 command: - - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedByDefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - "--entrypoints.websecure.address=:443" + - "--entrypoints.websecure.transport.respondingTimeouts.readTimeout=0" + - "--entrypoints.websecure.transport.respondingTimeouts.writeTimeout=0" + - "--entrypoints.websecure.transport.respondingTimeouts.idleTimeout=0" + - "--entrypoints.web.http.aliasHeadersStrategy=delete" + - "--entrypoints.websecure.http.aliasHeadersStrategy=delete" - "--certificatesresolvers.letsencrypt.acme.email=your-email@example.com" - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web" ports: - "80:80" - "443:443" - - "8080:8080" # Traefik dashboard (optional) volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./letsencrypt:/letsencrypt @@ -838,6 +856,8 @@ networks: name: traefik-network ``` +The example pins `traefik:v3.7.14`. Requirements 5 and 6 above explain the example's two NetBird-specific groups of settings. + When using `getting-started.sh` with Traefik, the script will ask for your network name, entrypoint, and certresolver. It will then generate a docker-compose.yml with all the correct labels pre-configured. diff --git a/src/pages/selfhosted/identity-providers/local.mdx b/src/pages/selfhosted/identity-providers/local.mdx index 02c87c1f3..19a788dc3 100644 --- a/src/pages/selfhosted/identity-providers/local.mdx +++ b/src/pages/selfhosted/identity-providers/local.mdx @@ -73,26 +73,32 @@ For a full list of available configuration options, see the [config.yaml.example This section applies to deployments using the older multi-container architecture. If you deployed using `getting-started.sh`, you are on the combined setup and should use the `config.yaml` instructions above. See the [migration guide](/selfhosted/migration/combined-container) to upgrade. -Update your `management.json`: +Update your `management.json`, replacing `netbird.example.com` with your domain: ```json { + "Datadir": "/var/lib/netbird", + "DataStoreEncryptionKey": "", "EmbeddedIdP": { "Enabled": true, - "DataDir": "/var/lib/netbird/idp" - }, - "EncryptionKey": "" + "Issuer": "https://netbird.example.com/oauth2", + "DashboardRedirectURIs": [ + "https://netbird.example.com/nb-auth", + "https://netbird.example.com/nb-silent-auth" + ], + "SessionCookieEncryptionKey": "" + } } ``` -Or configure via environment variables on the `management` container: +The `/oauth2`, `/nb-auth` and `/nb-silent-auth` paths match the dashboard's default `AUTH_AUTHORITY`, `AUTH_REDIRECT_URI` and `AUTH_SILENT_REDIRECT_URI`; replace only the domain. -```yaml -environment: - - NETBIRD_EMBEDDED_IDP_ENABLED=true - - NETBIRD_EMBEDDED_IDP_DATA_DIR=/var/lib/netbird/idp - - NETBIRD_ENCRYPTION_KEY=${ENCRYPTION_KEY} -``` +- **`Issuer` is required.** Without it, Management exits at startup with `issuer is required`. +- **`DataStoreEncryptionKey`:** if your `management.json` already has one, keep it, because the stored data is encrypted with it. Otherwise, generate one with `openssl rand -base64 32`. If the field is empty or missing, Management generates a key at startup and writes it into `management.json`. That only works on a writable mount; with `management.json` mounted read-only, Management exits with `failed to save config with new encryption key`. Back up the file once the key is in it: without that key, the stored data cannot be decrypted. +- **`SessionCookieEncryptionKey`:** set it here, or in the `NB_IDP_SESSION_COOKIE_ENCRYPTION_KEY` environment variable, which Management reads only when this field is empty. Generate one with `openssl rand -base64 32`. If neither is set, the embedded IdP's session cookie is not encrypted and carries the user's ID in readable form. The key must be a 16-, 24- or 32-character string, or that many bytes base64-encoded. +- **Single account mode:** the embedded IdP requires it. These are flags on the `management` container's command. Leave out `--disable-single-account-mode`, and either leave out `--single-account-mode-domain` (the domain then defaults to `netbird.selfhosted`) or give it a non-empty value. With `--disable-single-account-mode` or an empty domain, Management exits at startup. +- **Storage:** the embedded IdP keeps its data in SQLite at `idp.db` in `Datadir`, which is `/var/lib/netbird/idp.db` with the value above. Keep `Datadir` on the `management` container's persistent volume. `EmbeddedIdP.Storage` can set another location. +- **A leftover `HttpConfig` section** from a previous identity provider does not stop Management from starting. Management overwrites its identity provider fields (such as `AuthIssuer`, `AuthAudience` and `OIDCConfigEndpoint`) with values based on `Issuer`, and logs the warning `overriding HttpConfig with EmbeddedIdP config` at each startup while the section is present. Management still reads `CertFile` and `CertKey` from the section for its own TLS certificate. Deleting the section deletes them too, and Management stops using that certificate. If you set them, keep the section and accept the warning; if you don't, delete the section to clear the warning. ### Generating an Encryption Key diff --git a/src/pages/selfhosted/maintenance/configuration-files.mdx b/src/pages/selfhosted/maintenance/configuration-files.mdx index 5fac0c222..c1b69c887 100644 --- a/src/pages/selfhosted/maintenance/configuration-files.mdx +++ b/src/pages/selfhosted/maintenance/configuration-files.mdx @@ -57,21 +57,27 @@ The `netbird-server` container combines what were previously separate management ### Traefik Service -The Traefik service is only present when using the built-in reverse proxy option (option 0 during setup). It handles automatic TLS certificate provisioning via Let's Encrypt and routes requests to the correct backend services. +The Traefik service is only present when using the built-in reverse proxy option (option 0 during setup). ```yaml traefik: - image: traefik:v3.6 + image: traefik:v3.7.14 container_name: netbird-traefik restart: unless-stopped - networks: [netbird] + networks: + netbird: + ipv4_address: 172.30.0.10 command: - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - - "--providers.docker.network=netbird" + - "--providers.docker.network=${COMPOSE_PROJECT_NAME:-}_netbird" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" + - "--entrypoints.web.http.aliasHeadersStrategy=delete" + - "--entrypoints.websecure.http.aliasHeadersStrategy=delete" - "--entrypoints.websecure.transport.respondingTimeouts.readTimeout=0" + - "--entrypoints.websecure.transport.respondingTimeouts.writeTimeout=0" + - "--entrypoints.websecure.transport.respondingTimeouts.idleTimeout=0" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true" @@ -90,7 +96,9 @@ traefik: ``` -The `readTimeout=0` setting on the websecure entrypoint disables read timeouts, which is required for long-lived gRPC and WebSocket connections used by the signal and relay services. +Without the three `respondingTimeouts` settings, Traefik resets each peer's long-lived Signal connection every 60 seconds and the peer has to reconnect. + +The `aliasHeadersStrategy` settings make Traefik drop request headers whose names contain characters other than letters, digits and hyphens, such as `X_Forwarded_For`, which some backends read as `X-Forwarded-For`. NetBird relies on the forwarded headers Traefik sets to learn each client's address. Traefik releases older than v3.7.12 fail to start with these settings. `getting-started.sh` and `getting-started-enterprise.sh` install v3.7.14 and stop with an error when `TRAEFIK_IMAGE` or `NETBIRD_TRAEFIK_TAG` names a version older than v3.7.13, the release that also fixes CVE-2026-88004. See [Traefik header handling](/selfhosted/maintenance/upgrade#traefik-header-handling). ### Dashboard Service From 75405046c6ce4de334a46b2d5acf813f7629e00c Mon Sep 17 00:00:00 2001 From: Jack Carter <128555021+SunsetDrifter@users.noreply.github.com> Date: Fri, 13 Nov 2026 13:39:52 +0100 Subject: [PATCH 4/9] Shorten the Traefik and embedded IdP notes to the required settings --- src/pages/selfhosted/external-reverse-proxy.mdx | 6 +++--- src/pages/selfhosted/identity-providers/local.mdx | 8 +++----- src/pages/selfhosted/maintenance/configuration-files.mdx | 4 +--- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/pages/selfhosted/external-reverse-proxy.mdx b/src/pages/selfhosted/external-reverse-proxy.mdx index 038b20b69..aed06a7bb 100644 --- a/src/pages/selfhosted/external-reverse-proxy.mdx +++ b/src/pages/selfhosted/external-reverse-proxy.mdx @@ -804,7 +804,7 @@ entryPoints: scheme: https ``` -**5. No timeouts on the `websecure` entrypoint.** Peers keep long-lived connections to Signal, the service they use to set up peer-to-peer connections, and to Management. Without these settings, Traefik resets each peer's Signal connection every 60 seconds and the peer has to reconnect: +**5. No timeouts on the `websecure` entrypoint.** Without these settings, Traefik resets each peer's Signal connection every 60 seconds and the peer has to reconnect: ```yaml # As command args - "--entrypoints.websecure.transport.respondingTimeouts.readTimeout=0" @@ -812,7 +812,7 @@ entryPoints: - "--entrypoints.websecure.transport.respondingTimeouts.idleTimeout=0" ``` -**6. Header handling** (Traefik v3.7.13 or later). These settings make Traefik drop request headers whose names contain characters other than letters, digits and hyphens, such as `X_Forwarded_For`, which some backends read as `X-Forwarded-For`. NetBird relies on the forwarded headers Traefik sets to learn each client's address. Traefik releases older than v3.7.12 fail to start with these settings, and v3.7.13 also includes the fix for CVE-2026-88004. See [Traefik header handling](/selfhosted/maintenance/upgrade#traefik-header-handling): +**6. Header handling** (Traefik v3.7.13 or later; releases before v3.7.12 fail to start with these settings). See [Traefik header handling](/selfhosted/maintenance/upgrade#traefik-header-handling): ```yaml # As command args - "--entrypoints.web.http.aliasHeadersStrategy=delete" @@ -856,7 +856,7 @@ networks: name: traefik-network ``` -The example pins `traefik:v3.7.14`. Requirements 5 and 6 above explain the example's two NetBird-specific groups of settings. +The example pins `traefik:v3.7.14`. Requirements 5 and 6 above cover the example's two NetBird-specific groups of settings. When using `getting-started.sh` with Traefik, the script will ask for your network name, entrypoint, and certresolver. It will then generate a docker-compose.yml with all the correct labels pre-configured. diff --git a/src/pages/selfhosted/identity-providers/local.mdx b/src/pages/selfhosted/identity-providers/local.mdx index 19a788dc3..d6ef4c6e8 100644 --- a/src/pages/selfhosted/identity-providers/local.mdx +++ b/src/pages/selfhosted/identity-providers/local.mdx @@ -94,11 +94,9 @@ Update your `management.json`, replacing `netbird.example.com` with your domain: The `/oauth2`, `/nb-auth` and `/nb-silent-auth` paths match the dashboard's default `AUTH_AUTHORITY`, `AUTH_REDIRECT_URI` and `AUTH_SILENT_REDIRECT_URI`; replace only the domain. - **`Issuer` is required.** Without it, Management exits at startup with `issuer is required`. -- **`DataStoreEncryptionKey`:** if your `management.json` already has one, keep it, because the stored data is encrypted with it. Otherwise, generate one with `openssl rand -base64 32`. If the field is empty or missing, Management generates a key at startup and writes it into `management.json`. That only works on a writable mount; with `management.json` mounted read-only, Management exits with `failed to save config with new encryption key`. Back up the file once the key is in it: without that key, the stored data cannot be decrypted. -- **`SessionCookieEncryptionKey`:** set it here, or in the `NB_IDP_SESSION_COOKIE_ENCRYPTION_KEY` environment variable, which Management reads only when this field is empty. Generate one with `openssl rand -base64 32`. If neither is set, the embedded IdP's session cookie is not encrypted and carries the user's ID in readable form. The key must be a 16-, 24- or 32-character string, or that many bytes base64-encoded. -- **Single account mode:** the embedded IdP requires it. These are flags on the `management` container's command. Leave out `--disable-single-account-mode`, and either leave out `--single-account-mode-domain` (the domain then defaults to `netbird.selfhosted`) or give it a non-empty value. With `--disable-single-account-mode` or an empty domain, Management exits at startup. -- **Storage:** the embedded IdP keeps its data in SQLite at `idp.db` in `Datadir`, which is `/var/lib/netbird/idp.db` with the value above. Keep `Datadir` on the `management` container's persistent volume. `EmbeddedIdP.Storage` can set another location. -- **A leftover `HttpConfig` section** from a previous identity provider does not stop Management from starting. Management overwrites its identity provider fields (such as `AuthIssuer`, `AuthAudience` and `OIDCConfigEndpoint`) with values based on `Issuer`, and logs the warning `overriding HttpConfig with EmbeddedIdP config` at each startup while the section is present. Management still reads `CertFile` and `CertKey` from the section for its own TLS certificate. Deleting the section deletes them too, and Management stops using that certificate. If you set them, keep the section and accept the warning; if you don't, delete the section to clear the warning. +- **`DataStoreEncryptionKey`:** if your `management.json` already has one, keep it, because the stored data is encrypted with it. Otherwise, generate one with `openssl rand -base64 32`. If you leave it empty, Management generates one and writes it into `management.json`; with the file mounted read-only, Management exits with `failed to save config with new encryption key`. Back up `management.json` once it holds the key. +- **`SessionCookieEncryptionKey`:** generate one with `openssl rand -base64 32`. Without it, the embedded IdP's session cookie is not encrypted. +- **A leftover `HttpConfig` section** from a previous identity provider does not stop Management from starting. Management overrides its identity provider fields and logs the warning `overriding HttpConfig with EmbeddedIdP config`. If the section sets `CertFile` and `CertKey`, keep it: Management still uses them for its own TLS. Otherwise, delete it to clear the warning. ### Generating an Encryption Key diff --git a/src/pages/selfhosted/maintenance/configuration-files.mdx b/src/pages/selfhosted/maintenance/configuration-files.mdx index c1b69c887..f71513b80 100644 --- a/src/pages/selfhosted/maintenance/configuration-files.mdx +++ b/src/pages/selfhosted/maintenance/configuration-files.mdx @@ -96,9 +96,7 @@ traefik: ``` -Without the three `respondingTimeouts` settings, Traefik resets each peer's long-lived Signal connection every 60 seconds and the peer has to reconnect. - -The `aliasHeadersStrategy` settings make Traefik drop request headers whose names contain characters other than letters, digits and hyphens, such as `X_Forwarded_For`, which some backends read as `X-Forwarded-For`. NetBird relies on the forwarded headers Traefik sets to learn each client's address. Traefik releases older than v3.7.12 fail to start with these settings. `getting-started.sh` and `getting-started-enterprise.sh` install v3.7.14 and stop with an error when `TRAEFIK_IMAGE` or `NETBIRD_TRAEFIK_TAG` names a version older than v3.7.13, the release that also fixes CVE-2026-88004. See [Traefik header handling](/selfhosted/maintenance/upgrade#traefik-header-handling). +Without the three `respondingTimeouts` settings, Traefik resets each peer's long-lived Signal connection every 60 seconds and the peer has to reconnect. The installers require Traefik v3.7.13 or later for the `aliasHeadersStrategy` settings; see [Traefik header handling](/selfhosted/maintenance/upgrade#traefik-header-handling). ### Dashboard Service From e5bc27c79d05659be8d3c2eb112064dfcbcc5a25 Mon Sep 17 00:00:00 2001 From: Jack Carter <128555021+SunsetDrifter@users.noreply.github.com> Date: Fri, 13 Nov 2026 14:47:10 +0100 Subject: [PATCH 5/9] Fix the embedded IdP management.json section: dashboard values, keep other keys, LetsEncryptDomain --- .../selfhosted/identity-providers/local.mdx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/pages/selfhosted/identity-providers/local.mdx b/src/pages/selfhosted/identity-providers/local.mdx index d6ef4c6e8..a595c8e2b 100644 --- a/src/pages/selfhosted/identity-providers/local.mdx +++ b/src/pages/selfhosted/identity-providers/local.mdx @@ -73,12 +73,11 @@ For a full list of available configuration options, see the [config.yaml.example This section applies to deployments using the older multi-container architecture. If you deployed using `getting-started.sh`, you are on the combined setup and should use the `config.yaml` instructions above. See the [migration guide](/selfhosted/migration/combined-container) to upgrade. -Update your `management.json`, replacing `netbird.example.com` with your domain: +Add or change these keys in your `management.json` and keep its other keys (the last note below says when to delete `HttpConfig`). In this block and the dashboard block below, replace `netbird.example.com` with your domain: ```json { - "Datadir": "/var/lib/netbird", - "DataStoreEncryptionKey": "", + "DataStoreEncryptionKey": "", "EmbeddedIdP": { "Enabled": true, "Issuer": "https://netbird.example.com/oauth2", @@ -91,12 +90,23 @@ Update your `management.json`, replacing `netbird.example.com` with your domain: } ``` -The `/oauth2`, `/nb-auth` and `/nb-silent-auth` paths match the dashboard's default `AUTH_AUTHORITY`, `AUTH_REDIRECT_URI` and `AUTH_SILENT_REDIRECT_URI`; replace only the domain. +The dashboard must use the same values. In the dashboard container's environment ([dashboard.env](/selfhosted/maintenance/configuration-files#dashboardenv)), replace your previous identity provider's settings with these lines. `AUTH_AUTHORITY` is the `Issuer` above, `AUTH_REDIRECT_URI` and `AUTH_SILENT_REDIRECT_URI` are the paths of the two `DashboardRedirectURIs`, and `AUTH_CLIENT_SECRET` stays empty: + +```bash +AUTH_AUDIENCE=netbird-dashboard +AUTH_CLIENT_ID=netbird-dashboard +AUTH_CLIENT_SECRET= +AUTH_AUTHORITY=https://netbird.example.com/oauth2 +USE_AUTH0=false +AUTH_SUPPORTED_SCOPES=openid profile email groups +AUTH_REDIRECT_URI=/nb-auth +AUTH_SILENT_REDIRECT_URI=/nb-silent-auth +``` - **`Issuer` is required.** Without it, Management exits at startup with `issuer is required`. - **`DataStoreEncryptionKey`:** if your `management.json` already has one, keep it, because the stored data is encrypted with it. Otherwise, generate one with `openssl rand -base64 32`. If you leave it empty, Management generates one and writes it into `management.json`; with the file mounted read-only, Management exits with `failed to save config with new encryption key`. Back up `management.json` once it holds the key. - **`SessionCookieEncryptionKey`:** generate one with `openssl rand -base64 32`. Without it, the embedded IdP's session cookie is not encrypted. -- **A leftover `HttpConfig` section** from a previous identity provider does not stop Management from starting. Management overrides its identity provider fields and logs the warning `overriding HttpConfig with EmbeddedIdP config`. If the section sets `CertFile` and `CertKey`, keep it: Management still uses them for its own TLS. Otherwise, delete it to clear the warning. +- **A leftover `HttpConfig` section** from a previous identity provider does not stop Management from starting. Management overrides its identity provider fields and logs the warning `overriding HttpConfig with EmbeddedIdP config`. If the section sets `LetsEncryptDomain`, or `CertFile` and `CertKey`, keep the section: Management still uses those keys for its own TLS and keeps running with the warning. If it sets neither, delete the section to clear the warning. ### Generating an Encryption Key From df704b48a68db1b8972c021137238159811bd0a2 Mon Sep 17 00:00:00 2001 From: Jack Carter <128555021+SunsetDrifter@users.noreply.github.com> Date: Fri, 13 Nov 2026 15:40:00 +0100 Subject: [PATCH 6/9] Traefik rollback note: return to v3.7.13 or newer, the installers' minimum --- src/pages/selfhosted/maintenance/upgrade.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/selfhosted/maintenance/upgrade.mdx b/src/pages/selfhosted/maintenance/upgrade.mdx index 444707fda..902f3db09 100644 --- a/src/pages/selfhosted/maintenance/upgrade.mdx +++ b/src/pages/selfhosted/maintenance/upgrade.mdx @@ -187,7 +187,7 @@ Restore the previous image digest or full version tag and any configuration chan When rolling Traefik back to a release older than `v3.7.12`, also remove the `aliasHeadersStrategy` flags added in [Traefik header handling](#traefik-header-handling). Those releases do not recognize the option and fail to start with `field not found, node: aliasHeadersStrategy`. -Traefik v3.0.0 through v3.7.11 are affected by header-alias spoofing ([GHSA-rf44-j88r-hh8c](https://github.com/traefik/traefik/security/advisories/GHSA-rf44-j88r-hh8c)). Removing `aliasHeadersStrategy` so those releases can start restores that vulnerable handling: a client can send an alias of a header Traefik wrote, and a backend that treats those names as the same variable can read the client's value instead. Do not expose an affected rollback version to untrusted traffic. Return to `v3.7.12` or newer, with `aliasHeadersStrategy` set to `delete`, once service is restored. +Traefik v3.0.0 through v3.7.11 are affected by header-alias spoofing ([GHSA-rf44-j88r-hh8c](https://github.com/traefik/traefik/security/advisories/GHSA-rf44-j88r-hh8c)). Removing `aliasHeadersStrategy` so those releases can start restores that vulnerable handling: a client can send an alias of a header Traefik wrote, and a backend that treats those names as the same variable can read the client's value instead. Do not expose an affected rollback version to untrusted traffic. Return to `v3.7.13` or newer, the minimum the installers accept, with `aliasHeadersStrategy` set to `delete`, once service is restored. If the release migrated stored data or configuration in a way the older version cannot read, stop the affected service and [restore its pre-upgrade backup](/selfhosted/maintenance/backup) before starting the older image. Changing an image tag does not undo a database migration. From 34c87d35849081f3ca7748fd69082e2e9e16600c Mon Sep 17 00:00:00 2001 From: Brandon Hopkins Date: Fri, 13 Nov 2026 08:58:15 -0800 Subject: [PATCH 7/9] Update self-hosted docs to Traefik v3.7.18 --- .../selfhosted/enterprise/getting-started.mdx | 2 +- .../selfhosted/external-reverse-proxy.mdx | 4 +++- .../maintenance/configuration-files.mdx | 6 ++++-- .../maintenance/crowdsec-dashboard.mdx | 4 ++-- .../scaling/multiple-proxy-instances.mdx | 2 +- src/pages/selfhosted/maintenance/upgrade.mdx | 18 +++++++++--------- .../migration/combined-container.mdx | 2 +- 7 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/pages/selfhosted/enterprise/getting-started.mdx b/src/pages/selfhosted/enterprise/getting-started.mdx index 6f50f7446..d94318d3f 100644 --- a/src/pages/selfhosted/enterprise/getting-started.mdx +++ b/src/pages/selfhosted/enterprise/getting-started.mdx @@ -95,7 +95,7 @@ The combined stack: | Service | Image | Notes | | --- | --- | --- | -| `traefik` | `traefik:v3.6` | TLS termination, reverse proxy, HTTP→HTTPS redirect | +| `traefik` | `traefik:v3.7.18` | TLS termination, reverse proxy, HTTP→HTTPS redirect | | `dashboard` | `ghcr.io/netbirdio/dashboard-cloud:latest` | UI | | `postgres` | `postgres:17` | Datastore for management, embedded IdP, traffic events | | `netbird-server` | `ghcr.io/netbirdio/netbird-server-cloud:latest` | Management + signal + relay + embedded STUN on UDP/3478 | diff --git a/src/pages/selfhosted/external-reverse-proxy.mdx b/src/pages/selfhosted/external-reverse-proxy.mdx index e3d2d0e57..a58e510f9 100644 --- a/src/pages/selfhosted/external-reverse-proxy.mdx +++ b/src/pages/selfhosted/external-reverse-proxy.mdx @@ -811,7 +811,7 @@ If you don't have Traefik set up yet, here's a complete example: ```yaml services: traefik: - image: traefik:v3.2 + image: traefik:v3.7.18 command: - "--api.insecure=true" - "--providers.docker=true" @@ -820,6 +820,8 @@ services: - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - "--entrypoints.websecure.address=:443" + - "--entrypoints.web.http.aliasHeadersStrategy=delete" + - "--entrypoints.websecure.http.aliasHeadersStrategy=delete" - "--certificatesresolvers.letsencrypt.acme.email=your-email@example.com" - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web" diff --git a/src/pages/selfhosted/maintenance/configuration-files.mdx b/src/pages/selfhosted/maintenance/configuration-files.mdx index dd269ffcb..034e15bed 100644 --- a/src/pages/selfhosted/maintenance/configuration-files.mdx +++ b/src/pages/selfhosted/maintenance/configuration-files.mdx @@ -43,7 +43,7 @@ The Docker Compose file defines all NetBird services, their dependencies, networ | Service | Image | Internal Port | External (Exposed) | Description | |---------|-------|---------------|-------------------|-------------| -| `traefik` | `traefik:v3.6` | 80, 443 | 80:80, 443:443 | Handles TLS termination via Let's Encrypt and routes incoming HTTPS requests to the appropriate NetBird services. Only included when using the built-in Traefik option (option 0). | +| `traefik` | `traefik:v3.7.18` | 80, 443 | 80:80, 443:443 | Handles TLS termination via Let's Encrypt and routes incoming HTTPS requests to the appropriate NetBird services. Only included when using the built-in Traefik option (option 0). | | `dashboard` | `netbirdio/dashboard` | 80 | 8080:80 | The web-based management console where administrators configure networks, manage peers, create access policies, and view activity logs. Includes an embedded nginx server for serving the UI. | | `netbird-server` | `netbirdio/netbird-server` | 80, 3478/udp | 8081:80, 3478:3478/udp | Combined server that includes management, signal, relay, and embedded STUN in a single container. Configured via `config.yaml`. | @@ -61,7 +61,7 @@ The Traefik service is only present when using the built-in reverse proxy option ```yaml traefik: - image: traefik:v3.6 + image: traefik:v3.7.18 container_name: netbird-traefik restart: unless-stopped networks: [netbird] @@ -71,6 +71,8 @@ traefik: - "--providers.docker.network=netbird" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" + - "--entrypoints.web.http.aliasHeadersStrategy=delete" + - "--entrypoints.websecure.http.aliasHeadersStrategy=delete" - "--entrypoints.websecure.transport.respondingTimeouts.readTimeout=0" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" diff --git a/src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx b/src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx index dd9699d2c..581553ba7 100644 --- a/src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx +++ b/src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx @@ -134,12 +134,12 @@ Start from the `docker-compose.yml` generated by the quickstart with proxy Crowd - After manual setup, set `NETBIRD_CROWDSEC_APPSEC_ENABLED=yes` in `.env` before rerunning the enterprise script with `--enable-proxy` or `--enable-traffic-events`. -For quickstart installs, set the Traefik image to the validated version below. Add the header handling and bouncer plugin arguments to the existing `services.traefik.command` list: +For quickstart installs, set the Traefik image to `v3.7.18`. Add the header handling and bouncer plugin arguments to the existing `services.traefik.command` list: ```yaml services: traefik: - image: traefik:v3.7.14 + image: traefik:v3.7.18 command: - '--entrypoints.web.http.aliasHeadersStrategy=delete' - '--entrypoints.websecure.http.aliasHeadersStrategy=delete' diff --git a/src/pages/selfhosted/maintenance/scaling/multiple-proxy-instances.mdx b/src/pages/selfhosted/maintenance/scaling/multiple-proxy-instances.mdx index b65484c87..a3c3c6ae4 100644 --- a/src/pages/selfhosted/maintenance/scaling/multiple-proxy-instances.mdx +++ b/src/pages/selfhosted/maintenance/scaling/multiple-proxy-instances.mdx @@ -163,7 +163,7 @@ Create a `docker-compose.yml` on the new server: ```yaml services: traefik: - image: traefik:v3.6 + image: traefik:v3.7.18 container_name: traefik restart: unless-stopped command: diff --git a/src/pages/selfhosted/maintenance/upgrade.mdx b/src/pages/selfhosted/maintenance/upgrade.mdx index 444707fda..63734714d 100644 --- a/src/pages/selfhosted/maintenance/upgrade.mdx +++ b/src/pages/selfhosted/maintenance/upgrade.mdx @@ -86,23 +86,21 @@ Traefik and CrowdSec have their own release cycles. Upgrading NetBird does not u ### Choosing versions -The following dependency versions have been validated for self-hosted NetBird as of **October 9, 2026**: +The following version pins are used in these self-hosted NetBird examples: -| Component | Validated version | +| Component | Version | | --- | --- | -| Bundled Traefik | `traefik:v3.7.14` | +| Bundled Traefik | `traefik:v3.7.18` | | CrowdSec | `crowdsecurity/crowdsec:v1.8.1` | | Traefik CrowdSec bouncer plugin | `v1.7.1` | -Installer defaults can differ from these versions. Check the image tags in your `docker-compose.yml`; the bouncer plugin is configured separately in the [dashboard AppSec guide](/selfhosted/maintenance/crowdsec-dashboard#update-docker-compose-yml). Before adopting a newer release, review its migration requirements and verify it with your NetBird configuration in a test deployment. - - **Traefik:** check the [releases](https://github.com/traefik/traefik/releases), [support policy](https://doc.traefik.io/traefik/deprecation/releases/), and [v3 migration notes](https://doc.traefik.io/traefik/migrate/v3/). Configuration changes can be required within the same major version. - **CrowdSec:** check the [releases](https://github.com/crowdsecurity/crowdsec/releases) and [Docker documentation](https://docs.crowdsec.net/u/getting_started/installation/docker/). - **Dashboard AppSec:** also check the [Traefik bouncer plugin releases](https://github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/releases). The plugin version is set by `--experimental.plugins.bouncer.version` in Traefik's `command:` list. Changing the CrowdSec image does not update it. A new installation can turn AppSec on from the quickstart or the enterprise script. The quickstart never changes an existing install, and the enterprise script adds AppSec only together with the proxy through `--enable-proxy`. To add AppSec to a running proxy, follow the [dashboard guide](/selfhosted/maintenance/crowdsec-dashboard). You can subscribe to release notifications on each GitHub repository using **Watch → Custom → Releases**. Check security advisories and upstream support status as well as the installer defaults when planning updates. -Full version tags such as `traefik:v3.7.14` and `crowdsecurity/crowdsec:v1.8.1` select specific releases. A minor-version tag such as `traefik:v3.6` can fetch a newer patch image without a Compose edit. For controlled rollouts, use full version tags for all images, or image digests when you need immutable references, and update them deliberately after validation. +Full version tags such as `traefik:v3.7.18` and `crowdsecurity/crowdsec:v1.8.1` select specific releases. A minor-version tag such as `traefik:v3.7` can fetch a newer patch image without a Compose edit. For controlled rollouts, use full version tags for all images, or image digests when you need immutable references, and update them deliberately after validation. ### Before changing a version @@ -133,6 +131,8 @@ Full version tags such as `traefik:v3.7.14` and `crowdsecurity/crowdsec:v1.8.1` Change one component at a time so you can identify regressions. Edit that service's `image:` in `docker-compose.yml` to the version or digest you selected, preserving its existing environment, labels, networks, and volume mounts. If the image is supplied through a Compose variable, update that variable instead. +For this Traefik update, set `image: traefik:v3.7.18` on the `traefik` service in a quickstart installation. For an enterprise installation using `image: traefik:${NETBIRD_TRAEFIK_TAG}`, set `NETBIRD_TRAEFIK_TAG=v3.7.18` in the Compose project's `.env` file. + For Traefik, also check [header handling](#traefik-header-handling) and the [Docker network setting](#traefik-docker-network) before recreating the container. Validate the configuration before applying it: @@ -182,12 +182,12 @@ Record the exact image versions or digests, plugin version, and test results wit ### Roll back a dependency update -Restore the previous image digest or full version tag and any configuration changed for that component, including the bouncer plugin version. Recreate the affected service with `docker compose up -d --no-deps ` and repeat the verification checks. Restoring a moving tag such as `v3.6` or `latest` may fetch the upgraded image again; use the recorded digest to recover the exact previous image. +Restore the previous image digest or full version tag and any configuration changed for that component, including the bouncer plugin version. Recreate the affected service with `docker compose up -d --no-deps ` and repeat the verification checks. Restoring a moving tag such as `v3.7` or `latest` may fetch the upgraded image again; use the recorded digest to recover the exact previous image. When rolling Traefik back to a release older than `v3.7.12`, also remove the `aliasHeadersStrategy` flags added in [Traefik header handling](#traefik-header-handling). Those releases do not recognize the option and fail to start with `field not found, node: aliasHeadersStrategy`. -Traefik v3.0.0 through v3.7.11 are affected by header-alias spoofing ([GHSA-rf44-j88r-hh8c](https://github.com/traefik/traefik/security/advisories/GHSA-rf44-j88r-hh8c)). Removing `aliasHeadersStrategy` so those releases can start restores that vulnerable handling: a client can send an alias of a header Traefik wrote, and a backend that treats those names as the same variable can read the client's value instead. Do not expose an affected rollback version to untrusted traffic. Return to `v3.7.12` or newer, with `aliasHeadersStrategy` set to `delete`, once service is restored. +Traefik v3.0.0 through v3.7.11 are affected by header-alias spoofing ([GHSA-rf44-j88r-hh8c](https://github.com/traefik/traefik/security/advisories/GHSA-rf44-j88r-hh8c)). Removing `aliasHeadersStrategy` so those releases can start restores that vulnerable handling: a client can send an alias of a header Traefik wrote, and a backend that treats those names as the same variable can read the client's value instead. Do not expose an affected rollback version to untrusted traffic. Return to `v3.7.18` or a newer release you have verified, with `aliasHeadersStrategy` set to `delete`, once service is restored. If the release migrated stored data or configuration in a way the older version cannot read, stop the affected service and [restore its pre-upgrade backup](/selfhosted/maintenance/backup) before starting the older image. Changing an image tag does not undo a database migration. @@ -196,7 +196,7 @@ If the release migrated stored data or configuration in a way the older version ### Traefik header handling -Traefik v3.7.12 introduced `aliasHeadersStrategy` and warns when it is unset. When upgrading to the validated `v3.7.14` version, add these settings to the existing `services.traefik.command` list in `docker-compose.yml`: +Traefik v3.7.12 introduced `aliasHeadersStrategy` and warns when it is unset. The installers require at least `v3.7.13` for the option's header-sanitization fix. When upgrading to `v3.7.18`, add these settings to the existing `services.traefik.command` list in `docker-compose.yml`: ```yaml - '--entrypoints.web.http.aliasHeadersStrategy=delete' diff --git a/src/pages/selfhosted/migration/combined-container.mdx b/src/pages/selfhosted/migration/combined-container.mdx index 66b515972..81af26bac 100644 --- a/src/pages/selfhosted/migration/combined-container.mdx +++ b/src/pages/selfhosted/migration/combined-container.mdx @@ -22,7 +22,7 @@ The migration script **exits with an error** if it detects an external identity ### What's new - **Combined `netbird-server` container** - management, signal, relay, and the embedded STUN server all run in a single container, replacing four separate services -- **Traefik reverse proxy** (for Caddy-based setups) - replaces the embedded Caddy with Traefik v3.6, handling TLS termination via Let's Encrypt and routing for gRPC, WebSocket, and HTTP traffic +- **Traefik reverse proxy** (for Caddy-based setups) - replaces the embedded Caddy with Traefik v3.7.18, handling TLS termination via Let's Encrypt and routing for gRPC, WebSocket, and HTTP traffic - **Unified `config.yaml`** - a single configuration file replaces the combination of `management.json`, `setup.env`, and multiple environment files - **Simplified Docker Compose** - the stack goes from 5+ services down to 2 (Traefik + `netbird-server`) or 3 (with dashboard) From 90a544be31cbc657696de20e3d329e47d0042bfb Mon Sep 17 00:00:00 2001 From: Jack Carter <128555021+SunsetDrifter@users.noreply.github.com> Date: Fri, 13 Nov 2026 18:10:38 +0100 Subject: [PATCH 8/9] Revert "Traefik rollback note: return to v3.7.13 or newer, the installers' minimum" This reverts commit df704b48a68db1b8972c021137238159811bd0a2. --- src/pages/selfhosted/maintenance/upgrade.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/selfhosted/maintenance/upgrade.mdx b/src/pages/selfhosted/maintenance/upgrade.mdx index 902f3db09..444707fda 100644 --- a/src/pages/selfhosted/maintenance/upgrade.mdx +++ b/src/pages/selfhosted/maintenance/upgrade.mdx @@ -187,7 +187,7 @@ Restore the previous image digest or full version tag and any configuration chan When rolling Traefik back to a release older than `v3.7.12`, also remove the `aliasHeadersStrategy` flags added in [Traefik header handling](#traefik-header-handling). Those releases do not recognize the option and fail to start with `field not found, node: aliasHeadersStrategy`. -Traefik v3.0.0 through v3.7.11 are affected by header-alias spoofing ([GHSA-rf44-j88r-hh8c](https://github.com/traefik/traefik/security/advisories/GHSA-rf44-j88r-hh8c)). Removing `aliasHeadersStrategy` so those releases can start restores that vulnerable handling: a client can send an alias of a header Traefik wrote, and a backend that treats those names as the same variable can read the client's value instead. Do not expose an affected rollback version to untrusted traffic. Return to `v3.7.13` or newer, the minimum the installers accept, with `aliasHeadersStrategy` set to `delete`, once service is restored. +Traefik v3.0.0 through v3.7.11 are affected by header-alias spoofing ([GHSA-rf44-j88r-hh8c](https://github.com/traefik/traefik/security/advisories/GHSA-rf44-j88r-hh8c)). Removing `aliasHeadersStrategy` so those releases can start restores that vulnerable handling: a client can send an alias of a header Traefik wrote, and a backend that treats those names as the same variable can read the client's value instead. Do not expose an affected rollback version to untrusted traffic. Return to `v3.7.12` or newer, with `aliasHeadersStrategy` set to `delete`, once service is restored. If the release migrated stored data or configuration in a way the older version cannot read, stop the affected service and [restore its pre-upgrade backup](/selfhosted/maintenance/backup) before starting the older image. Changing an image tag does not undo a database migration. From cf7a4146612fe57fb6be2cf9494277631ad46f71 Mon Sep 17 00:00:00 2001 From: Jack Carter <128555021+SunsetDrifter@users.noreply.github.com> Date: Fri, 13 Nov 2026 18:13:35 +0100 Subject: [PATCH 9/9] Move the Traefik pins to v3.7.18 and limit the management.json section to new deployments --- src/pages/selfhosted/enterprise/getting-started.mdx | 2 +- src/pages/selfhosted/external-reverse-proxy.mdx | 6 +++--- src/pages/selfhosted/identity-providers/local.mdx | 9 +++++++-- src/pages/selfhosted/maintenance/configuration-files.mdx | 4 ++-- src/pages/selfhosted/migration/combined-container.mdx | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/pages/selfhosted/enterprise/getting-started.mdx b/src/pages/selfhosted/enterprise/getting-started.mdx index 085cae7b6..88d64b488 100644 --- a/src/pages/selfhosted/enterprise/getting-started.mdx +++ b/src/pages/selfhosted/enterprise/getting-started.mdx @@ -94,7 +94,7 @@ The combined stack: | Service | Image | Notes | | --- | --- | --- | -| `traefik` | `traefik:v3.7.14` | TLS termination, reverse proxy, HTTP→HTTPS redirect | +| `traefik` | `traefik:v3.7.18` | TLS termination, reverse proxy, HTTP→HTTPS redirect | | `dashboard` | `ghcr.io/netbirdio/dashboard-cloud:latest` | UI | | `postgres` | `postgres:17` | Datastore for management, embedded IdP, traffic events | | `netbird-server` | `ghcr.io/netbirdio/netbird-server-cloud:latest` | Management + signal + relay + embedded STUN on UDP/3478 | diff --git a/src/pages/selfhosted/external-reverse-proxy.mdx b/src/pages/selfhosted/external-reverse-proxy.mdx index aed06a7bb..8a8568b6c 100644 --- a/src/pages/selfhosted/external-reverse-proxy.mdx +++ b/src/pages/selfhosted/external-reverse-proxy.mdx @@ -812,7 +812,7 @@ entryPoints: - "--entrypoints.websecure.transport.respondingTimeouts.idleTimeout=0" ``` -**6. Header handling** (Traefik v3.7.13 or later; releases before v3.7.12 fail to start with these settings). See [Traefik header handling](/selfhosted/maintenance/upgrade#traefik-header-handling): +**6. Header handling** (Traefik v3.7.18 or later: earlier v3.7 releases are affected by published Traefik security advisories, and releases before v3.7.12 fail to start with these settings). See [Traefik header handling](/selfhosted/maintenance/upgrade#traefik-header-handling): ```yaml # As command args - "--entrypoints.web.http.aliasHeadersStrategy=delete" @@ -826,7 +826,7 @@ If you don't have Traefik set up yet, use this example: ```yaml services: traefik: - image: traefik:v3.7.14 + image: traefik:v3.7.18 command: - "--providers.docker=true" - "--providers.docker.exposedByDefault=false" @@ -856,7 +856,7 @@ networks: name: traefik-network ``` -The example pins `traefik:v3.7.14`. Requirements 5 and 6 above cover the example's two NetBird-specific groups of settings. +The example pins `traefik:v3.7.18`. Requirements 5 and 6 above cover the example's two NetBird-specific groups of settings. When using `getting-started.sh` with Traefik, the script will ask for your network name, entrypoint, and certresolver. It will then generate a docker-compose.yml with all the correct labels pre-configured. diff --git a/src/pages/selfhosted/identity-providers/local.mdx b/src/pages/selfhosted/identity-providers/local.mdx index a595c8e2b..9d1f53269 100644 --- a/src/pages/selfhosted/identity-providers/local.mdx +++ b/src/pages/selfhosted/identity-providers/local.mdx @@ -73,6 +73,10 @@ For a full list of available configuration options, see the [config.yaml.example This section applies to deployments using the older multi-container architecture. If you deployed using `getting-started.sh`, you are on the combined setup and should use the `config.yaml` instructions above. See the [migration guide](/selfhosted/migration/combined-container) to upgrade. + +Use this section only on a new deployment that has no users yet. It does not move existing users. On a deployment that already has users from another identity provider, Management starts, but the embedded identity provider has none of them and the dashboard's first-user setup page does not appear, so nobody can sign in. To switch such a deployment, follow [Migration Guide: External IdP to Embedded IdP](/selfhosted/migration/external-to-embedded-idp) instead. + + Add or change these keys in your `management.json` and keep its other keys (the last note below says when to delete `HttpConfig`). In this block and the dashboard block below, replace `netbird.example.com` with your domain: ```json @@ -90,7 +94,7 @@ Add or change these keys in your `management.json` and keep its other keys (the } ``` -The dashboard must use the same values. In the dashboard container's environment ([dashboard.env](/selfhosted/maintenance/configuration-files#dashboardenv)), replace your previous identity provider's settings with these lines. `AUTH_AUTHORITY` is the `Issuer` above, `AUTH_REDIRECT_URI` and `AUTH_SILENT_REDIRECT_URI` are the paths of the two `DashboardRedirectURIs`, and `AUTH_CLIENT_SECRET` stays empty: +The dashboard must use the same values. In the dashboard container's environment ([dashboard.env](/selfhosted/maintenance/configuration-files#dashboardenv)), set these lines. `AUTH_AUTHORITY` is the `Issuer` above, `AUTH_REDIRECT_URI` and `AUTH_SILENT_REDIRECT_URI` are the paths of the two `DashboardRedirectURIs`, and `AUTH_CLIENT_SECRET` stays empty: ```bash AUTH_AUDIENCE=netbird-dashboard @@ -104,9 +108,10 @@ AUTH_SILENT_REDIRECT_URI=/nb-silent-auth ``` - **`Issuer` is required.** Without it, Management exits at startup with `issuer is required`. +- **Your reverse proxy must route `/oauth2/*` to Management.** The embedded identity provider is served there, and the dashboard sends sign-ins to the `Issuer` URL. - **`DataStoreEncryptionKey`:** if your `management.json` already has one, keep it, because the stored data is encrypted with it. Otherwise, generate one with `openssl rand -base64 32`. If you leave it empty, Management generates one and writes it into `management.json`; with the file mounted read-only, Management exits with `failed to save config with new encryption key`. Back up `management.json` once it holds the key. - **`SessionCookieEncryptionKey`:** generate one with `openssl rand -base64 32`. Without it, the embedded IdP's session cookie is not encrypted. -- **A leftover `HttpConfig` section** from a previous identity provider does not stop Management from starting. Management overrides its identity provider fields and logs the warning `overriding HttpConfig with EmbeddedIdP config`. If the section sets `LetsEncryptDomain`, or `CertFile` and `CertKey`, keep the section: Management still uses those keys for its own TLS and keeps running with the warning. If it sets neither, delete the section to clear the warning. +- **A leftover `HttpConfig` section** does not stop Management from starting. Management overrides its identity provider fields and logs the warning `overriding HttpConfig with EmbeddedIdP config`. If the section sets `LetsEncryptDomain`, or `CertFile` and `CertKey`, keep the section: Management still uses those keys for its own TLS and keeps running with the warning. If it sets neither, delete the section to clear the warning. ### Generating an Encryption Key diff --git a/src/pages/selfhosted/maintenance/configuration-files.mdx b/src/pages/selfhosted/maintenance/configuration-files.mdx index f71513b80..a5aed3888 100644 --- a/src/pages/selfhosted/maintenance/configuration-files.mdx +++ b/src/pages/selfhosted/maintenance/configuration-files.mdx @@ -43,7 +43,7 @@ The Docker Compose file defines all NetBird services, their dependencies, networ | Service | Image | Internal Port | External (Exposed) | Description | |---------|-------|---------------|-------------------|-------------| -| `traefik` | `traefik:v3.7.14` | 80, 443 | 80:80, 443:443 | Handles TLS termination via Let's Encrypt and routes incoming HTTPS requests to the appropriate NetBird services. Only included when using the built-in Traefik option (option 0). | +| `traefik` | `traefik:v3.7.18` | 80, 443 | 80:80, 443:443 | Handles TLS termination via Let's Encrypt and routes incoming HTTPS requests to the appropriate NetBird services. Only included when using the built-in Traefik option (option 0). | | `dashboard` | `netbirdio/dashboard` | 80 | 8080:80 | The web-based management console where administrators configure networks, manage peers, create access policies, and view activity logs. Includes an embedded nginx server for serving the UI. | | `netbird-server` | `netbirdio/netbird-server` | 80, 3478/udp | 8081:80, 3478:3478/udp | Combined server that includes management, signal, relay, and embedded STUN in a single container. Configured via `config.yaml`. | @@ -61,7 +61,7 @@ The Traefik service is only present when using the built-in reverse proxy option ```yaml traefik: - image: traefik:v3.7.14 + image: traefik:v3.7.18 container_name: netbird-traefik restart: unless-stopped networks: diff --git a/src/pages/selfhosted/migration/combined-container.mdx b/src/pages/selfhosted/migration/combined-container.mdx index 60afa2a49..81af26bac 100644 --- a/src/pages/selfhosted/migration/combined-container.mdx +++ b/src/pages/selfhosted/migration/combined-container.mdx @@ -22,7 +22,7 @@ The migration script **exits with an error** if it detects an external identity ### What's new - **Combined `netbird-server` container** - management, signal, relay, and the embedded STUN server all run in a single container, replacing four separate services -- **Traefik reverse proxy** (for Caddy-based setups) - replaces the embedded Caddy with Traefik v3.7.14, handling TLS termination via Let's Encrypt and routing for gRPC, WebSocket, and HTTP traffic +- **Traefik reverse proxy** (for Caddy-based setups) - replaces the embedded Caddy with Traefik v3.7.18, handling TLS termination via Let's Encrypt and routing for gRPC, WebSocket, and HTTP traffic - **Unified `config.yaml`** - a single configuration file replaces the combination of `management.json`, `setup.env`, and multiple environment files - **Simplified Docker Compose** - the stack goes from 5+ services down to 2 (Traefik + `netbird-server`) or 3 (with dashboard)