From dc0a24f2248446dd63715881caff9ee2545993ed Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 6 Nov 2026 16:23:00 -0500 Subject: [PATCH] =?UTF-8?q?feat(galaxy):=20operator-authenticated=20alarm?= =?UTF-8?q?=20ack=20=E2=80=94=20send=20Galaxy=20credential=20to=20mxaccess?= =?UTF-8?q?gw=20(#749)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mxaccessgw 488649c+ acknowledges by AuthenticateUser as the operator and writing .AckMsg, so AcknowledgeAlarmRequest now needs a real Galaxy operator_user + operator_domain + operator_password. The OPC UA data plane is API-key/anonymous and carries no Galaxy password, so the driver resolves the account per ack: 1. the session's API key's Galaxy ack identity, set on AdminUI /apikeys and held ONLY in the encrypted Secrets store (otopcua/apikeys//galaxy-ack-{user,password}); a key with a user but no password secret is refused, never silently downgraded; 2. else the driver instance's AlarmAck fallback account (DriverConfig FallbackUser/FallbackDomain/FallbackPasswordSecretRef; secret:/${secret:}/ env:/file:/dev: only — a literal is refused); 3. else DriverOperationRefusedException: nothing is sent, never an empty operator_user. Refusals are excluded from the resilience retry/breaker. - Pins ZB.MOM.WW.MxGateway.Client/Contracts 0.2.1 -> 0.3.0 (vendored into nuget-packages/ from the Gitea feed, per the local-mxgw source mapping). - The session ApiKeyId (not a secret) rides NativeAlarmAck -> RouteNativeAlarmAck -> RouteAlarmAck -> AlarmAcknowledgeRequest; the password is resolved in the driver and never travels through actor messages. - The OPC UA principal + key id are appended to the AckMsg comment ("[OPC UA: , key ]") and logged, so a shared fallback account does not lose who acknowledged. - Password never logged: GalaxyAckCredential.ToString omits it, the acknowledger scrubs it from every gateway message it echoes and withholds any exception that echoes it; audit rows carry only the key id. - AdminUI: /apikeys "Ack identity" editor (set/remove; delete-key clears it), Galaxy driver form gains the fallback section. - Synchronous OPC UA status for a refusal is follow-up #760 (the native ack path is fire-and-forget after the Part 9 method returns). Tests: request mapping (password populated), password absent from logs and exception text (mutation-checked), key-mapped / fallback / refused paths at resolver and driver level, breaker non-trip, key-id propagation through node manager and DriverHostActor, AdminUI service + form round-trip. Closes #749 --- Directory.Packages.props | 4 +- docs/drivers/Galaxy.md | 36 ++ docs/security.md | 8 + .../ZB.MOM.WW.MxGateway.Client.0.3.0.nupkg | Bin 0 -> 76342 bytes .../ZB.MOM.WW.MxGateway.Contracts.0.3.0.nupkg | Bin 0 -> 363072 bytes .../DriverOperationRefusedException.cs | 21 + .../IAlarmSource.cs | 8 +- .../DriverResiliencePipelineBuilder.cs | 4 +- .../GalaxyAckIdentitySecrets.cs | 69 +++ .../GalaxyDriverOptions.cs | 41 ++ .../GalaxyDriver.cs | 63 ++- .../GalaxyDriverFactoryExtensions.cs | 19 + .../Runtime/GalaxyAckCredential.cs | 78 ++++ .../Runtime/GalaxyAckCredentialResolver.cs | 197 +++++++++ .../Runtime/GatewayGalaxyAlarmAcknowledger.cs | 130 ++++-- .../Runtime/IGalaxyAckCredentialResolver.cs | 18 + .../Runtime/IGalaxyAlarmAcknowledger.cs | 18 +- .../ApiKeyGalaxyAckIdentityService.cs | 163 +++++++ .../Components/Pages/ApiKeys.razor | 130 ++++++ .../Drivers/Forms/GalaxyDriverForm.razor | 42 ++ .../EndpointRouteBuilderExtensions.cs | 6 + .../OpcUa/OtOpcUaServerHostedService.cs | 2 +- .../NativeAlarmAck.cs | 4 +- .../OtOpcUaNodeManager.cs | 3 +- .../Drivers/DriverHostActor.cs | 6 +- .../Drivers/DriverInstanceActor.cs | 14 +- .../DriverResiliencePipelineBuilderTests.cs | 33 ++ .../GalaxyDriverAlarmSourceTests.cs | 27 +- .../GalaxyDriverFactoryTests.cs | 21 + .../Runtime/CapturingLogger.cs | 31 ++ .../Runtime/GalaxyAckCredentialTests.cs | 417 ++++++++++++++++++ .../Runtime/GalaxyDriverRawPathKeyingTests.cs | 7 +- .../GatewayGalaxyAlarmAcknowledgerTests.cs | 26 +- .../Runtime/TestAckCredentials.cs | 33 ++ .../ApiKeyGalaxyAckIdentityServiceTests.cs | 156 +++++++ .../GalaxyDriverPageFormSerializationTests.cs | 25 ++ .../AlarmCommandRouterTests.cs | 31 ++ ...iverHostActorNativeAlarmAckRoutingTests.cs | 6 +- 38 files changed, 1814 insertions(+), 83 deletions(-) create mode 100644 nuget-packages/ZB.MOM.WW.MxGateway.Client.0.3.0.nupkg create mode 100644 nuget-packages/ZB.MOM.WW.MxGateway.Contracts.0.3.0.nupkg create mode 100644 src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverOperationRefusedException.cs create mode 100644 src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Contracts/GalaxyAckIdentitySecrets.cs create mode 100644 src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxyAckCredential.cs create mode 100644 src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxyAckCredentialResolver.cs create mode 100644 src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxyAckCredentialResolver.cs create mode 100644 src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ApiKeyAck/ApiKeyGalaxyAckIdentityService.cs create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/CapturingLogger.cs create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyAckCredentialTests.cs create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/TestAckCredentials.cs create mode 100644 tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ApiKeyAck/ApiKeyGalaxyAckIdentityServiceTests.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index d63e819e..642f4b0f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -220,8 +220,8 @@ - - + + diff --git a/docs/drivers/Galaxy.md b/docs/drivers/Galaxy.md index 49206d59..adc94d74 100644 --- a/docs/drivers/Galaxy.md +++ b/docs/drivers/Galaxy.md @@ -85,6 +85,42 @@ Project root files: - **`MxAccess`** — `ClientName` (must be unique per OtOpcUa instance — redundancy pairs enforce uniqueness at install time), `PublishingIntervalMs` (forwarded as `buffered_update_interval_ms` on subscribe), `WriteUserId` for ArchestrA secured-write, `EventPumpChannelCapacity` (default 50_000 — one second of headroom at 50k tags / 1Hz; tune via the `galaxy.events.dropped` metric). - **`Repository`** — `DiscoverPageSize`, `WatchDeployEvents`. - **`Reconnect`** — `InitialBackoffMs`, `MaxBackoffMs`, `ReplayOnSessionLost` (calls the gateway's `ReplaySubscriptions` RPC after reconnect rather than re-issuing subscribe-bulk for every tag). +- **`AlarmAck`** (optional, lmxopcua#749) — `FallbackUser`, `FallbackDomain`, `FallbackPasswordSecretRef`. See [Alarm acknowledge credential](#alarm-acknowledge-credential). + +### Alarm acknowledge credential + +mxaccessgw `488649c`+ (client/contracts `0.3.0`) acknowledges a native alarm by running +`AuthenticateUser` as the operator and writing the comment to `.AckMsg`, so the Historian +`ACK_ALM` row records a real Galaxy account. `AcknowledgeAlarmRequest` therefore carries +`operator_user` + `operator_domain` + `operator_password`, and the gateway rejects an empty +`operator_user`. The OPC UA data plane is machine-to-machine (API key or anonymous) and carries no +Galaxy password, so `GalaxyAckCredentialResolver` picks the account per acknowledge: + +1. **The API key's own Galaxy ack identity** — set on AdminUI `/apikeys` ("Ack identity"). Stored only + in the encrypted Secrets store as `otopcua/apikeys//galaxy-ack-user` (`DOMAIN\user` or + `user`) and `otopcua/apikeys//galaxy-ack-password` (`GalaxyAckIdentitySecrets`) — never in + the config DB or the deployment artifact. A key with a user but no password secret is **refused**, + not silently downgraded to the fallback. +2. **The driver's fallback account** — `AlarmAck.FallbackUser` / `FallbackDomain` / + `FallbackPasswordSecretRef` in `DriverConfig`, used by keys without an identity and by anonymous + sessions. The password ref must be `secret:NAME` / `${secret:NAME}` (production), `env:NAME`, + `file:PATH` or `dev:VALUE`; a bare literal is refused so a password never sits in DriverConfig. +3. **Neither** → the driver throws `DriverOperationRefusedException`; **nothing is sent to the + gateway**. The refusal is excluded from the resilience breaker (it says nothing about gateway + health). ⚠️ The native ack path is fire-and-forget after the Part 9 method returns, so the OPC UA + call itself still returns `Good` and the refusal surfaces as a `DriverInstance` warning naming the + fix — a synchronous status / revert is #760. + +The comment written to `AckMsg` is suffixed with the OPC UA identity — +`" [OPC UA: , key ]"` or `"[OPC UA: anonymous]"` — so the client is not +lost when the Historian's Operator is a shared fallback account. The password is never logged: the +acknowledger scrubs it from every gateway message it echoes and `GalaxyAckCredential.ToString()` +omits it. + +⚠️ **Secrets are node-local** (optionally Akka-replicated within one mesh). The per-key secrets and +the fallback secret must exist on the node that serves the OPC UA session and hosts the Galaxy +driver; on a split admin/driver topology create them on the driver nodes too (`/admin/secrets` or +the `secret` CLI) under exactly those names. Full per-field descriptions live in `Config/GalaxyDriverOptions.cs`. The full JSON skeleton is reproduced in [docs/v2/driver-specs.md §1](../v2/driver-specs.md). diff --git a/docs/security.md b/docs/security.md index c6e44483..c4d3f052 100644 --- a/docs/security.md +++ b/docs/security.md @@ -345,6 +345,14 @@ of a credential was wrong. ConfigDb, so keys reach them inside the deployment artifact exactly as `NodeAcl` rows do. Clearing `Enabled` takes effect on a node only once a new artifact reaches it. +**Galaxy alarm-ack identity per key (lmxopcua#749).** mxaccessgw acknowledges Galaxy alarms as a real +Galaxy account, so a key can carry an optional Galaxy ack identity (AdminUI `/apikeys` → "Ack +identity"). It is held only in the encrypted Secrets store +(`otopcua/apikeys//galaxy-ack-{user,password}`), never in the ConfigDb or the artifact; keys +without one and anonymous sessions use the Galaxy driver's `AlarmAck` fallback account, and with +neither the ack is refused before it reaches the gateway. Deleting a key on `/apikeys` removes its +identity. See [docs/drivers/Galaxy.md § Alarm acknowledge credential](drivers/Galaxy.md#alarm-acknowledge-credential). + ### Anonymous is a normal principal Anonymous sessions are **admitted by default** (`OpcUa:AllowAnonymous`, default `true` — flipping the diff --git a/nuget-packages/ZB.MOM.WW.MxGateway.Client.0.3.0.nupkg b/nuget-packages/ZB.MOM.WW.MxGateway.Client.0.3.0.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..d4da517879d68b5bfaffce6b537d2a2dbd04b2d5 GIT binary patch literal 76342 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