Skip to content

fix: kraft kerberos support - #999

Draft
razvan wants to merge 14 commits into
mainfrom
feature/kraft-kerberos-support
Draft

fix: kraft kerberos support#999
razvan wants to merge 14 commits into
mainfrom
feature/kraft-kerberos-support

Conversation

@razvan

@razvan razvan commented Aug 7, 2026

Copy link
Copy Markdown
Member
  • docs: add KRaft controller Kerberos implementation plan
  • feat: allow SASL_SSL on the KRaft CONTROLLER listener when Kerberos is enabled
  • feat: set sasl.mechanism.controller.protocol for Kerberos-enabled clusters
  • feat: support pod-scoped Kerberos keytabs for roles without a kcat prober container
  • feat: mount Kerberos keytab into KRaft controller pods when enabled
  • feat: generate a controller.KafkaServer JAAS section for the CONTROLLER listener
  • feat: export KERBEROS_REALM and template jaas.properties in the KRaft controller startup command
  • fix: make controller_kafka_container_command byte-identical to pre-Kerberos output when disabled
  • test: add kuttl integration test for Kerberos-secured KRaft clusters
  • fix: use kafka-kraft dimension (not kafka) for access-kafka image in kraft-kerberos test
  • docs: document Kerberos support for KRaft controllers
  • fix: address final whole-branch review findings for KRaft Kerberos
  • fix: use explicit numeric offset in kraft-kerberos kcat consume step
  • docs: reflect live-cluster verification of Kerberos KRaft support

Description

Please add a description here. This will become the commit message of the merge request later.

Definition of Done Checklist

  • Not all of these items are applicable to all PRs, the author should update this template to only leave the boxes in that are relevant
  • Please make sure all these things are done and tick the boxes

Author

  • Changes are OpenShift compatible
  • CRD changes approved
  • CRD documentation for all fields, following the style guide.
  • Helm chart can be installed and deployed operator works
  • Integration tests passed (for non trivial changes)
  • Changes need to be "offline" compatible
  • Links to generated (nightly) docs added
  • Release note snippet added

Reviewer

  • Code contains useful comments
  • Code contains useful logging statements
  • (Integration-)Test cases added
  • Documentation added or updated. Follows the style guide.
  • Changelog updated
  • Cargo.toml only contains references to git tags (not specific commits or branches)

Acceptance

  • Feature Tracker has been updated
  • Proper release label has been added
  • Links to generated (nightly) docs added
  • Release note snippet added
  • Add type/deprecation label & add to the deprecation schedule
  • Add type/experimental label & add to the experimental features tracker

razvan and others added 14 commits August 7, 2026 15:13
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…s enabled

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…sters

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ober container

add_kerberos_pod_config now takes cb_kcat_prober: Option<&mut ContainerBuilder>
instead of a required reference, and scopes the Kerberos keytab volume based
on the KafkaRole: Broker keeps the existing listener-volume scoping (client +
bootstrap listeners), Controller gets a pod-scoped keytab since KRaft
controllers have no listener-operator Listener volume.

Updates the one existing call site in build_broker_rolegroup_statefulset to
pass Some(&mut cb_kcat_prober).

Adds a unit test covering the controller-role path (pod-scoped keytab,
no kcat container, env vars still applied to the kafka container).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wire the already-refactored add_kerberos_pod_config (Task 3) into
build_controller_rolegroup_statefulset so KRaft controller pods get a
pod-scoped kerberos volume, KRB5_CONFIG and KAFKA_OPTS on the kafka
container when kafka_security.has_kerberos_enabled() is true. Broker
pods already got this via build_broker_rolegroup_statefulset.

Adds two unit tests covering the enabled and disabled cases.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ER listener

Broker pods reuse the broker listener address already used for
client.KafkaServer, since their keytab only covers broker/bootstrap
listener addresses. Controller pods use their own pod FQDN template
(same as KAFKA_LISTENERS in controller_properties.rs), since they have
no listener-operator Listener volume and their keytab is pod-scoped.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… controller startup command

Wires Kerberos startup handling into controller_kafka_container_command,
matching the broker equivalent (broker_kafka_container_commands): when
Kerberos is enabled it now exports KERBEROS_REALM from krb5.conf and
copies/templates jaas.properties into /tmp before kafka-storage.sh
format and kafka-server-start.sh run. Behaviour is unchanged when
Kerberos is disabled (ZooKeeper mode / plaintext).

- controller_kafka_container_command gains a kafka_security: &ValidatedKafkaSecurity
  parameter (first position, matching broker_kafka_container_commands's style).
- Updated the sole call site in build_controller_rolegroup_statefulset.
- Added unit tests covering both the Kerberos-enabled and disabled paths.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…rberos output when disabled

Restructure the formatdoc! template in controller_kafka_container_command
so the set_realm_env and jaas_setup placeholders no longer sit next to a
literal blank template line. Instead, blank-line separation is embedded
in the substituted value itself, so an empty (Kerberos-disabled)
substitution produces exactly the same output as before Kerberos support
was added, satisfying the plan's byte-identity constraint.

Add controller_command_is_byte_identical_to_pre_kerberos_output_when_disabled,
which reconstructs the pre-change (commit d9942ad) template as a
test-only helper and asserts real string equality against the current
Kerberos-disabled output, replacing reliance on substring-only checks.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds tests/templates/kuttl/kraft-kerberos/, combining the KRaft
spec.controllers setup from smoke-kraft with the MIT KDC deployment,
Kerberos AuthenticationClass/SecretClass, and kcat produce/consume job
from the kerberos test case. Registers the new case as 'kraft-kerberos'
in tests/test-definition.yaml alongside kafka-kraft/operations-kraft/
smoke-kraft.

NOTE: untested in this environment - no reachable Kubernetes cluster
was available to run kuttl against. Validated statically only (YAML/
Jinja rendering, CRD field-path cross-checks, structural comparison
against smoke-kraft and kerberos). See task-8-report.md for details.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…kraft-kerberos test

The kraft-kerberos kuttl test case declares the kafka-kraft dimension in
tests/test-definition.yaml, not kafka. 30-access-kafka.txt.j2 was copied
verbatim from tests/templates/kuttl/kerberos/30-access-kafka.txt.j2 and
still referenced test_scenario['values']['kafka'], which is Undefined
for this scenario and would raise a Jinja2 UndefinedError at render
time, preventing the produce/consume Job from rendering at all.

Fixed the reference to use kafka-kraft, and brought the image-rendering
logic in line with this same test case's 20-install-kafka.yaml.j2 (and
smoke-kraft/30-install-kafka.yaml.j2), which both support the
'version[,custom-image]' convention for the kafka-kraft dimension via
an if/else branch on a comma in the value.

Re-ran static Jinja2/YAML validation with a stub test_scenario dict
built strictly from the kraft-kerberos entry's actual dimensions (no
stray kafka key this time), for both the plain-version and
custom-image-comma forms of kafka-kraft. All 12 files in the test case
render and parse without UndefinedError.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- controller.KafkaServer JAAS section: drop isInitiator=false so the
  process can act as GSSAPI initiator on the CONTROLLER listener
  (needed since brokers connect to controllers and controllers
  connect to each other for Raft); add explanatory comment and a
  test asserting the controller section specifically lacks
  isInitiator=false.
- kraft-kerberos kuttl test: bump controller replicas to 3 so
  inter-controller Kerberos-authenticated Raft traffic is actually
  exercised; update the corresponding assert step.
- kraft-kerberos test-definition.yaml: drop the orthogonal
  bootstrap-listener-class dimension (already covered by the plain
  kerberos test case); pin it to cluster-internal in the template,
  following the smoke-kraft precedent.
- docs: promote the Kerberos subsection out from under Known Issues
  into its own top-level section before Internal operator details;
  correct the claim that Kerberos covers the INTERNAL listener (it
  stays mutual TLS, unaffected by Kerberos); restore an accurate
  caveat in supported-versions.adoc noting the integration test is
  implemented/unit-tested but not yet run end-to-end against a live
  cluster.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The bundled kcat's librdkafka (1.7.0) mis-detects broker feature support
against Kafka >=4.0 brokers: KIP-896 dropped old low-numbered API versions,
and this librdkafka version matches ApiVersions by exact version instead
of range, so it wrongly reports the ListOffsets logical-offset query
("-o stored" falling back to auto.offset.reset=beginning) as unsupported
even though the broker handles it fine (confluentinc/librdkafka#4948).

This is unrelated to SASL/Kerberos or KRaft: broker logs show every GSSAPI
handshake succeeding, including inter-controller and broker-to-controller
auth over the CONTROLLER listener. Only the client-side logical-offset
detection was wrong. Swapping to an explicit numeric offset ("-o 0") -
valid here since the topic is freshly created and this is the only
message ever produced to it - routes around the buggy code path entirely.

Confirmed against a live minikube cluster with a real MIT KDC: the kuttl
test (kraft-kerberos_kafka-kraft-4.2.1_..._kerberos-realm-PROD.MYCORP_...)
now passes end-to-end, including 3-controller Raft quorum formation over
Kerberos and real message produce/consume through the CLIENT listener.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The kraft-kerberos kuttl test now passes against a real minikube cluster
with a real MIT KDC (Kafka 4.2.1, 3 controller replicas, GSSAPI over the
CONTROLLER listener). Update the caveat that previously said end-to-end
verification was pending.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@razvan razvan self-assigned this Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant