0f001bccdb
v2-ci / unit-tests (push) Has been cancelled
Console helper that mints and revokes OPC UA API keys through the same IApiKeyAdminService the AdminUI /apikeys page calls, edits NodeAcl grants in the rig ConfigDb, and holds one OPC UA session open per node (Client.Shared) with a subscription plus a read/write every 3 s, so a revocation, expiry or grant removal can be observed landing on an ALREADY-OPEN session on both pair nodes next to a non-revoked control key. Credentials go to a 0600 file and are never printed. LOCAL docker-dev only. Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
36 lines
2.3 KiB
Markdown
36 lines
2.3 KiB
Markdown
# Pair-behaviour gate helper (lmxopcua#801)
|
|
|
|
`PairGate` is a small console tool for the **local docker-dev rig only**. Never point it at a wonder box.
|
|
It drives the redundancy and revocation half of the #801 SDK 2 live gate against the central pair
|
|
(central-1 `opc.tcp://localhost:4840`, central-2 `:4841`):
|
|
|
|
- **`mint` / `set-enabled`** call the same `IApiKeyAdminService` that the AdminUI `/apikeys` page uses
|
|
(Create key, Revoke / Re-enable). The rig's committed dev pepper is the default. Override it with
|
|
`PAIRGATE_PEPPER` and the ConfigDb with `PAIRGATE_CONFIGDB`. The credential goes to `<credDir>/<name>.key`
|
|
(mode 0600) and is never printed. Output names the key id only.
|
|
- **`grant` / `ungrant`** add or remove a `NodeAcl` row in the rig ConfigDb.
|
|
- **`probe`** opens a fresh session (a new activation), then reads and writes the probe tag
|
|
(`PAIRGATE_TAG`, default the writable `ns=2;s=pymodbus/plc/HR200`).
|
|
- **`watch`** holds **one** session open for N seconds. It subscribes to the given tags and reads and writes
|
|
every 3 s. Output is JSON lines, so a revocation or expiry can be seen landing on an
|
|
**already-open** session.
|
|
- **`server`** reads `Server.ServiceLevel` (`i=2267`) and `Server.ServerArray` (`i=2254`).
|
|
|
|
DB edits only reach the nodes' data-plane snapshot through a deployment (`docker-dev/deploy.sh </dev/null`).
|
|
That's the documented revocation contract in `docs/security.md`.
|
|
|
|
```sh
|
|
dotnet build scripts/validation/pair-gate -c Release -o /tmp/pairgate
|
|
g="dotnet /tmp/pairgate/PairGate.dll"
|
|
$g mint ./keys k-revoke p-revoke # plus a control key, and one with an expiry: ... p-exp 2026-11-12T15:00:00Z
|
|
$g grant p-revoke-main p-revoke MAIN Cluster - 159 # ReadOnly + WriteOperate
|
|
./docker-dev/deploy.sh </dev/null
|
|
$g watch opc.tcp://localhost:4840 @keys/k-revoke.key 600 ns=2\;s=pymodbus/plc/HR200 ns=2\;s=pymodbus/plc/HR100 > c1.log &
|
|
$g watch opc.tcp://localhost:4841 @keys/k-revoke.key 600 ns=2\;s=pymodbus/plc/HR200 ns=2\;s=pymodbus/plc/HR100 > c2.log &
|
|
$g set-enabled <keyId> false # new activations refused within StoreCacheSeconds
|
|
./docker-dev/deploy.sh </dev/null # the open sessions on BOTH nodes lose write (ACL on: everything)
|
|
```
|
|
|
|
Always run a non-revoked control key next to the revoked one, so a denial can't be mistaken for an outage.
|
|
The 2026-11-12 run and its results are on #801.
|