feat(transport): production-boundary signing advisory, secret-key proof, runbook (#110)
Owner decisions on #110: one signing key per environment; TrustedKeys stay hand-edited; Signing:ProductionBoundary (new, default off) logs a startup warning and reports a Degraded bundle-signing-policy /health/ready entry while RequireSignedBundles is off; the private key is read via ${secret:} (proved end to end, never logged); cluster pull decided not to be built; differential deletions stay deferred. Adds docs/operations/bundle-signing-runbook.md. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -384,10 +384,10 @@ Signing adds **non-repudiation** on top of the passphrase encryption — the pas
|
||||
|
||||
Generate a key pair with `openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 -topk8 -nocrypt -out bundle-signing.pem` and `openssl ec -in bundle-signing.pem -pubout`. The private key belongs in the encrypted secret store, referenced as `${secret:}` (expanded pre-host like every other config secret); both central nodes of a cluster share it, since either may run the export.
|
||||
- **Pluggable key material.** `IBundleSigningKeySource` (export key) and `IBundleTrustStore` (fingerprint → trusted public key) are `TryAdd`-registered; a host can register its own (HSM/KMS signing, a shared trust registry) before `AddTransport()`.
|
||||
- **Open decisions** (not settled by the original design, recorded here rather than guessed in code): (1) whether each environment gets its own signing key or one key per organisation; (2) how trust is distributed and rotated beyond hand-edited `TrustedKeys` (a trust bundle in the config DB editable from the Admin UI? key expiry/revocation?); (3) whether `RequireSignedBundles` should become the default at the production boundary once every exporting cluster signs; (4) whether the default key source should read the Secrets store directly (by secret name) instead of through `${secret:}` config expansion.
|
||||
- **Decisions** (owner, 2026-11-09, ScadaBridge#110): (1) **one signing key per environment**; (2) trust stays the hand-edited `TrustedKeys` (no Admin UI trust list, no expiry/revocation store); (3) `RequireSignedBundles` is to be **on at the production boundary** once every exporting cluster signs — the code default stays off, and a cluster marked `Signing:ProductionBoundary=true` that still has it off logs a startup Warning and reports a Degraded `bundle-signing-policy` entry on `/health/ready` (HTTP 200); (4) the private key is read through `${secret:}` config expansion from the Secrets store (verified end to end by `TransportSigningSecretExpansionTests`; the key is never logged), not by a direct Secrets-store key source. Operator procedure (key generation, storage, distribution, rotation, the order for turning enforcement on): `docs/operations/bundle-signing-runbook.md`.
|
||||
- **Compatibility proof.** `Transport.IntegrationTests/Fixtures/pre-signing-*.scadabundle` were exported by the pre-#45 code and are imported end to end by `LegacyBundleCompatibilityTests` (plaintext and encrypted, signing off and on); `SignedBundleLoadTests.Signed_bundle_is_readable_by_pre_signing_read_path` shows the reverse direction.
|
||||
|
||||
Cluster-to-cluster pull and differential bundles (the other two items deferred with signing) are **not implemented**; their design questions are recorded in `docs/plans/2026-11-09-transport-pull-and-differential-addendum.md`.
|
||||
Cluster-to-cluster pull is **decided not to be built** (CLI export → import covers it) and differential-bundle deletions stay **deferred**; the rationale is recorded in `docs/plans/2026-11-09-transport-pull-and-differential-addendum.md`.
|
||||
|
||||
## Configuration Audit Trail
|
||||
|
||||
|
||||
Reference in New Issue
Block a user