From c3ae458a95c7b43b260c8cfc5d1d9c7567b596dc Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 4 Jun 2026 11:37:21 -0400 Subject: [PATCH] fix(config): let env vars override per-role appsettings overlay + correct dev-compose Ldap section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The per-role overlay (appsettings.{role}.json) was appended after WebApplicationBuilder's default sources, so it outranked environment variables — a baked role file could not be overridden by a deployment env var. In otopcua-dev this meant appsettings.admin.json's Security:Ldap:DevStubMode=false beat the compose's DevStub override, so every AdminUI login attempted a real LDAP bind against a non-existent server and failed with 'Unexpected authentication error'. - Program.cs: re-append AddEnvironmentVariables() + AddCommandLine(args) after the role overlay so deployment overrides keep top precedence (overlay still beats base appsettings). - docker-dev/docker-compose.yml: the DevStub env var targeted the stale 'Authentication:Ldap' section; the code reads 'Security:Ldap'. Corrected the prefix on every host node (+ comment). Dev AdminUI login now signs in as Administrator via the DevStub bypass. --- docker-dev/docker-compose.yml | 16 ++++++++-------- src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs | 10 ++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/docker-dev/docker-compose.yml b/docker-dev/docker-compose.yml index 15e99758..2315718a 100644 --- a/docker-dev/docker-compose.yml +++ b/docker-dev/docker-compose.yml @@ -74,8 +74,8 @@ services: # OpenLDAP was previously here but the bitnami/openldap:2.6 image was retired # (manifest gone) and bitnamilegacy/openldap:2.6 crashes during LDIF setup with # exit 68. For the dev compose every host container now runs with - # Authentication__Ldap__DevStubMode=true, so any non-empty username/password - # signs in as `FleetAdmin`. Restore a real LDAP service when there's a need + # Security__Ldap__DevStubMode=true, so any non-empty username/password + # signs in as `Administrator`. Restore a real LDAP service when there's a need # for end-to-end LDAP coverage (the host code path is unchanged). admin-a: &otopcua-host @@ -97,7 +97,7 @@ services: Security__Jwt__SigningKey: "docker-dev-signing-key-with-at-least-32-bytes-of-utf8-content-12345" Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" - Authentication__Ldap__DevStubMode: "true" + Security__Ldap__DevStubMode: "true" GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua2_GI7-tNozYE6cXGUSgEzL3AHDV7bYcYIHdMwKYgyHdX4}" admin-b: @@ -114,7 +114,7 @@ services: Security__Jwt__SigningKey: "docker-dev-signing-key-with-at-least-32-bytes-of-utf8-content-12345" Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" - Authentication__Ldap__DevStubMode: "true" + Security__Ldap__DevStubMode: "true" GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua2_GI7-tNozYE6cXGUSgEzL3AHDV7bYcYIHdMwKYgyHdX4}" driver-a: @@ -167,7 +167,7 @@ services: Security__Jwt__SigningKey: "docker-dev-signing-key-with-at-least-32-bytes-of-utf8-content-12345" Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" - Authentication__Ldap__DevStubMode: "true" + Security__Ldap__DevStubMode: "true" GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua2_GI7-tNozYE6cXGUSgEzL3AHDV7bYcYIHdMwKYgyHdX4}" ports: - "4842:4840" @@ -190,7 +190,7 @@ services: Security__Jwt__SigningKey: "docker-dev-signing-key-with-at-least-32-bytes-of-utf8-content-12345" Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" - Authentication__Ldap__DevStubMode: "true" + Security__Ldap__DevStubMode: "true" GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua2_GI7-tNozYE6cXGUSgEzL3AHDV7bYcYIHdMwKYgyHdX4}" ports: - "4843:4840" @@ -212,7 +212,7 @@ services: Security__Jwt__SigningKey: "docker-dev-signing-key-with-at-least-32-bytes-of-utf8-content-12345" Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" - Authentication__Ldap__DevStubMode: "true" + Security__Ldap__DevStubMode: "true" GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua2_GI7-tNozYE6cXGUSgEzL3AHDV7bYcYIHdMwKYgyHdX4}" ports: - "4844:4840" @@ -235,7 +235,7 @@ services: Security__Jwt__SigningKey: "docker-dev-signing-key-with-at-least-32-bytes-of-utf8-content-12345" Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" - Authentication__Ldap__DevStubMode: "true" + Security__Ldap__DevStubMode: "true" GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua2_GI7-tNozYE6cXGUSgEzL3AHDV7bYcYIHdMwKYgyHdX4}" ports: - "4845:4840" diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs index c44c5171..fafbb6d4 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs @@ -49,8 +49,18 @@ builder.Host.UseWindowsService(options => options.ServiceName = "OtOpcUaHost"); // (both). Optional — base appsettings.json carries enough to boot if these don't exist. var roleSuffix = roles.Length == 0 ? null : string.Join('-', roles.OrderBy(r => r, StringComparer.Ordinal)); if (roleSuffix is not null) +{ builder.Configuration.AddJsonFile($"appsettings.{roleSuffix}.json", optional: true, reloadOnChange: true); + // The overlay above is appended AFTER WebApplicationBuilder's default sources, so without this it + // would outrank environment variables and command-line args — i.e. a baked role file could not be + // overridden by a deployment env var (e.g. the dev compose's Security__Ldap__DevStubMode=true was + // silently beaten by appsettings.admin.json's DevStubMode:false). Re-append env vars then args last + // so deployment overrides keep top precedence; the overlay still wins over base appsettings.json. + builder.Configuration.AddEnvironmentVariables(); + builder.Configuration.AddCommandLine(args); +} + // Serilog — shared ZB.MOM.WW.Telemetry bootstrap. Sinks (Console + rolling daily file) // now live in appsettings.json (ReadFrom.Configuration); AddZbSerilog layers in the // shared NodeHostname / TraceContext / Redaction enrichers and trace correlation.