maintenance: version cluster message envelopes - #4294
Conversation
|
Author remediation update and remaining blocker: The branch now has versioned, challenge-bound envelopes; configurable optional-to-required rollout modes; HKDF-separated authentication keys; key IDs and dual-key acceptance for rotation; startup validation; configurable clock skew; rejection metrics; replay handling; and an old and new manager and collector test matrix. Focused and reactor validation passed locally. This PR is intentionally not author-approved for direct merge yet. It still targets the unmerged #4263 branch, so it must be rebased onto the final integrated master state after #4263 lands and then receive the full repository CI. GitHub currently shows only the label check. Until that dependency and CI condition are satisfied, the merge result remains blocked. |
c5639a2 to
7768405
Compare
|
The remaining review blockers are addressed in the latest head ( |
7768405 to
183cf6b
Compare
|
Standalone verification is now complete on head
For transparency, the first Maven E2E attempt stopped after 2m42s in the unrelated The dependency, CI coverage, and secure steady-state default blockers from the review are therefore resolved. |
183cf6b to
0d85053
Compare
|
Addressed the remaining default-install and upgrade blocker on head What changed:
Regression proof:
Validation:
The branch remains exactly one commit above |
|
Final current-head CI update for
The PR remains based on |
0d85053 to
e533b4e
Compare
|
Addressed the remaining standalone Collector bootstrap blocker on head The installation contract now provisions two independent values to Manager and
The 64-character authentication output is explicitly not reused as Regression and runtime proof:
The temporary package extraction and source-tree test logs were removed after |
|
Final current-head CI update for
The PR remains based directly on |
|
I think there's a gap in the rollout story that blocks merging as-is. The compatibility matrix misses AES key distribution optional mode makes a new Manager accept unsigned messages from an old Collector, so the signing side of a mixed-version rollout is covered. But on master, the GO_ONLINE response is the only place an old Collector ever obtains the AES key (GoOnlineProcessor.java:49-54): ServerInfo serverInfo = JsonUtil.fromJson(message.getMsg().toStringUtf8(), ServerInfo.class); This PR deletes ServerInfo entirely and strips that branch. So with new Manager + old Collector, the collector logs one warning, keeps AesUtil.secretKey at DEFAULT_ENCODE_RULES, comes online successfully — and then every job carrying a PARAM_TYPE_PASSWORD configmap fails at WheelTimerTask.java:70-73: if (item.getType() == CommonConstants.PARAM_TYPE_PASSWORD && item.getValue() != null) { The credential is set to null and collection silently breaks for every monitor with a password parameter. optional mode does not help here, because the failure is in key distribution, not message signing. The added compatibility tests exercise heartbeat/handshake paths; as far as I can tell none dispatch a job containing a real encrypted parameter, which is why this doesn't show up. Worth adding a test at that level. Suggestion: either keep a documented upgrade order (upgrade all collectors first, with common.secret pre-provisioned, before upgrading the manager) enforced/asserted somewhere, or have the manager detect a pre-auth collector and refuse to dispatch credentialed jobs to it rather than dispatching jobs it cannot decrypt. Breaking change that needs a PMC decision CollectServer.initializeAesSecret() now throws on a blank common.secret. Since the key used to arrive over Netty, every existing standalone collector will fail to start after upgrade until an operator pulls the manager's randomized AES secret out of SystemSecret and provisions it on each collector. I think that's the correct end state, and I can see the docs were updated across the deploy guides. But it's a hard compatibility break for anyone running remote collectors, so it needs to be an explicit release decision with a prominent upgrade note — not something carried under a maintenance: title. Worth raising on dev@ before merge. AUTH_CHALLENGE is processed before authentication and can re-pin the nonce In processReceiveMsg, the AUTH_CHALLENGE branch returns before the verification block, and setChannelNonce has no guard against being called again. A peer that can write to the socket can therefore send a well-formed challenge after the handshake and overwrite the stored channel nonce. It can't forge a signature, so this isn't a bypass, but it does let an on-path party break an established session at will, and it makes the channel-binding invariant weaker than it reads. Suggestion: accept a challenge only while the channel nonce is unset (mirror the APPLICATION_ACTIVE_NOTIFIED compare-and-set pattern you already use). Replay cache can evict inside the acceptance window maximumSize(65_536) with expireAfterWrite(2 × maxClockSkew): on a busy cluster — every heartbeat and every metrics message is signed and inserted — entries can be evicted well before the 10-minute window closes, at which point a captured message becomes replayable again within its skew window. The timestamp bound still limits the exposure, so this is a degradation rather than a hole, but it should be stated in the docs rather than implied to be absolute. Performance — needs numbers before I'd call it a regression Every message is signed via message.toByteArray() (a full copy), and verification does toBuilder().clearAuthSignature().build().toByteArray() — two more full serializations — on the Netty event loop. For metrics uploads, which are the large messages here, that's three extra full serializations per message plus the HMAC. I have no benchmark, so I'm flagging it as something to measure rather than asserting a regression, but given this sits on the data path it seems worth a throughput comparison before/after. Minor .gitignore and the three README files are unrelated to cluster message authentication. Splitting those out would make the PR easier to review and easier to revert if needed. |
Summary
This standalone update defines a versioned cluster-message envelope, a safe
fresh-install path, and an explicit mixed-version rollout path.
masteras one neutral commit;it no longer depends on [fix](security): stop sending AES secret over unauthenticated Netty #4263
requiredenforcement as the Java and YAML defaultoptionalavailable only as an explicit mixed-version rollout settingindependently configured authentication secret
Compatibility matrix
Installation and upgrade
Manager and every standalone Collector now receive two independent values:
COMMON_SECRET: the identical AES key on both sides, exactly 16, 24, or 32bytes
CLUSTER_AUTH_ACTIVE_SECRET: a separate cluster-message authenticationsecret of at least 32 bytes
Fresh Docker and Compose instructions use:
rand -hex 16produces 32 ASCII bytes and is therefore a valid AES-256COMMON_SECRET.rand -hex 32produces 64 ASCII characters for clusterauthentication and must not be reused directly as the AES key.
All five shipped Compose definitions fail interpolation with an actionable
message when either value is absent. Their packaged application configuration
maps
COMMON_SECRETtocommon.secret, and the same private.envis suppliedto Manager and standalone Collector.
Existing installations must preserve their current AES secret. Replacing it
during an ordinary upgrade can make encrypted data unreadable. The upgrade
guide covers recovering the existing Manager value through restricted
administrator access, provisioning both values on every node, using explicit
optional mode only during the mixed-version window, and returning to required
mode after convergence.
Regression proof
expected REQUIRED but was OPTIONAL; it now passes.not require an install-specific authentication secret; it now passes for all
five distributions.
configuration, and installation documentation omitted
COMMON_SECRET; itnow requires and documents both independent values.
CollectorDocumentedEnvironmentIntegrationTeststarts a separate realCollector JVM with the documented environment, waits for Spring startup, and
verifies that it stays alive without missing- or invalid-AES-secret errors.
apache-hertzbeat-collector-1.9.0.jarwith those same two variables andobserves stable startup.
Validation on the standalone master-based commit
documented-process reactor: all selected tests passed.
ignored
targetoutput.independent valid fixtures.
passed; the extracted packaged JAR reached stable startup with the documented
environment.
Docker, and Docker Compose archives.
expressions, the
common.secretmapping, and the corrected AES generator inall five packaged distributions.
git diff --check, andgit diff --cached --checkpassed.masterplus one commit (e533b4e106), with nodependency branch.
E2E (12m57s), Backend Image E2E (2m46s), DOC CI (9m25s), License Checker,
and labeler.
Operational notes
Blank, known-default, weak authentication secrets and invalid-length AES
secrets fail before the standalone Collector endpoint starts. Required mode
rejects unsigned peers, so existing clusters must follow the documented
explicit-optional rollout sequence rather than upgrading Manager first. Clock
synchronization, secret backup, and controlled key distribution remain
deployment responsibilities.
AI assistance: used for draft implementation and test iteration.
Human validation: reproduced the documented startup failure, ran focused
authentication/compatibility and bootstrap tests, rendered all Compose
variants, started a real packaged Collector, and completed source and release
package proofs.
Risk notes: optional mode deliberately accepts unsigned legacy traffic and must
be enabled only for a bounded rollout window; required remains the shipped and
intended steady-state default.