Added SASL/GSSAPI (Kerberos) authentication support#95
Added SASL/GSSAPI (Kerberos) authentication support#95kthimjo wants to merge 1 commit intoosodevops:mainfrom
Conversation
|
Thanks for tackling this — Kerberos is a known gap and the Phase 1 / Phase 2 state machine in Heads-up that 0.14.0 (PR #94, currently in review) has since landed the
Proposal. Rework as a I've prototyped this on Credit for the Phase 1/2 state machine stays yours either way. |
|
Hi Sion, thank you for the quick feedback, I appreciate it. My mistake for not being aware that draft PR #94 was created before this. |
Adds a `gssapi` cargo feature on both `kafka-backup-core` and `kafka-backup-cli` (passthrough) with `default = []`, plus an optional `libgssapi = "0.9"` workspace dependency. No logic changes — subsequent commits build the `GssapiPlugin` impl behind this gate. Default builds are unchanged and do not pull `libgssapi`. The gssapi feature requires system krb5 development headers at build time: - Debian/Ubuntu: `apt-get install libkrb5-dev` - RHEL/Fedora: `dnf install krb5-devel` - macOS: `brew install krb5` (then `export PKG_CONFIG_PATH="$(brew --prefix krb5)/lib/pkgconfig:$PKG_CONFIG_PATH"`) Verified: - `cargo check -p kafka-backup-core` (default, no libgssapi) - `cargo check -p kafka-backup-core --features gssapi` (pulls libgssapi) - `cargo check -p kafka-backup-cli` (default) - `cargo check -p kafka-backup-cli --features gssapi` (passthrough works) Part of the GSSAPI plugin rework superseding PR #95 (authored by @kthimjo) on the `SaslMechanismPlugin` extension point. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the always-present `SaslMechanism::Gssapi` enum variant and three optional `SecurityConfig` fields backing it: - `sasl_kerberos_service_name` — Kafka service principal (defaults to `kafka` at the CLI layer) - `sasl_keytab_path` — keytab file path; OS credential cache is used if unset - `sasl_krb5_config_path` — path to `krb5.conf`; system default if unset All three are `#[serde(default)]` so existing configs keep parsing. YAML round-trip tested: `sasl_mechanism: GSSAPI` (SCREAMING-KEBAB-CASE) decodes to `SaslMechanism::Gssapi` and all three path fields populate. The variant is always compiled (so the YAML surface is consistent across binaries), but a working GSSAPI client requires the `gssapi` cargo feature at the CLI level. Core's `authenticate()` surfaces a clear error if `SaslMechanism::Gssapi` is set without a plugin — the CLI installs a `GssapiPlugin` via `populate_sasl_plugin` in a later commit. Part of the GSSAPI plugin rework superseding PR #95 by @kthimjo. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds `kafka_backup_core::kafka::GssapiPlugin` behind the `gssapi`
cargo feature. The plugin is a state machine around
`libgssapi::context::ClientCtx` that implements RFC 4752 §3.1:
Phase 1 — gss_init_sec_context rounds (Context → ContextInProgress)
Phase 1→2 transition — empty turnaround token (AwaitingLayerProposal)
Phase 2 — unwrap broker proposal, check 0x01 (no-security-layer) bit,
wrap reply `0x01 0x00 0x00 0x00 | authz_id` (AwaitingFinalAck)
Done — broker ack closes the handshake
Notable design decisions:
- Interior mutability via `Arc<tokio::sync::Mutex<State>>` to bridge
the trait's `&self` methods with `ClientCtx::step`'s `&mut`.
- Process-wide `KRB5_ENV_LOCK: tokio::sync::Mutex<()>` serialises
`KRB5_CLIENT_KTNAME` / `KRB5_CONFIG` env-var mutation around
`Cred::acquire`. libgssapi 0.9.1 does not expose a keytab-path
argument, so env vars are the only route; without this lock,
concurrent `KafkaClient`s would race. PR #95's unsynchronised
`set_var` is the underlying issue this fixes.
- `reauth_payload` resets state to Initial and re-acquires a fresh
credential + ClientCtx — Kerberos tickets expire and a stale
context cannot be reused.
- Keytab existence is checked upfront in `new()` so misconfig fails
fast at construction rather than mid-handshake.
Day-1 spike result: libgssapi 0.9.1 exposes `Cred::acquire` and
`Cred::acquire_with_password` only; no keytab-aware constructor. The
env-var mutex is the correct mitigation for OSS until upstream gains
a keytab argument.
Tests (7 unit tests, feature-gated): Phase 2 proposal parser
(rejects <4 bytes, rejects missing 0x01 bit, accepts 0x01/0x07),
Phase 2 reply wire format, keytab-missing construction error,
continue_payload-before-initial poison, mechanism name. The full
gss_init_sec_context / wrap / unwrap round-trip is exercised by the
Docker E2E added in a later commit.
Part of the GSSAPI plugin rework superseding PR #95 by @kthimjo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ports two small improvements from PR #95: - Build an OidSet containing GSS_MECH_KRB5 and pass it to Cred::acquire's desired-mechs parameter instead of None. Locks the mechanism to Kerberos 5 rather than relying on the libgssapi default; matches the convention in librdkafka + the Java Kafka client. - Pass Some(&GSS_MECH_KRB5) to ClientCtx::new for the same reason. Plus one observability improvement adapted from PR #95: parse_phase2_proposal now returns the observed layer mask and the caller emits it at DEBUG alongside the authz_id when Phase 2 wrap succeeds, so a field report can distinguish "broker offered 0x01" from "broker offered 0x07". The thread-safe KRB5_ENV_LOCK + plugin-trait refactor + upfront keytab validation + Docker E2E fixture + unit tests + CLI flag surface remain as they were — those stay ours. Co-authored-by: Krist Thimjo <krist.thimjo@intesasanpaolo.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a `gssapi` cargo feature on both `kafka-backup-core` and `kafka-backup-cli` (passthrough) with `default = []`, plus an optional `libgssapi = "0.9"` workspace dependency. No logic changes — subsequent commits build the `GssapiPlugin` impl behind this gate. Default builds are unchanged and do not pull `libgssapi`. The gssapi feature requires system krb5 development headers at build time: - Debian/Ubuntu: `apt-get install libkrb5-dev` - RHEL/Fedora: `dnf install krb5-devel` - macOS: `brew install krb5` (then `export PKG_CONFIG_PATH="$(brew --prefix krb5)/lib/pkgconfig:$PKG_CONFIG_PATH"`) Verified: - `cargo check -p kafka-backup-core` (default, no libgssapi) - `cargo check -p kafka-backup-core --features gssapi` (pulls libgssapi) - `cargo check -p kafka-backup-cli` (default) - `cargo check -p kafka-backup-cli --features gssapi` (passthrough works) Part of the GSSAPI plugin rework superseding PR #95 (authored by @kthimjo) on the `SaslMechanismPlugin` extension point. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the always-present `SaslMechanism::Gssapi` enum variant and three optional `SecurityConfig` fields backing it: - `sasl_kerberos_service_name` — Kafka service principal (defaults to `kafka` at the CLI layer) - `sasl_keytab_path` — keytab file path; OS credential cache is used if unset - `sasl_krb5_config_path` — path to `krb5.conf`; system default if unset All three are `#[serde(default)]` so existing configs keep parsing. YAML round-trip tested: `sasl_mechanism: GSSAPI` (SCREAMING-KEBAB-CASE) decodes to `SaslMechanism::Gssapi` and all three path fields populate. The variant is always compiled (so the YAML surface is consistent across binaries), but a working GSSAPI client requires the `gssapi` cargo feature at the CLI level. Core's `authenticate()` surfaces a clear error if `SaslMechanism::Gssapi` is set without a plugin — the CLI installs a `GssapiPlugin` via `populate_sasl_plugin` in a later commit. Part of the GSSAPI plugin rework superseding PR #95 by @kthimjo. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds `kafka_backup_core::kafka::GssapiPlugin` behind the `gssapi`
cargo feature. The plugin is a state machine around
`libgssapi::context::ClientCtx` that implements RFC 4752 §3.1:
Phase 1 — gss_init_sec_context rounds (Context → ContextInProgress)
Phase 1→2 transition — empty turnaround token (AwaitingLayerProposal)
Phase 2 — unwrap broker proposal, check 0x01 (no-security-layer) bit,
wrap reply `0x01 0x00 0x00 0x00 | authz_id` (AwaitingFinalAck)
Done — broker ack closes the handshake
Notable design decisions:
- Interior mutability via `Arc<tokio::sync::Mutex<State>>` to bridge
the trait's `&self` methods with `ClientCtx::step`'s `&mut`.
- Process-wide `KRB5_ENV_LOCK: tokio::sync::Mutex<()>` serialises
`KRB5_CLIENT_KTNAME` / `KRB5_CONFIG` env-var mutation around
`Cred::acquire`. libgssapi 0.9.1 does not expose a keytab-path
argument, so env vars are the only route; without this lock,
concurrent `KafkaClient`s would race. PR #95's unsynchronised
`set_var` is the underlying issue this fixes.
- `reauth_payload` resets state to Initial and re-acquires a fresh
credential + ClientCtx — Kerberos tickets expire and a stale
context cannot be reused.
- Keytab existence is checked upfront in `new()` so misconfig fails
fast at construction rather than mid-handshake.
Day-1 spike result: libgssapi 0.9.1 exposes `Cred::acquire` and
`Cred::acquire_with_password` only; no keytab-aware constructor. The
env-var mutex is the correct mitigation for OSS until upstream gains
a keytab argument.
Tests (7 unit tests, feature-gated): Phase 2 proposal parser
(rejects <4 bytes, rejects missing 0x01 bit, accepts 0x01/0x07),
Phase 2 reply wire format, keytab-missing construction error,
continue_payload-before-initial poison, mechanism name. The full
gss_init_sec_context / wrap / unwrap round-trip is exercised by the
Docker E2E added in a later commit.
Part of the GSSAPI plugin rework superseding PR #95 by @kthimjo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ports two small improvements from PR #95: - Build an OidSet containing GSS_MECH_KRB5 and pass it to Cred::acquire's desired-mechs parameter instead of None. Locks the mechanism to Kerberos 5 rather than relying on the libgssapi default; matches the convention in librdkafka + the Java Kafka client. - Pass Some(&GSS_MECH_KRB5) to ClientCtx::new for the same reason. Plus one observability improvement adapted from PR #95: parse_phase2_proposal now returns the observed layer mask and the caller emits it at DEBUG alongside the authz_id when Phase 2 wrap succeeds, so a field report can distinguish "broker offered 0x01" from "broker offered 0x07". The thread-safe KRB5_ENV_LOCK + plugin-trait refactor + upfront keytab validation + Docker E2E fixture + unit tests + CLI flag surface remain as they were — those stay ours. Co-authored-by: Krist Thimjo <krist.thimjo@intesasanpaolo.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a `gssapi` cargo feature on both `kafka-backup-core` and `kafka-backup-cli` (passthrough) with `default = []`, plus an optional `libgssapi = "0.9"` workspace dependency. No logic changes — subsequent commits build the `GssapiPlugin` impl behind this gate. Default builds are unchanged and do not pull `libgssapi`. The gssapi feature requires system krb5 development headers at build time: - Debian/Ubuntu: `apt-get install libkrb5-dev` - RHEL/Fedora: `dnf install krb5-devel` - macOS: `brew install krb5` (then `export PKG_CONFIG_PATH="$(brew --prefix krb5)/lib/pkgconfig:$PKG_CONFIG_PATH"`) Verified: - `cargo check -p kafka-backup-core` (default, no libgssapi) - `cargo check -p kafka-backup-core --features gssapi` (pulls libgssapi) - `cargo check -p kafka-backup-cli` (default) - `cargo check -p kafka-backup-cli --features gssapi` (passthrough works) Part of the GSSAPI plugin rework superseding PR #95 (authored by @kthimjo) on the `SaslMechanismPlugin` extension point. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the always-present `SaslMechanism::Gssapi` enum variant and three optional `SecurityConfig` fields backing it: - `sasl_kerberos_service_name` — Kafka service principal (defaults to `kafka` at the CLI layer) - `sasl_keytab_path` — keytab file path; OS credential cache is used if unset - `sasl_krb5_config_path` — path to `krb5.conf`; system default if unset All three are `#[serde(default)]` so existing configs keep parsing. YAML round-trip tested: `sasl_mechanism: GSSAPI` (SCREAMING-KEBAB-CASE) decodes to `SaslMechanism::Gssapi` and all three path fields populate. The variant is always compiled (so the YAML surface is consistent across binaries), but a working GSSAPI client requires the `gssapi` cargo feature at the CLI level. Core's `authenticate()` surfaces a clear error if `SaslMechanism::Gssapi` is set without a plugin — the CLI installs a `GssapiPlugin` via `populate_sasl_plugin` in a later commit. Part of the GSSAPI plugin rework superseding PR #95 by @kthimjo. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds `kafka_backup_core::kafka::GssapiPlugin` behind the `gssapi`
cargo feature. The plugin is a state machine around
`libgssapi::context::ClientCtx` that implements RFC 4752 §3.1:
Phase 1 — gss_init_sec_context rounds (Context → ContextInProgress)
Phase 1→2 transition — empty turnaround token (AwaitingLayerProposal)
Phase 2 — unwrap broker proposal, check 0x01 (no-security-layer) bit,
wrap reply `0x01 0x00 0x00 0x00 | authz_id` (AwaitingFinalAck)
Done — broker ack closes the handshake
Notable design decisions:
- Interior mutability via `Arc<tokio::sync::Mutex<State>>` to bridge
the trait's `&self` methods with `ClientCtx::step`'s `&mut`.
- Process-wide `KRB5_ENV_LOCK: tokio::sync::Mutex<()>` serialises
`KRB5_CLIENT_KTNAME` / `KRB5_CONFIG` env-var mutation around
`Cred::acquire`. libgssapi 0.9.1 does not expose a keytab-path
argument, so env vars are the only route; without this lock,
concurrent `KafkaClient`s would race. PR #95's unsynchronised
`set_var` is the underlying issue this fixes.
- `reauth_payload` resets state to Initial and re-acquires a fresh
credential + ClientCtx — Kerberos tickets expire and a stale
context cannot be reused.
- Keytab existence is checked upfront in `new()` so misconfig fails
fast at construction rather than mid-handshake.
Day-1 spike result: libgssapi 0.9.1 exposes `Cred::acquire` and
`Cred::acquire_with_password` only; no keytab-aware constructor. The
env-var mutex is the correct mitigation for OSS until upstream gains
a keytab argument.
Tests (7 unit tests, feature-gated): Phase 2 proposal parser
(rejects <4 bytes, rejects missing 0x01 bit, accepts 0x01/0x07),
Phase 2 reply wire format, keytab-missing construction error,
continue_payload-before-initial poison, mechanism name. The full
gss_init_sec_context / wrap / unwrap round-trip is exercised by the
Docker E2E added in a later commit.
Part of the GSSAPI plugin rework superseding PR #95 by @kthimjo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ports two small improvements from PR #95: - Build an OidSet containing GSS_MECH_KRB5 and pass it to Cred::acquire's desired-mechs parameter instead of None. Locks the mechanism to Kerberos 5 rather than relying on the libgssapi default; matches the convention in librdkafka + the Java Kafka client. - Pass Some(&GSS_MECH_KRB5) to ClientCtx::new for the same reason. Plus one observability improvement adapted from PR #95: parse_phase2_proposal now returns the observed layer mask and the caller emits it at DEBUG alongside the authz_id when Phase 2 wrap succeeds, so a field report can distinguish "broker offered 0x01" from "broker offered 0x07". The thread-safe KRB5_ENV_LOCK + plugin-trait refactor + upfront keytab validation + Docker E2E fixture + unit tests + CLI flag surface remain as they were — those stay ours. Co-authored-by: Krist Thimjo <krist.thimjo@intesasanpaolo.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a `gssapi` cargo feature on both `kafka-backup-core` and `kafka-backup-cli` (passthrough) with `default = []`, plus an optional `libgssapi = "0.9"` workspace dependency. No logic changes — subsequent commits build the `GssapiPlugin` impl behind this gate. Default builds are unchanged and do not pull `libgssapi`. The gssapi feature requires system krb5 development headers at build time: - Debian/Ubuntu: `apt-get install libkrb5-dev` - RHEL/Fedora: `dnf install krb5-devel` - macOS: `brew install krb5` (then `export PKG_CONFIG_PATH="$(brew --prefix krb5)/lib/pkgconfig:$PKG_CONFIG_PATH"`) Verified: - `cargo check -p kafka-backup-core` (default, no libgssapi) - `cargo check -p kafka-backup-core --features gssapi` (pulls libgssapi) - `cargo check -p kafka-backup-cli` (default) - `cargo check -p kafka-backup-cli --features gssapi` (passthrough works) Part of the GSSAPI plugin rework superseding PR #95 (authored by @kthimjo) on the `SaslMechanismPlugin` extension point. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the always-present `SaslMechanism::Gssapi` enum variant and three optional `SecurityConfig` fields backing it: - `sasl_kerberos_service_name` — Kafka service principal (defaults to `kafka` at the CLI layer) - `sasl_keytab_path` — keytab file path; OS credential cache is used if unset - `sasl_krb5_config_path` — path to `krb5.conf`; system default if unset All three are `#[serde(default)]` so existing configs keep parsing. YAML round-trip tested: `sasl_mechanism: GSSAPI` (SCREAMING-KEBAB-CASE) decodes to `SaslMechanism::Gssapi` and all three path fields populate. The variant is always compiled (so the YAML surface is consistent across binaries), but a working GSSAPI client requires the `gssapi` cargo feature at the CLI level. Core's `authenticate()` surfaces a clear error if `SaslMechanism::Gssapi` is set without a plugin — the CLI installs a `GssapiPlugin` via `populate_sasl_plugin` in a later commit. Part of the GSSAPI plugin rework superseding PR #95 by @kthimjo. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds `kafka_backup_core::kafka::GssapiPlugin` behind the `gssapi`
cargo feature. The plugin is a state machine around
`libgssapi::context::ClientCtx` that implements RFC 4752 §3.1:
Phase 1 — gss_init_sec_context rounds (Context → ContextInProgress)
Phase 1→2 transition — empty turnaround token (AwaitingLayerProposal)
Phase 2 — unwrap broker proposal, check 0x01 (no-security-layer) bit,
wrap reply `0x01 0x00 0x00 0x00 | authz_id` (AwaitingFinalAck)
Done — broker ack closes the handshake
Notable design decisions:
- Interior mutability via `Arc<tokio::sync::Mutex<State>>` to bridge
the trait's `&self` methods with `ClientCtx::step`'s `&mut`.
- Process-wide `KRB5_ENV_LOCK: tokio::sync::Mutex<()>` serialises
`KRB5_CLIENT_KTNAME` / `KRB5_CONFIG` env-var mutation around
`Cred::acquire`. libgssapi 0.9.1 does not expose a keytab-path
argument, so env vars are the only route; without this lock,
concurrent `KafkaClient`s would race. PR #95's unsynchronised
`set_var` is the underlying issue this fixes.
- `reauth_payload` resets state to Initial and re-acquires a fresh
credential + ClientCtx — Kerberos tickets expire and a stale
context cannot be reused.
- Keytab existence is checked upfront in `new()` so misconfig fails
fast at construction rather than mid-handshake.
Day-1 spike result: libgssapi 0.9.1 exposes `Cred::acquire` and
`Cred::acquire_with_password` only; no keytab-aware constructor. The
env-var mutex is the correct mitigation for OSS until upstream gains
a keytab argument.
Tests (7 unit tests, feature-gated): Phase 2 proposal parser
(rejects <4 bytes, rejects missing 0x01 bit, accepts 0x01/0x07),
Phase 2 reply wire format, keytab-missing construction error,
continue_payload-before-initial poison, mechanism name. The full
gss_init_sec_context / wrap / unwrap round-trip is exercised by the
Docker E2E added in a later commit.
Part of the GSSAPI plugin rework superseding PR #95 by @kthimjo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ports two small improvements from PR #95: - Build an OidSet containing GSS_MECH_KRB5 and pass it to Cred::acquire's desired-mechs parameter instead of None. Locks the mechanism to Kerberos 5 rather than relying on the libgssapi default; matches the convention in librdkafka + the Java Kafka client. - Pass Some(&GSS_MECH_KRB5) to ClientCtx::new for the same reason. Plus one observability improvement adapted from PR #95: parse_phase2_proposal now returns the observed layer mask and the caller emits it at DEBUG alongside the authz_id when Phase 2 wrap succeeds, so a field report can distinguish "broker offered 0x01" from "broker offered 0x07". The thread-safe KRB5_ENV_LOCK + plugin-trait refactor + upfront keytab validation + Docker E2E fixture + unit tests + CLI flag surface remain as they were — those stay ours. Co-authored-by: Krist Thimjo <krist.thimjo@intesasanpaolo.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary
This PR adds GSSAPI/Kerberos authentication to
kafka-backup-core, enablingbackup and restore operations against Kafka clusters secured with Kerberos, a
common requirement in enterprise environments. The implementation follows the
same always-compiled, configuration-driven pattern as the existing SCRAM and
PLAIN authentication methods: if
sasl_mechanismis not set toGSSAPI, thenew code paths are never reached and behaviour is completely unchanged.
Motivation
Many organisations use Kerberos to secure their Kafka infrastructure.
kafka-backuppreviously supportedPLAINandSCRAM-SHA-256/512but had nopath for Kerberos users, making it unusable in those environments without
workarounds. This change closes that gap.
Changes
New file:
crates/kafka-backup-core/src/kafka/gssapi.rsImplements the
GssapiClientstruct, which drives the two-phase KafkaSASL/GSSAPI wire protocol (RFC 4752 §3.1):
SaslAuthenticateround-trips until
gss_init_sec_contextsignals completion.0x01(no per-message wrapping), which is the only layer Kafka requires.
Uses
libgssapi0.9.1— a safe Rustbinding over the OS GSSAPI library (
libgssapi_krb5on Linux, Heimdal onmacOS/BSD). No pure-Rust Kerberos implementation is used; all KDC
communication, ticket management, and keytab handling are delegated to the OS
library, which is the standard approach and is how the Java, Python, and
C Kafka clients work).
Modified:
crates/kafka-backup-core/src/kafka/client.rshostname: Stringfield toBrokerConnection(extracted from thebroker address at connect time) so the GSSAPI client can build the correct
GSS target principal without re-parsing the address string.
Gssapiarms toauthenticate()andauthenticate_raw(), passing thefour GSSAPI parameters (service name, broker host, keytab path, krb5 config
path) to the new
GssapiClient.sasl_gssapi_auth()(usessend_request, for initial connections) andsasl_gssapi_auth_raw()(usessend_raw_request, for reconnects), mirroringthe existing SCRAM method split that prevents async recursion.
Modified:
crates/kafka-backup-core/src/config.rsAdded
Gssapivariant toSaslMechanismand three new optional fields toSecurityConfig:sasl_kerberos_service_nameOption<String>None→"kafka"sasl.kerberos.service.namesasl_keytab_pathOption<PathBuf>NoneKRB5_CLIENT_KTNAMEsasl_krb5_config_pathOption<PathBuf>Nonekrb5.conf; setsKRB5_CONFIGAll three fields are optional. When
None, the OS Kerberos library uses itsown resolution order (
$KRB5_CLIENT_KTNAME/$KRB5_CONFIGenv vars, then/etc/krb5.conf, then the system credential cache). Existing configurationsthat do not set these fields are completely unaffected.
Modified:
crates/kafka-backup-core/src/kafka/mod.rsAdded
mod gssapi;.Modified:
crates/kafka-backup-cli/src/commands/offset_reset.rs,offset_reset_bulk.rs,offset_rollback.rsThese files construct
SecurityConfigstruct literals by field name. The threenew fields were added explicitly as
Noneto fix compilation. Full GSSAPIsupport for the offset-reset commands (reading keytab/krb5 config from the
YAML or CLI flags) is left for a possible future PR (see Known limitations).
Modified:
crates/kafka-backup-core/Cargo.tomlAdded
libgssapi = "0.9"as a regular (non-optional) dependency, consistentwith how
ringis included regardless of which auth mechanism is configured.Configuration
Below is a complete example for a SASL_SSL + GSSAPI setup. All three
GSSAPI-specific fields are optional.
Minimum viable config (relying on
kinit/OS cache and systemkrb5.conf):Build requirements
The system GSSAPI development library must be present on the build host.
The runtime host needs only the runtime library (usually installed by default).
sudo dnf install krb5-develkrb5-libs(default)sudo apt install libkrb5-devlibkrb5-3(default)brew install krb5+ setPKG_CONFIG_PATHFor CI (GitHub Actions Linux runner), add:
Testing
Tested on Red Hat Enterprise Linux 9.7 (bare metal VM) against a
multi-broker Kafka cluster with SASL_SSL + GSSAPI:
kinitcredential cache, no keytab configsasl_keytab_path, customkrb5.confsasl_keytab_path, OS defaultkrb5.confkrb5.confpathcargo testpassedTesting in Docker / Kubernetes environments would be appreciated. A
docker-composesetup with a Kerberized Kafka and a KDC container would be avaluable addition to the
kafka-backup-demosrepository.Example log output
Successful authentication with keytab:
Invalid keytab with no ticket in the OS cache:
Known limitations
Offset-reset commands (
offset-reset,offset-reset-bulk,offset-rollback) currently default all GSSAPI fields toNonein theirparse_security_confighelper. These commands will work withkinitcredential cache but do not yet read
sasl_keytab_pathorsasl_krb5_config_pathfrom YAML or CLI flags. Could be the focus ofa future PR.
This implementation has only been tested on Linux (RHEL 9.7).
Checklist
libgssapi 0.9) is justified and documentedsasl_mechanismis notGSSAPIgssapi.rs(a full Kerberos handshake test requires a live KDC; contributions welcome)