Files
Joseph Doherty 5f97c9d1ed docs(glauth): point all dev/test LDAP at the shared GLAuth on 10.100.0.35
deployment.md / CLAUDE.md / env_vars.md: the per-app LDAP (scadabridge-ldap
container, OtOpcUa DevStubMode, per-box C:\publish\glauth) is replaced by one
shared zb-shared-glauth on 10.100.0.35:3893 (dc=zb,dc=local); source of truth
infra/glauth/. Fixed stale baseDNs (dc=lmxopcua/dc=otopcua -> dc=zb).
2026-06-04 16:37:52 -04:00

20 KiB
Raw Permalink Blame History

Environment Variables — SCADA/OT family

Cross-project audit of every environment variable used or read by the sister projects and the shared ZB.MOM.WW.* libraries. Compiled 2026-06-03 by sweeping C# reads, Docker/compose, launchSettings.json, shell/PowerShell scripts, and CI for each repo. This is a summary index — when a value matters operationally, confirm against the cited file:line in the owning repo (paths below are relative to each project root).

Scope

Project Root Covered
OtOpcUa ~/Desktop/OtOpcUa Host, Galaxy/Historian drivers, docker-dev, tests, CI
MxAccessGateway ~/Desktop/MxAccessGateway Server (x64), Worker (x86), client CLI, tests, pack script
ScadaBridge ~/Desktop/ScadaBridge Host (Central/Site), CLI, docker/, docker-env2/, infra/, tests
Shared libs ~/Desktop/scadaproj/ZB.MOM.WW.* Auth, Theme, Health, Telemetry, Configuration, Audit (code + build scripts)

How env vars reach these apps

All four .NET apps call AddEnvironmentVariables(), so any configuration key is overridable from the environment using the double-underscore (__) → colon (:) convention (ScadaBridge__InboundApi__ApiKeyPepper overrides ScadaBridge:InboundApi:ApiKeyPepper). Array indices use a trailing __0, __1 (Cluster__SeedNodes__0). Because every options key is technically settable this way, the tables below split into:

  • Direct reads / operationally-setEnvironment.GetEnvironmentVariable(...) in code, or values actually set in compose/launchSettings/scripts. These are the ones you'll really touch.
  • Config keys overridable via __ — the validated/notable options that are normally in appsettings*.json but are commonly (or required to be) supplied via environment in containers. Not every options key is reproduced — only validated, secret, or container-set ones.

Secrets: rows marked 🔒 are secrets. Per the Auth/Config normalization, peppers/keys/passwords are per-environment secrets injected out-of-band (secret store / orchestrator), never committed. The dev-only values that do appear in compose are explicitly insecure placeholders for the local clusters — see scadabridge-local-deploy-gotchas and docs/operations/inbound-api-key-reissue.md.


1. OtOpcUa

1.1 Direct reads / operationally-set (runtime)

Variable Where Purpose Req? / default Process
OTOPCUA_ROLES src/Server/.../Host/Program.cs:31 Comma-list of roles (admin, driver) for conditional wiring optional Host
ASPNETCORE_ENVIRONMENT Host/Properties/launchSettings.json:9 ASP.NET Core environment optional / Production Host
ASPNETCORE_URLS docker-dev/docker-compose.yml Kestrel bind address/port optional Host
GALAXY_MXGW_API_KEY 🔒 docker-dev/docker-compose.yml; resolved in GalaxyDriver.cs:466 mxaccessgw API key for the Galaxy driver required if Galaxy driver deployed Driver (Galaxy)
OTOPCUA_CONFIG_CONNECTION 🔒 Configuration/DesignTimeDbContextFactory.cs:25 SQL connection for EF design-time (dotnet ef) required for migrations tooling build-time
OTOPCUA_HISTORIAN_PIPE Driver.Historian.Wonderware/Program.cs:32 Named-pipe name for the Historian sidecar IPC required Historian sidecar
OTOPCUA_ALLOWED_SID …/Program.cs:34 Windows SID allowed to connect to the sidecar required Historian sidecar
OTOPCUA_HISTORIAN_SECRET 🔒 …/Program.cs:36 Shared secret for named-pipe auth required Historian sidecar
OTOPCUA_HISTORIAN_ENABLED …/Program.cs:48 Init the Historian SDK (else pipe-only) optional / false Historian sidecar
OTOPCUA_HISTORIAN_SERVER …/Program.cs:89 Wonderware Historian host optional / localhost Historian sidecar
OTOPCUA_HISTORIAN_PORT …/Program.cs:90 Historian port optional / 32568 Historian sidecar
OTOPCUA_HISTORIAN_INTEGRATED …/Program.cs:91 Use Windows Integrated Security optional / true Historian sidecar
OTOPCUA_HISTORIAN_USER …/Program.cs:92 SQL user (when not integrated) optional Historian sidecar
OTOPCUA_HISTORIAN_PASS 🔒 …/Program.cs:93 SQL password (when not integrated) optional Historian sidecar
OTOPCUA_HISTORIAN_TIMEOUT_SEC …/Program.cs:94 SQL command timeout (s) optional / 30 Historian sidecar
OTOPCUA_HISTORIAN_MAX_VALUES …/Program.cs:95 Max values per read query optional / 10000 Historian sidecar
OTOPCUA_HISTORIAN_COOLDOWN_SEC …/Program.cs:96 Failure cooldown before retry (s) optional / 60 Historian sidecar
OTOPCUA_HISTORIAN_SERVERS …/Program.cs:99 Comma-list of historian servers for failover optional Historian sidecar
OTOPCUA_HISTORIAN_ALARM_WRITE_ENABLED …/Program.cs:125 Enable alarm-event writer to historian optional / true when enabled Historian sidecar

The Galaxy API key supports prefix resolution (GalaxyDriver.cs:466): env:VAR (read another env var), file:PATH (read a file), dev:LITERAL (dev only), or an unprefixed literal.

1.2 Config keys overridable via __ (set in docker-dev/docker-compose.yml)

Variable Purpose Req?
ConnectionStrings__ConfigDb 🔒 SQL connection for the OtOpcUa config DB required
Cluster__Hostname / Cluster__Port / Cluster__PublicHostname Akka remoting bind/advertise required
Cluster__SeedNodes__0 Initial seed node URL required
Cluster__Roles__0 (__1) Cluster role assignment (admin/driver) required
Security__Jwt__SigningKey 🔒 JWT signing key (≥32 bytes) required
Security__Jwt__Issuer / Security__Jwt__Audience JWT claims required
Security__Ldap__* LDAP host/bind (in appsettings.admin*.json) per-deploy
Authentication__Ldap__DevStubMode Dev LDAP stub (any user → FleetAdmin) — removed from docker-dev; docker-dev now binds the shared GLAuth on 10.100.0.35:3893 optional / false

1.3 Docker infra & test fixtures (OtOpcUa)

  • docker-dev SQL: ACCEPT_EULA=Y, SA_PASSWORD 🔒 (OtOpcUa!Dev123 dev-only), MSSQL_PID=Developer.
  • docker-dev seed (seed/entrypoint.sh): SQL_HOST / SQL_USER / SQL_PASSWORD 🔒 / SQL_DATABASE (defaults sql/sa/OtOpcUa!Dev123/OtOpcUa).
  • Integration compose: SQL (SA_PASSWORD 🔒 OtOpcUa!Harness123) + OpenLDAP (LDAP_ROOT, LDAP_ADMIN_USERNAME, LDAP_ADMIN_PASSWORD 🔒, LDAP_USERS, LDAP_PASSWORDS 🔒, LDAP_USER_DC) — this OpenLDAP instance is integration-test-only; the standard DEV auth is the shared GLAuth at 10.100.0.35:3893 (dc=zb,dc=local, see scadaproj/infra/glauth/).
  • Test-fixture overrides (all optional, default to the shared host 10.100.0.35): OPCUA_SIM_ENDPOINT, MODBUS_SIM_ENDPOINT, MODBUS_SIM_PROFILE, AB_SERVER_ENDPOINT, AB_SERVER_PROFILE, S7_SIM_ENDPOINT, OTOPCUA_FOCAS_SIM_ENDPOINT, OTOPCUA_FOCAS_SIM_PROFILE, TWINCAT_HOST, TWINCAT_NETID, TWINCAT_PORT, AB_LEGACY_ENDPOINT, AB_LEGACY_CIP_PATH, AB_LEGACY_COMPOSE_PROFILE, OTOPCUA_CONFIG_TEST_SERVER, OTOPCUA_CONFIG_TEST_SA_PASSWORD 🔒, OTOPCUA_HARNESS_USE_SQL, OTOPCUA_HARNESS_USE_LDAP, MXGW_ENDPOINT, D1_SMOKE_OUT.
  • CI (.github/workflows/v2-*.yml): DOTNET_NOLOGO=1, DOTNET_CLI_TELEMETRY_OPTOUT=1.

2. MxAccessGateway

Ships no Docker/compose assets — it's a Windows-native app (x64 .NET 10 Server + x86 .NET 4.8 Worker). The Server spawns the Worker via ProcessStartInfo.Environment, passing the two MXGATEWAY_WORKER_* vars below.

2.1 Direct reads / operationally-set (runtime)

Variable Where Purpose Req? / default Process
MXGATEWAY_WORKER_NONCE 🔒 Server WorkerProcessLauncher.cs:180 → Worker WorkerOptionsParser.cs:78 Per-session handshake nonce (kept off the command line) required (generated per session) Server→Worker
MXGATEWAY_WORKER_PIPE_CONNECT_ATTEMPT_TIMEOUT_MS Server WorkerProcessLauncher.cs:181 → Worker WorkerPipeClient.cs:255 Per-attempt named-pipe connect timeout optional / 2000 Server→Worker
ASPNETCORE_CONTENTROOT GatewayApplication.cs:130 Content-root override (logs/wwwroot) optional / auto Server
ASPNETCORE_ENVIRONMENT Server/Properties/launchSettings.json:10 Environment selector optional / Production Server

2.2 Config keys overridable via __ (notable)

Variable Purpose Req?
MxGateway__ApiKeyPepper 🔒 HMAC pepper for API-key secrets in the SQLite auth DB required when auth Mode=ApiKey
MxGateway__Authentication__Mode / __SqlitePath / __PepperSecretName Auth mode, auth DB path, pepper config-key name per-deploy
MxGateway__Worker__ExecutablePath / __WorkingDirectory / __StartupTimeoutSeconds / __PipeConnectAttemptTimeoutMilliseconds x86 worker launch config per-deploy
MxGateway__Sessions__* / MxGateway__Events__QueueCapacity Session pool & event queue tuning optional
MxGateway__Galaxy__ConnectionString 🔒 SQL connection for Galaxy browse RPCs per-deploy
MxGateway__Alarms__* / MxGateway__Dashboard__* Alarm monitor & dashboard config optional
MxGateway__Telemetry__Exporter / __OtlpEndpoint OpenTelemetry exporter selection / OTLP endpoint optional
MxGateway__Tls__SelfSignedCertPath Self-signed PFX path optional
Kestrel__Endpoints__Http__Url / __Protocols, Kestrel__Endpoints__Dashboard__Url gRPC (h2c) + dashboard endpoints per-deploy

2.3 Client CLI, tests & build script (MxGateway)

  • Client CLI / smoke tests: MXGATEWAY_ENDPOINT (default http://localhost:5000), MXGATEWAY_API_KEY 🔒 (MxGatewayClientCli.cs:289).
  • Live-test opt-in gates (set to 1 to enable; otherwise skipped): MXGATEWAY_RUN_LIVE_MXACCESS_TESTS, MXGATEWAY_RUN_LIVE_LDAP_TESTS.
  • Live-test params (optional): MXGATEWAY_LIVE_MXACCESS_WORKER_EXE, _ITEM, _CLIENT_NAME, _EVENT_TIMEOUT_SECONDS, _WRITE_SECURED_USER, _WRITE_SECURED_PASSWORD 🔒, MXGATEWAY_LIVE_GALAXY_CONN 🔒.
  • Pack/publish (scripts/pack-clients.ps1): GITEA_USERNAME, GITEA_TOKEN 🔒 (required with -Publish), JAVA_HOME (Java client build).

3. ScadaBridge

Role is selected by SCADABRIDGE_CONFIG (Central|Site), which picks appsettings.{role}.json (falls back to DOTNET_ENVIRONMENT, then Production). The pre-host StartupValidator / ConfigPreflight enforces the required keys below and fails fast if any are missing/invalid.

3.1 Direct reads (C#)

Variable Where Purpose Req? / default Scope
SCADABRIDGE_CONFIG Host/Program.cs:31 Role selector → appsettings file optional (→ DOTNET_ENVIRONMENTProduction) both
DOTNET_ENVIRONMENT Host/Program.cs:32 Fallback role/env selector optional both
ASPNETCORE_ENVIRONMENT Host/Program.cs:245 Dev-mode check optional both
SCADABRIDGE_DESIGNTIME_CONNECTIONSTRING 🔒 ConfigurationDatabase/DesignTimeDbContextFactory.cs:48 EF tooling connection (dotnet ef) optional (build-time) design-time
SCADABRIDGE_MANAGEMENT_URL CLI/CliConfig.cs:72 Management API URL for the CLI optional CLI
SCADABRIDGE_FORMAT CLI/CliConfig.cs:76 CLI default output format optional / json CLI
SCADABRIDGE_USERNAME CLI/CliConfig.cs:81 CLI LDAP username (safer than --password) optional CLI
SCADABRIDGE_PASSWORD 🔒 CLI/CliConfig.cs:85 CLI LDAP password optional CLI

3.2 Config keys required at startup (__ form, enforced by StartupValidator)

Variable Purpose Constraint Scope
ScadaBridge__Node__Role Central or Site required all
ScadaBridge__Node__NodeHostname Hostname advertised to cluster required, non-empty all
ScadaBridge__Node__RemotingPort Akka remoting TCP port required, 165535 (default 8081) all
ScadaBridge__Cluster__SeedNodes__0 / __1 Akka seed addresses required, ≥2 entries all
ScadaBridge__Database__ConfigurationDb 🔒 SQL config-DB connection required Central
ScadaBridge__Security__JwtSigningKey 🔒 Cookie-JWT HMAC key required, ≥32 chars Central
ScadaBridge__Security__Ldap__Server LDAP host required Central
ScadaBridge__InboundApi__ApiKeyPepper 🔒 Peppered-HMAC inbound API-key pepper required, ≥16 chars Central
ScadaBridge__Node__SiteId Site identifier required Site
ScadaBridge__Database__SiteDbPath Site-local SQLite path required Site
ScadaBridge__Node__GrpcPort gRPC streaming port default 8083; ≠ remoting/metrics Site
ScadaBridge__Node__MetricsPort Prometheus /metrics port default 8084; ≠ remoting/grpc Site

LDAP service-account fields (ScadaBridge__Security__Ldap__ServiceAccountDn, __ServiceAccountPassword 🔒, __SearchBase) are validated post-host by LdapOptionsValidator for Central, not in the fail-fast pre-host pass.

3.3 Config keys overridable via __ (optional, not validated)

Large surface — all bound from the role appsettings and overridable via env. Grouped by module (each prefixed ScadaBridge__):

  • Node: Node__NodeName. Database: Database__MachineDataDb 🔒, Database__SkipMigrations.
  • Security: Security__Ldap__Port|Transport|AllowInsecure, Security__JwtExpiryMinutes, Security__IdleTimeoutMinutes, Security__JwtRefreshThresholdMinutes, Security__RequireHttpsCookie.
  • Cluster (SBR): Cluster__SplitBrainResolverStrategy|StableAfter|HeartbeatInterval|FailureDetectionThreshold|MinNrOfMembers|DownIfAlone.
  • Communication: Communication__DeploymentTimeout|LifecycleTimeout|QueryTimeout|TransportHeartbeatInterval|TransportFailureThreshold, Communication__CentralContactPoints__0… (Site→Central ClusterClient).
  • HealthMonitoring: HealthMonitoring__ReportInterval|OfflineTimeout|CentralOfflineTimeout.
  • InboundApi: InboundApi__DefaultMethodTimeout|MaxRequestBodyBytes.
  • Notification (SMTP): Notification__SmtpServer|SmtpPort|AuthMode|FromAddress|ConnectionTimeoutSeconds|MaxConcurrentConnections.
  • NotificationOutbox (Central): NotificationOutbox__DispatchInterval|DispatchBatchSize|StuckAgeThreshold|TerminalRetention|PurgeInterval|DeliveredKpiWindow.
  • Transport (Central): Transport__SourceEnvironment|BundleSessionTtlMinutes|MaxBundleSizeMb|MaxBundleEntryDecompressedMb|MaxBundleEntryCount|MaxBundleEntryCompressionRatio|MaxUnlockAttemptsPerSession|MaxUnlockAttemptsPerIpPerHour|Pbkdf2Iterations.
  • Logging: Logging__MinimumLevel.
  • DataConnection (Site): DataConnection__ReconnectInterval|TagResolutionRetryInterval|WriteTimeout|StableConnectionThreshold.
  • StoreAndForward (Site): StoreAndForward__SqliteDbPath|ReplicationEnabled|DefaultRetryInterval|DefaultMaxRetries.
  • SiteEventLog (Site): SiteEventLog__RetentionDays|MaxStorageMb|DatabasePath|PurgeInterval.
  • SiteRuntime (Site): SiteRuntime__StartupBatchSize|StartupBatchDelayMs|MaxScriptCallDepth|ScriptExecutionTimeoutSeconds|StreamBufferSize|ScriptExecutionThreadCount.
  • Telemetry (Site): Telemetry__Exporter|OtlpEndpoint.

3.4 Docker / infra / build (ScadaBridge)

  • docker/docker-compose.yml (3-site cluster) — Central: SCADABRIDGE_CONFIG=Central, ASPNETCORE_ENVIRONMENT=Development, ASPNETCORE_URLS=http://+:5000, ScadaBridge__InboundApi__ApiKeyPepper 🔒 = dev-only-insecure-pepper-docker-cluster-0001; Sites: SCADABRIDGE_CONFIG=Site.
  • docker-env2/docker-compose.yml (1-site cluster) — same shape, pepper 🔒 = dev-only-insecure-pepper-env2-cluster-0001 (distinct per environment).
  • infra/docker-compose.yml — MSSQL (ACCEPT_EULA=Y, MSSQL_SA_PASSWORD 🔒 ScadaBridge_Dev1#, MSSQL_PID=Developer), Mailpit SMTP (MP_SMTP_AUTH_ACCEPT_ANY=1, MP_SMTP_AUTH_ALLOW_INSECURE=1, MP_MAX_MESSAGES=500), REST API (API_NO_AUTH=0, PORT=5200).
  • docker/Dockerfile build args: NUGET_GITEA_USER / NUGET_GITEA_PASS 🔒 → injected as NuGetPackageSourceCredentials_dohertj2-gitea. docker/build.sh reads MXGW_NUGET_USER / MXGW_NUGET_PASS 🔒 from host env (blank ⇒ anonymous feed).
  • Seed/init scripts (docker*/init-db.sh, seed-sites.sh) hardcode the dev SA password 🔒 ScadaBridge_Dev1#.
  • launchSettings.json profiles set DOTNET_ENVIRONMENT, ASPNETCORE_ENVIRONMENT, SCADABRIDGE_CONFIG (Central/Site).

4. Shared libraries (ZB.MOM.WW.*)

The libraries deliberately read almost nothing from the environment directly — config flows through strongly-typed options bound by the consuming app. Notable exceptions:

Variable Where Purpose Req? / default
GITEA_NUGET_SOURCE ZB.MOM.WW.{Auth,Theme,Audit}/build/push.sh:16 Gitea NuGet feed URL for dotnet nuget push required to publish
GITEA_NUGET_KEY 🔒 …/build/push.sh:17 Gitea token (package:write) required to publish
ZB_LDAP_IT ZB.MOM.WW.Auth/tests/.../GLAuthIntegrationTests.cs:48 Gate flag (1) to run the live LDAP test optional (skipped if unset)
ZB_LDAP_SERVER / _PORT / _BASE / _SVC_DN / _SVC_PW 🔒 / _USER / _PW 🔒 / _USERATTR …/GLAuthIntegrationTests.cs:52-59 Live LDAP test connection params optional (defaults: localhost/3893/dc=zb,dc=local/…); point at the shared GLAuth (10.100.0.35:3893, dc=zb,dc=local) for the live test

Telemetry: ZB.MOM.WW.Telemetry does not read standard OTEL_* env vars — OTel identity/exporter come from ZbTelemetryOptions passed to AddZbTelemetry(). It only reads system properties (Environment.MachineName, Environment.ProcessId in ZbResource.cs:20) to form service.instance.id / host.name. Consuming apps that want OTLP wire it via their own …Telemetry__OtlpEndpoint config key (see MxGateway §2.2, ScadaBridge §3.3).

Health, Configuration, Audit, Theme: no direct environment-variable reads (code or build).


5. Cross-cutting cheat sheet

Standard framework vars (honored everywhere)

ASPNETCORE_ENVIRONMENT, ASPNETCORE_URLS, ASPNETCORE_CONTENTROOT, DOTNET_ENVIRONMENT, DOTNET_NOLOGO, DOTNET_CLI_TELEMETRY_OPTOUT.

Build/publish (Gitea NuGet feed) — naming differs per repo

Repo Vars
Shared libs GITEA_NUGET_SOURCE, GITEA_NUGET_KEY 🔒
MxGateway pack GITEA_USERNAME, GITEA_TOKEN 🔒
ScadaBridge image build MXGW_NUGET_USER, MXGW_NUGET_PASS 🔒 (→ NUGET_GITEA_USER/NUGET_GITEA_PASS build args)

Secrets inventory (🔒) — inject out-of-band, never commit real values

  • Peppers: ScadaBridge__InboundApi__ApiKeyPepper (≥16, Central-only), MxGateway__ApiKeyPepper.
  • Signing/JWT keys: Security__Jwt__SigningKey (OtOpcUa), ScadaBridge__Security__JwtSigningKey.
  • API keys / nonces: GALAXY_MXGW_API_KEY, MXGATEWAY_API_KEY, MXGATEWAY_WORKER_NONCE, OTOPCUA_HISTORIAN_SECRET.
  • DB / LDAP / SMTP passwords: all *SA_PASSWORD/MSSQL_SA_PASSWORD, ConnectionStrings__ConfigDb, ScadaBridge__Database__ConfigurationDb, *Ldap*Password, SCADABRIDGE_PASSWORD, OTOPCUA_HISTORIAN_PASS.
  • Feed tokens: GITEA_NUGET_KEY, GITEA_TOKEN, MXGW_NUGET_PASS/NUGET_GITEA_PASS.

The only secret-typed values that legitimately appear in source are the dev-only, insecure local-cluster placeholders in docker*/docker-compose.yml and the dev SA passwords in the infra/seed scripts — usable for the local stacks only, never as real secrets.