ACL: an expired API key keeps its session's roles while Security:Acl:Enabled is false (arch-review 03/S19 residual R2) #563

Closed
opened 2026-08-20 10:05:42 -04:00 by dohertj2 · 1 comment
Owner

Source: arch-review round 3, archreview/plans/R3-05-acl-hot-path-expiry-plan.md → Residual R2.
Discovered while closing 03/S19.

What is wrong

With Security:Acl:Enabled = false — the default — there is no per-operation API-key check anywhere in
the server. DataPlaneAuthorizer reports itself disabled, every seam falls back to its server-wide role
check, and RevalidateMonitoredItems early-returns on !AclEnforced.

Consequence: a session's roles are resolved once, at activation, from
ApiKeyInterimRoleMapper.Map(grants, principal). When that key's ExpiresUtc passes, the open session
keeps whatever roles it was given — WriteOperate, AlarmAck — until the client disconnects. On a
machine-to-machine data plane that can be indefinite.

With the flag on, this is closed: Evaluate re-checks snapshot.FindUsableKey(keyId, now) against the
authorizer's clock on every operation, and (since R3-05) a timer carries the same verdict to live
subscriptions.

Why R3-05 did not fix it

Closing it means gating on key usability independently of ACL enforcement — i.e. changing behaviour on
the default configuration, for every deployment that has not opted into Security:Acl:Enabled. That is a
separate decision from the two 03/S18+03/S19 fixes, which were both designed to be inert when the flag is
off, so it was named as a residual rather than folded in.

Anchors

  • src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Security/DataPlaneAuthorizer.cs — Enabled, and the
    snapshot.FindUsableKey(keyId, now) re-check inside Evaluate that only runs when enforcing.
  • src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OtOpcUaNodeManager.cs — RevalidateMonitoredItems's
    if (!AclEnforced …) return;.
  • src/Server/ZB.MOM.WW.OtOpcUa.Host/OpcUa/ApiKeyInterimRoleMapper.cs — where the activation-time roles
    come from.

Suggested shape

A key-usability check on the two role gates (WriteOperate on Value writes, AlarmAck on the Part 9
methods) that runs regardless of Security:Acl:Enabled — reading the same artifact snapshot the authorizer
does, so it costs one dictionary probe and no I/O. Decide explicitly whether it also applies to Anonymous
sessions (which carry no key and so cannot expire) and whether it needs its own flag for the rollout.

**Source:** arch-review round 3, `archreview/plans/R3-05-acl-hot-path-expiry-plan.md` → Residual **R2**. Discovered while closing 03/S19. ### What is wrong With `Security:Acl:Enabled = false` — **the default** — there is no per-operation API-key check anywhere in the server. `DataPlaneAuthorizer` reports itself disabled, every seam falls back to its server-wide role check, and `RevalidateMonitoredItems` early-returns on `!AclEnforced`. Consequence: a session's roles are resolved **once**, at activation, from `ApiKeyInterimRoleMapper.Map(grants, principal)`. When that key's `ExpiresUtc` passes, the open session **keeps whatever roles it was given** — `WriteOperate`, `AlarmAck` — until the client disconnects. On a machine-to-machine data plane that can be indefinite. With the flag **on**, this is closed: `Evaluate` re-checks `snapshot.FindUsableKey(keyId, now)` against the authorizer's clock on every operation, and (since R3-05) a timer carries the same verdict to live subscriptions. ### Why R3-05 did not fix it Closing it means gating on key usability **independently of ACL enforcement** — i.e. changing behaviour on the default configuration, for every deployment that has not opted into `Security:Acl:Enabled`. That is a separate decision from the two 03/S18+03/S19 fixes, which were both designed to be inert when the flag is off, so it was named as a residual rather than folded in. ### Anchors - `src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Security/DataPlaneAuthorizer.cs` — `Enabled`, and the `snapshot.FindUsableKey(keyId, now)` re-check inside `Evaluate` that only runs when enforcing. - `src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OtOpcUaNodeManager.cs` — `RevalidateMonitoredItems`'s `if (!AclEnforced …) return;`. - `src/Server/ZB.MOM.WW.OtOpcUa.Host/OpcUa/ApiKeyInterimRoleMapper.cs` — where the activation-time roles come from. ### Suggested shape A key-usability check on the two role gates (`WriteOperate` on Value writes, `AlarmAck` on the Part 9 methods) that runs regardless of `Security:Acl:Enabled` — reading the same artifact snapshot the authorizer does, so it costs one dictionary probe and no I/O. Decide explicitly whether it also applies to Anonymous sessions (which carry no key and so cannot expire) and whether it needs its own flag for the rollout.
Author
Owner

Fixed: key usability (FindUsableKey on the deployed snapshot) now gates WriteOperate value writes and AlarmAck methods regardless of Security:Acl:Enabled; anonymous unaffected; no new flag. Note: on a fused node a key minted after the last deploy cannot write/ack until a deployment carries it (same as flag-on). Tests: NodeManagerKeyUsabilityGateTests. (merge 35152fca on master, pushed 2026-10-08)

Fixed: key usability (FindUsableKey on the deployed snapshot) now gates WriteOperate value writes and AlarmAck methods regardless of Security:Acl:Enabled; anonymous unaffected; no new flag. Note: on a fused node a key minted after the last deploy cannot write/ack until a deployment carries it (same as flag-on). Tests: NodeManagerKeyUsabilityGateTests. (merge `35152fca` on master, pushed 2026-10-08)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#563