fix(SEC-33,SEC-34): host-meaningful path rooting; verification-cache invalidate race
SEC-33: make rooting host-meaningful and stop shipping foreign-platform literals. - Delete IsRootedForAnyPlatform; AddIfNotRooted now uses Path.IsPathRooted (current OS). - Promote AddIfNotRooted/AddIfInvalidPath to shared GatewayConfigPathRules so the new Galaxy validator reuses them and the two validators cannot drift. - Remove Authentication:SqlitePath and Galaxy:SnapshotCachePath Windows literals from appsettings.json; the CommonApplicationData-derived code defaults take over. The Galaxy default is seeded as a configuration value before AddZbGalaxyRepository (SnapshotCachePath is init-only, so a PostConfigure mutation cannot compile). - New GalaxyRepositoryOptionsValidator (ValidateOnStart) enforces a valid, host-rooted SnapshotCachePath when PersistSnapshot is true. - Root-cause the stray junk-named auth DB: host start eagerly builds AuthSqliteConnectionFactory; under the non-rooted Windows literal on macOS SQLite wrote it relative to the test bin CWD. The three real-host-start tests now pin SqlitePath to a temp path. SEC-34: verification cache Invalidate-vs-in-flight-repopulation race closed with a per-key generation counter (bump-before-evict, snapshot-then-recheck). The expiry cap (window 2) takes the documented fallback: the library verification identity carries no ExpiresUtc, so the cache cannot cap at the key's expiry (donor-library ask). GWC-24 rider: cap MxGateway:Events:QueueCapacity at int.MaxValue/2 so the derived checked(2 * EventChannelCapacity) in WorkerClient cannot overflow at session creation. SEC-35 (doc-only): note IsProduction() env-name semantics in GatewayConfiguration.md. Docs updated same commit (GatewayConfiguration.md, Authentication.md) and tracking registers/change-log flipped (00-tracking.md, 40-security-dashboard.md).
This commit is contained in:
+22
-7
@@ -99,9 +99,20 @@ library:
|
||||
are skipped. Only successes are cached; failures always reach the inner verifier.
|
||||
On a gateway-initiated revoke/rotate/delete the dashboard admin service calls
|
||||
`IApiKeyCacheInvalidator.Invalidate(keyId)`, evicting the cached entry
|
||||
immediately. The short TTL is the backstop for out-of-band mutations (a direct DB
|
||||
edit, or a revoke run by the separate `apikey` CLI process, whose in-memory cache
|
||||
is not the running gateway's cache).
|
||||
immediately. `Invalidate` bumps a per-key generation counter **before** it evicts,
|
||||
and `VerifyAsync` snapshots that generation before the inner verify and re-checks
|
||||
it after writing the cache entry (set-then-recheck); a revoke that lands while a
|
||||
verification is still in flight in the inner library therefore discards that
|
||||
verification's repopulation instead of re-caching the just-revoked identity for a
|
||||
full TTL (SEC-34). The short TTL remains the backstop for two bounded-staleness
|
||||
windows it cannot close directly: (1) out-of-band mutations (a direct DB edit, or a
|
||||
revoke run by the separate `apikey` CLI process, whose in-memory cache is not the
|
||||
running gateway's cache); and (2) a key whose `ExpiresUtc` passes while cached keeps
|
||||
authenticating until the entry's TTL elapses — expiry is enforced by the inner
|
||||
library verifier, which a cache hit never reaches, and the verification identity the
|
||||
library returns carries no expiry timestamp, so the cache cannot cap an entry at the
|
||||
key's expiry (capping it needs the donor library to surface expiry on the
|
||||
verification identity). The default 15 s TTL bounds both windows.
|
||||
- **`CoalescingMarkApiKeyStore`** wraps the library `IApiKeyStore` and forwards at
|
||||
most one `MarkUsed` write per key per
|
||||
`MxGateway:Security:ApiKeyLastUsedCoalesceSeconds` (default 60 s), so even under a
|
||||
@@ -148,10 +159,14 @@ is derived from `Environment.GetFolderPath(SpecialFolder.CommonApplicationData)`
|
||||
(`C:\ProgramData\MxGateway\gateway-auth.db` on Windows,
|
||||
`/usr/share/MxGateway/gateway-auth.db` or the container equivalent elsewhere) so the
|
||||
credential store is never written relative to the launch working directory on a
|
||||
non-Windows host. The production hosts pin the explicit Windows path in
|
||||
`appsettings.json`. `GatewayOptionsValidator` rejects a non-rooted (relative)
|
||||
`SqlitePath` so a bad override fails fast at startup rather than scattering the store
|
||||
by launch CWD (SEC-01).
|
||||
non-Windows host. `appsettings.json` no longer ships an explicit path (SEC-33): the
|
||||
removed Windows literal matched the Windows code default and, being non-rooted on a
|
||||
Unix host, would have resolved against the CWD there; deployed hosts override it
|
||||
through the NSSM environment (`MxGateway__Authentication__SqlitePath`).
|
||||
`GatewayOptionsValidator` rejects a `SqlitePath` that is not rooted **on the host
|
||||
running the gateway** (`Path.IsPathRooted`, current OS) — a relative filename or a
|
||||
foreign-platform literal fails fast at startup rather than scattering the store by
|
||||
launch CWD (SEC-01, SEC-33).
|
||||
|
||||
The library owns the SQLite schema and connection factory. The `api_keys` table
|
||||
carries the key id, key prefix, secret-hash blob, display name, serialized scopes,
|
||||
|
||||
Reference in New Issue
Block a user