fix(host): resolve Host-003,004 — replace plaintext secrets with env placeholders, validate site seed-node ports; re-triage Host-002
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
| Last reviewed | 2026-05-16 |
|
||||
| Reviewer | claude-agent |
|
||||
| Commit reviewed | `9c60592` |
|
||||
| Open findings | 10 |
|
||||
| Open findings | 8 |
|
||||
|
||||
## Summary
|
||||
|
||||
@@ -98,10 +98,10 @@ response body; it failed before the fix and passes after. Full Host suite green
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Severity | Medium |
|
||||
| Severity | Medium — re-triaged: stale design doc, Host code is correct |
|
||||
| Category | Design-document adherence |
|
||||
| Status | Open |
|
||||
| Location | `src/ScadaLink.Host/Actors/AkkaHostedService.cs:70-108` |
|
||||
| Location | `src/ScadaLink.Host/Actors/AkkaHostedService.cs:70-108`, `docs/requirements/Component-Host.md` REQ-HOST-6 |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -126,7 +126,21 @@ add the plugin packages and HOCON. Either way, code and doc must agree.
|
||||
|
||||
**Resolution**
|
||||
|
||||
_Unresolved._
|
||||
_Verified 2026-05-16, left Open — re-triaged._ The finding is accurate: a repo-wide
|
||||
search confirms there are **no** `PersistentActor` / `ReceivePersistentActor`
|
||||
subclasses anywhere in `src/`, no `akka.persistence` section in the HOCON built by
|
||||
`AkkaHostedService.StartAsync`, and `ScadaLink.Host.csproj` references no persistence
|
||||
plugin packages. The system deliberately uses component-owned SQLite storage
|
||||
services instead. The **Host code is therefore correct and internally consistent** —
|
||||
the only defect is that `docs/requirements/Component-Host.md` REQ-HOST-6 and its
|
||||
Dependencies list still mandate Akka.Persistence, a subsystem that does not (and is
|
||||
not intended to) exist. The sole fix is editing that design document, which lies
|
||||
outside this resolution task's permitted edit scope (`src/ScadaLink.Host`,
|
||||
`tests/ScadaLink.Host.Tests`, `code-reviews/Host/findings.md`). No code or test
|
||||
change can resolve a stale-doc finding. Left **Open** and surfaced for the design-doc
|
||||
owner: REQ-HOST-6 must drop the Akka.Persistence requirement (and the
|
||||
`Akka.Persistence.Hosting` Dependencies entry), stating that node-local persistence
|
||||
is provided by component-owned SQLite storage services.
|
||||
|
||||
### Host-003 — Secrets committed in plaintext in `appsettings.Central.json`
|
||||
|
||||
@@ -134,7 +148,7 @@ _Unresolved._
|
||||
|--|--|
|
||||
| Severity | Medium |
|
||||
| Category | Security |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.Host/appsettings.Central.json:20-31` |
|
||||
|
||||
**Description**
|
||||
@@ -159,7 +173,29 @@ environment.
|
||||
|
||||
**Resolution**
|
||||
|
||||
_Unresolved._
|
||||
Resolved 2026-05-16 (commit `pending`). Root cause confirmed against
|
||||
`appsettings.Central.json`: the committed file carried real-looking secrets in
|
||||
plaintext — SQL Server passwords (`Password=ScadaLink_Dev1#`) in both connection
|
||||
strings, an LDAP service-account password, and a JWT signing key. Fix: all four
|
||||
secrets were removed from the committed file and replaced with non-functional
|
||||
`${...}` placeholder references (`ConfigurationDb` / `MachineDataDb`,
|
||||
`LdapServiceAccountPassword`, `JwtSigningKey`). A new top-level `_secrets` note
|
||||
documents that the Host's configuration builder (`AddEnvironmentVariables()`)
|
||||
overlays the real values supplied via environment variables
|
||||
(`ScadaLink__Database__ConfigurationDb`, `ScadaLink__Database__MachineDataDb`,
|
||||
`ScadaLink__Security__LdapServiceAccountPassword`,
|
||||
`ScadaLink__Security__JwtSigningKey`); the placeholders are intentionally invalid so
|
||||
a misconfigured deployment fails loudly rather than silently using a committed key.
|
||||
Regression test class `ConfigSecretsTests` asserts the committed file carries no
|
||||
plaintext `Password=` value, no committed LDAP service-account password, and no
|
||||
committed JWT signing key; all three tests failed before the fix and pass after.
|
||||
Tests that drive the full `Program` startup pipeline against the real SQL provider
|
||||
(`HealthCheckTests`, `HostStartupTests.CentralRole_StartsWithoutError`) were adapted
|
||||
to supply the local dev connection strings themselves via the new
|
||||
`CentralDbTestEnvironment` test helper (environment variables) — they must no longer
|
||||
depend on committed secrets. Note: the `docker/central-node-*/appsettings.Central.json`
|
||||
files still contain the same dev secrets but lie outside this task's permitted edit
|
||||
scope; they should receive the same treatment in a follow-up.
|
||||
|
||||
### Host-004 — Site seed-node list points at the gRPC port, not a remoting port
|
||||
|
||||
@@ -167,7 +203,7 @@ _Unresolved._
|
||||
|--|--|
|
||||
| Severity | Medium |
|
||||
| Category | Correctness & logic bugs |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.Host/appsettings.Site.json:10-19` |
|
||||
|
||||
**Description**
|
||||
@@ -190,7 +226,23 @@ to reject a seed node whose port equals this node's `GrpcPort`.
|
||||
|
||||
**Resolution**
|
||||
|
||||
_Unresolved._
|
||||
Resolved 2026-05-16 (commit `pending`). Root cause confirmed against
|
||||
`appsettings.Site.json`: with `Node:RemotingPort = 8082` and `Node:GrpcPort = 8083`,
|
||||
the second `Cluster:SeedNodes` entry was `akka.tcp://scadalink@localhost:8083` — the
|
||||
Kestrel HTTP/2 gRPC port, not an Akka.Remote endpoint. `StartupValidator` only
|
||||
checked seed-node *count* (≥2), so the misconfiguration passed silently. Fix, two
|
||||
parts: (1) the shipped `appsettings.Site.json` second seed entry was corrected to a
|
||||
remoting port (`localhost:8084`); (2) `StartupValidator.Validate` was extended — for
|
||||
`Site` nodes it now parses each seed node's TCP port (via a new `SeedNodePort`
|
||||
helper) and rejects any entry whose port equals the node's `GrpcPort`, using the
|
||||
resolved GrpcPort including the `8083` `NodeOptions` default when the key is absent.
|
||||
The seed-node-count check was hoisted above the Site block so the new rule can reuse
|
||||
the parsed list. Regression tests in `StartupValidatorTests`:
|
||||
`Site_SeedNodeOnGrpcPort_FailsValidation`,
|
||||
`Site_SeedNodeOnDefaultGrpcPort_FailsValidation` (default-8083 path),
|
||||
`Site_SeedNodesOnRemotingPort_PassesValidation`, and
|
||||
`Central_SeedNodeOnPort8083_PassesValidation` (rule is Site-only) — all failed
|
||||
appropriately before the fix and pass after.
|
||||
|
||||
### Host-005 — Blocking sync-over-async (`GetAwaiter().GetResult()`) inside `StartAsync`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user