KAFKA-20025: Reload KRaft Raft controller-quorum SSL keystore on dynamic update - #23288
Open
sidb98 wants to merge 1 commit into
Open
KAFKA-20025: Reload KRaft Raft controller-quorum SSL keystore on dynamic update#23288sidb98 wants to merge 1 commit into
sidb98 wants to merge 1 commit into
Conversation
…mic update Register the Raft channel's SslChannelBuilder as a Reconfigurable on the broker/controller config so a dynamic controller-listener keystore update reaches the running Raft channel. KafkaRaftManager exposes the builder; BrokerServer/ControllerServer register it on their own config (sharedServerConfig never receives cert-rotation updates). Co-Authored-By: Claude Opus 5 <claude@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
In KRaft,
KafkaRaftManagerbuilds its controller-quorum network client with anSslChannelBuilderthat is never registered as aReconfigurableon the config thatprocesses dynamic updates. A renewed controller-listener certificate therefore never
reaches the running Raft channel: it keeps the cert loaded at startup and, on the next
reconnect (e.g. controller failover), can present a stale/expired cert, breaking the
broker↔controller connection and stalling metadata propagation. (KAFKA-20025.)
SharedServercreates threeKafkaConfigobjects (sharedServerConfig,brokerConfig,controllerConfig).RaftManageris constructed withsharedServerConfig, whosedynamicConfigis initialized but never receives cert-rotation updates (those go tobrokerConfig/controllerConfig), so self-registering on RaftManager's own config wouldbe a silent no-op.
Changes
KafkaRaftManager: expose the channel'sReconfigurable(reconfigurableChannelBuilder)and capture it when the channel builder is an SSL builder.
BrokerServer/ControllerServer: register it on their own config(
config.dynamicConfig.addReconfigurable) alongside the other reconfigurables, keepingRaftManageronsharedServerConfigwhile routing the reconfigurable to the config thatactually receives cert-rotation updates.
Testing
Unit :
RaftManagerTest.testRaftManagerExposesSslChannelBuilderAsReconfigurable: aRaftManager built with an SSL controller listener exposes its
SslChannelBuilderviareconfigurableChannelBuilder.Integration :
RaftManagerSslReconfigIntegrationTest: brings up an SSLKRaft cluster, rotates the broker's
listener.name.controller.ssl.keystore.*from cert Ato cert B via
incrementalAlterConfigs, and asserts the certificate serial loaded in therunning Raft channel's
SslFactory(read by reflection) flips A → B. Reverting theregistration makes it fail with "Raft channel SslFactory never loaded the rotated keystore
(cert B)". Deterministic: it inspects the loaded keystore directly, not cert expiry or
handshake timing. Cert A and B share a CA (controller trusts both; cluster stays healthy)
and a subject DN.
Alternate integration test considered but rejected
Kraft cluster with short-lived cert A → rotate to B → force re-handshake → assert no SSL errorThis is flaky by construction: invalidating the old cert relies on expiry (A and B share
a CA), and JSSE's PKIX validation reads the real system clock with no injectable clock,
giving a bring-up race and an expiry race that generous margins only slow down, never
remove. The implemented test asserts the fix's effect deterministically instead.
Why this approach rather than #21333
#21333 targets the same root cause and is a valid fix, the difference is where the
reconfigurable is registered, and test coverage.
KAFKA-20025: enable dynamic TLS reconfiguration for KafkaRaftManager #21333 changes the
KafkaConfiginstance passed intoKafkaRaftManager(fromsharedServerConfigtobrokerConfig/controllerConfigdepending on role) andself-registers the channel builder on it. That reaches the right config, but it also
changes the config object
RaftManageruses for everything it reads (node id, quorumconfig, connection settings, …), a broader change than the fix strictly needs.
This PR leaves
KafkaRaftManageronsharedServerConfig(no change to whatRaftManagerreads) and instead exposes the channel builder so thatBrokerServer/ControllerServerregister it on their own config, alongside the otherreconfigurables. The registration sits where the existing reconfigurables are already
wired, and the change stays scoped to just the registration.
Tests: this PR adds a unit test plus a deterministic integration test that rotates the
controller-listener keystore at runtime and asserts the running Raft
SslFactoryreloads it, verified to fail without the fix. KAFKA-20025: enable dynamic TLS reconfiguration for KafkaRaftManager #21333 currently has no tests.