fix(host): follow an in-place site PSK rotation without a central restart (ScadaBridge#96)
SitePskProvider cached each site's gRPC preshared key forever; only site removal
invalidated it, so rotating SB-GRPC-PSK-{site} broke the central<->site link on
both central nodes until they restarted. The cached key is now dropped when:
- the secret is written through /admin/secrets: central replaces the secrets
library's ISecretCacheInvalidator with SitePskRotationInvalidator, which also
publishes SitePskInvalidated on a new DistributedPubSub topic
(site-psk-invalidation) so the peer central node drops its copy and its
resolver cache entry too (SitePskInvalidationSubscriber);
- a site rejects a central->site call with PermissionDenied/Unauthenticated:
WithSiteCredentials wraps every per-site channel's handler in
PskRejectionRefreshHandler, so the next call re-reads the key (covers
out-of-process writes such as the secret CLI);
- a site->central call presents a token that does not match the cached key:
CentralControlAuthInterceptor invalidates and re-reads once before refusing;
- ScadaBridge:Communication:SitePsks reloads.
Invalidation is case-insensitive because SecretName lowercases names. Rotation
runbook (topology-guide), Component-Communication/Host and the #85 cache audit
(D2) updated.
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -407,6 +407,18 @@ public class CentralCompositionRootTests : IDisposable
|
||||
Assert.Null(_factory.Services.GetService<IInboundApiKeyStoreImporter>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gitea #96: the secrets write path's <see cref="ZB.MOM.WW.Secrets.Abstractions.ISecretCacheInvalidator"/>
|
||||
/// must be the PSK-aware wrapper on central, or an in-place site-PSK rotation through
|
||||
/// /admin/secrets never reaches the PSK cache.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Central_SecretCacheInvalidator_IsTheSitePskRotationInvalidator()
|
||||
{
|
||||
Assert.IsType<SitePskRotationInvalidator>(
|
||||
_factory.Services.GetService<ZB.MOM.WW.Secrets.Abstractions.ISecretCacheInvalidator>());
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the shared audit seam resolves to the central direct-write bridge.</summary>
|
||||
[Fact]
|
||||
public void Central_SharedAuditSeam_IsTheCentralDirectWriteBridge()
|
||||
|
||||
Reference in New Issue
Block a user