e347286f28
Add components/secrets/ (SPEC, realized shared-contract, code-verified current-state for OtOpcUa/mxaccessgw/ScadaBridge, GAPS adoption backlog) and register the Secrets row in CLAUDE.md + components/README.md. The ZB.MOM.WW.Secrets lib is built + published 0.1.2 + reference-consumer-proven (HistorianGateway, live vs the wonder historian); per-app adoption is the tracked follow-on.
5.1 KiB
5.1 KiB
ScadaBridge — secrets current state
Descriptive baseline (code-verified) of how ScadaBridge handles secrets today, before
adopting ZB.MOM.WW.Secrets. Paths are relative to ~/Desktop/ScadaBridge.
Where each secret lives today
| Secret | Location today | At-rest protection |
|---|---|---|
| MSSQL ConfigDb password | ScadaBridge:Database:ConfigurationDb connstr (Host/Program.cs:221-223); committed dev plaintext docker/central-node-a/appsettings.Central.json:21 |
Plaintext |
Central dbo MachineDataDb password |
ScadaBridge:Database:MachineDataDb (StartupValidator.cs:63-65; appsettings.Central.json:22) |
Plaintext |
| Site SQLite | SiteDbPath / StoreAndForward SqliteDbPath (appsettings.Site.json:26,34) — plain file paths |
None (no SQLCipher) |
| LDAP service-account password | ScadaBridge:Security:Ldap:ServiceAccountPassword via ZB.MOM.WW.Auth.Ldap (Program.cs:140-142); committed serviceaccount123 appsettings.Central.json:32 (+ loose ldap_login.txt etc. at repo root) |
Plaintext |
Inbound /api keys |
Hashed (peppered-HMAC) in SQLite data/inbound-api-keys.sqlite via ZB.MOM.WW.Auth.ApiKeys (Program.cs:184-217); legacy SQL-Server key store retired (20260602092753_RetireInboundApiKeyStore) |
Hashed (not reversible) |
| API-key pepper | ScadaBridge:InboundApi:ApiKeyPepper (InboundAPI/InboundApiOptions.cs:23-36); committed dev dev-only-insecure-pepper-… docker/docker-compose.yml:18,45 (both central nodes share) |
Plaintext env |
| JWT signing key | ScadaBridge:Security:JwtSigningKey (HS256, Security/SecurityOptions.cs:34); appsettings.Central.json:38 |
Plaintext |
DatabaseConnectionDefinition.ConnectionString |
ConfigDb column | Encrypted (Data Protection — see below) |
MxGateway per-endpoint ApiKey |
Inside DataConnection.PrimaryConfiguration/BackupConfiguration JSON in ConfigDb; column mapped without the encrypting converter (ConfigurationDatabase/Configurations/SiteConfiguration.cs:52-56) |
Plaintext in ConfigDb (redacted for display/audit only, ConfigSecretScrubber.cs:18-19) |
| Akka remoting | HOCON AkkaHostedService.cs:250-308 — hostname/port only |
No secure-cookie / no TLS / no shared secret (plain TCP) |
Encryption-at-rest today (the one existing analog)
EncryptedStringConverter(ASP.NET Core Data Protection EF value converter,ConfigurationDatabase/EncryptedStringConverter.cs:20-52, purpose"…ConfigurationDatabase.EncryptedColumn") encrypts four ConfigDb columns (ScadaBridgeDbContext.cs:319-348):SmtpConfiguration.Credentials,SmsConfiguration.AuthToken,ExternalSystemDefinition.AuthConfiguration,DatabaseConnectionDefinition.ConnectionString.- Key ring lives in ConfigDb (
IDataProtectionKeyContext+DataProtectionKeys,AddDataProtection().PersistKeysToDbContext<ScadaBridgeDbContext>()) — shared across central nodes. NoSetApplicationName(discriminator = content-root path) and noProtectKeysWith*— the key-ring keys are stored unencrypted in the DB; the DB is the only protection boundary.
Config expansion today
- None functional. Stock
AddJsonFile+AddEnvironmentVariables+AddCommandLine(Program.cs:38-43). The${SCADABRIDGE_*}tokens inappsettings.Central.json:23,33,35are literal, non-functional placeholders — the real values arrive by whole-key env override (ScadaBridge__Security__Ldap__ServiceAccountPassword, etc.), documented in the_secretsnote (appsettings.Central.json:21).StartupValidator(ConfigPreflightfrom the sharedZB.MOM.WW.Configurationpackage) validates presence/shape only — no resolution.
Adoption plan (toward SPEC)
- Formalize the existing placeholders: the non-functional
${SCADABRIDGE_*}+ whole-key-override convention is exactly the pattern${secret:}replaces — swap the plaintext ConfigDb/MachineDataDb passwords, LDAP password, JWT signing key, and pepper to${secret:…}tokens resolved pre-host, beforeStartupValidator/ConfigPreflightruns. Delete the committed dev plaintext + the loose*_login.txtfiles. - Close the MxGateway
ApiKeyplaintext-in-ConfigDb gap — either extendEncryptedStringConverterto that JSON column or resolve it viaISecretResolver. - Clustered sync: ScadaBridge is Akka-clustered (central pair + site nodes). Two SPEC-
sanctioned options: (a) point
ISecretStoreat the shared ConfigDb (SQL-Server store) — one source of truth, mirrors how the Data-Protection key ring is already shared; or (b) adopt theZB.MOM.WW.Secrets.Akkareplicator (built on adoption) with a shared KEK (FileMasterKeyProvider, same mounted key on every node — hard requirement). - UI: mount
/admin/secretsin CentralUI; map ScadaBridge's admin role onto the policies. - Keep bespoke: the existing Data-Protection column encryption can coexist (or migrate); the peppered-HMAC inbound-key SQLite store stays (already hashed). Akka remoting auth/TLS is a separate hardening concern, not this component.