Known-cluster gate can stay unpublished (fail-open) for up to 60 s after central boot #654

Closed
opened 2026-10-08 20:51:29 -04:00 by dohertj2 · 1 comment
Owner

Found in the #606 CI-lane review (batch b647). CentralCommunicationActor starts its contact-refresh timer with TimeSpan.Zero, so the first RefreshContacts runs immediately after PreStart — not 'long after every starter finished' as its comment claims. If that first refresh loses the race with the starter that resolves the provider, the known-cluster gate is not published until the next refresh, MeshTransport:ContactRefreshSeconds (default 60 s) later. ControlPlaneStarterOrder's Dps case failed 2/7 Gitea runs on exactly this race (the test now uses a 1 s refresh, so it no longer proves production heals fast).

Impact: for up to 60 s after a central admin node boots, the gate (documented as a defence against a timing oracle and a pre-auth store/audit flood) is fail-open. Bounded, admin-only, still behind the PSK interceptor; dialling unaffected.
Fix: a short bounded retry (2–5 s) while the provider is null, or a small initial timer delay; plus a test that the gate publishes within seconds when the provider resolves late under the default refresh interval.

Found in the #606 CI-lane review (batch b647). `CentralCommunicationActor` starts its contact-refresh timer with `TimeSpan.Zero`, so the first `RefreshContacts` runs immediately after `PreStart` — not 'long after every starter finished' as its comment claims. If that first refresh loses the race with the starter that resolves the provider, the known-cluster gate is not published until the next refresh, `MeshTransport:ContactRefreshSeconds` (default 60 s) later. `ControlPlaneStarterOrder`'s Dps case failed 2/7 Gitea runs on exactly this race (the test now uses a 1 s refresh, so it no longer proves production heals fast). Impact: for up to 60 s after a central admin node boots, the gate (documented as a defence against a timing oracle and a pre-auth store/audit flood) is fail-open. Bounded, admin-only, still behind the PSK interceptor; dialling unaffected. Fix: a short bounded retry (2–5 s) while the provider is null, or a small initial timer delay; plus a test that the gate publishes within seconds when the provider resolves late under the default refresh interval.
Author
Owner

Fixed on master — merge 7c4cc318 (commits 9dc6390d, 2659a690), pushed 2026-10-09.

While the known-cluster gate is unpublished, CentralCommunicationActor now schedules a single-shot RefreshContacts retry every 2 s (max 15, stops as soon as the gate publishes) instead of waiting for the 60 s refresh. The "will NEVER be published" Error now fires only when the retry budget is exhausted. Covers both causes: the TimeSpan.Zero first refresh racing the IMeshPskProvider resolve, and a failed first ClusterNode load.

Tests: a non-vacuous stop-on-publish test (the gate is unpublished on the first attempt so a retry really is scheduled), plus the budget-exhaustion Error. Docs: docs/Configuration.md. Full suite green on the merged master.

Fixed on master — merge `7c4cc318` (commits `9dc6390d`, `2659a690`), pushed 2026-10-09. While the known-cluster gate is unpublished, `CentralCommunicationActor` now schedules a single-shot `RefreshContacts` retry every 2 s (max 15, stops as soon as the gate publishes) instead of waiting for the 60 s refresh. The "will NEVER be published" Error now fires only when the retry budget is exhausted. Covers both causes: the TimeSpan.Zero first refresh racing the `IMeshPskProvider` resolve, and a failed first ClusterNode load. Tests: a non-vacuous stop-on-publish test (the gate is unpublished on the first attempt so a retry really is scheduled), plus the budget-exhaustion Error. Docs: `docs/Configuration.md`. Full suite green on the merged master.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#654