docs(security): #545 record the accepted h2c posture for the five internal gRPC planes
OtOpcUa runs five internal gRPC planes between its own processes, all of them cleartext HTTP/2 (h2c) with a shared-bearer interceptor: the live telemetry stream, config-serve artifact fetch, LocalDb pair sync, mesh commands and mesh deploy acks. The bearer therefore crosses the wire in the clear on every one of them. That was true and undocumented; the two mesh planes are command surfaces, so it is worth saying out loud rather than leaving to be rediscovered per plane. docs/security.md gains "Internal gRPC planes (h2c) — accepted cleartext posture" under Transport Security (whose existing content is entirely about the OPC UA endpoint). It carries a five-row table — service name, listener config key, credential config key, interceptor — the honest read-vs-write breakdown per plane, and the decision itself. Decision (issue #545, option 1): risk ACCEPTED 2026-08-06, once, for all five planes together, on the trusted-link assumption every plane already makes (VPN / dedicated WAN). Per-cluster PSKs already bound blast radius on the two command planes — a captured SITE-A key cannot drive SITE-B — and production keys arrive as ${secret:...} references that must never be reused across clusters. Recorded future work: a UseTls/scheme option, which if built must cover all five planes together, since a mixed posture is harder to reason about than either uniform answer. docs/Configuration.md's MeshTransport section gains one cross-reference paragraph pointing at the new subsection. Docs only — no code, no behaviour change. Every service name, config key, interceptor name and shared-listener claim was verified against the source; ports 4056/4055 are phrased as the docker-dev rig's values of Telemetry:GrpcListenPort / ConfigServe:GrpcListenPort, not as hardcoded ports. Claude-Session: https://claude.ai/code/session_014WNM4vjoVksyyBraTXSZE1
This commit is contained in:
@@ -184,6 +184,8 @@ The checked-in `appsettings*.json` files are deliberately thin: they carry only
|
||||
|
||||
**Both command-plane gRPC services ride an existing listener — neither claims a port of its own.** `NodeCommandService` is mapped alongside `TelemetryStreamGrpcService` on the driver-role node's `Telemetry:GrpcListenPort` listener (the docker-dev rig's `:4056`) whenever that port is bound, deliberately NOT gated on `MeshTransport:Mode` — mapping it under `Dps` costs nothing (nothing dials it), whereas gating it would mean a running fleet needs a central restart before any node could ack after flipping the mode. `CentralCommandService` (the node→central ack ingress) is mapped alongside `DeploymentArtifactService` on the admin-role node's `ConfigServe:GrpcListenPort` listener (the rig's `:4055`), for the same reason — see [`docs/Telemetry.md`](Telemetry.md) for the shared driver-role listener and [§ `ConfigSource`/`ConfigServe`](#configsource--configserve-config-fetch-and-cache) for the shared admin-role one.
|
||||
|
||||
**The transport underneath is cleartext h2c, deliberately.** Both command-plane listeners are HTTP/2-only on a plaintext port and every dial is a prior-knowledge `http://host:port` address, so the PSK bearer crosses the wire in the clear on a link assumed private. That posture is **recorded and accepted** for these two planes and the three others that share their shape — see [`security.md` § Internal gRPC planes (h2c)](security.md#internal-grpc-planes-h2c--accepted-cleartext-posture) for the full five-plane table, what each one exposes, and why a TLS option would have to cover all five at once.
|
||||
|
||||
On the docker-dev rig every node carries these keys already; the rig's compose default is `Grpc` (`OTOPCUA_MESH_MODE:-Grpc`, cutover commit `caf2eec6`) — the compiled class default in `MeshTransportOptions` stays `Dps`, so an unconfigured deployment outside the rig still needs `Mode=Grpc` set explicitly. Every rig node carries a `cluster-{ClusterId}` role (Phase 6), so `OTOPCUA_MESH_MODE=Dps` is not a usable override on this rig as shipped — `SplitTopologyTransportValidator` refuses to boot any of them on `Dps`.
|
||||
|
||||
See the [ClusterClient→gRPC migration design](plans/2026-08-06-clusterclient-to-grpc-design.md), [implementation plan](plans/2026-08-06-clusterclient-to-grpc.md), [tracking doc](plans/2026-08-06-clusterclient-to-grpc-tracking.md) and [live gate](plans/2026-08-06-clusterclient-to-grpc-live-gate.md) for the migration's phase-by-phase history — those are historical records of the cutover and are not updated further by this sweep.
|
||||
|
||||
Reference in New Issue
Block a user