From 9b86559a7b6b16b3890c74b0767355c73203f150 Mon Sep 17 00:00:00 2001 From: Sam Barker Date: Wed, 2 Sep 2026 21:34:28 +1200 Subject: [PATCH 1/5] Initial draft Signed-off-by: Sam Barker --- _posts/2026-09-02-release-0_24_0.md | 127 ++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 _posts/2026-09-02-release-0_24_0.md diff --git a/_posts/2026-09-02-release-0_24_0.md b/_posts/2026-09-02-release-0_24_0.md new file mode 100644 index 0000000..c76c009 --- /dev/null +++ b/_posts/2026-09-02-release-0_24_0.md @@ -0,0 +1,127 @@ +--- +layout: post +title: "Kroxylicious release 0.24.0" +date: 2026-09-02 00:00:00 +0000 +author: "Sam Barker" +author_url: "https://github.com/sambarker" +# noinspection YAMLSchemaValidation +categories: blog kroxylicious-proxy releases +tags: [ "releases", "kroxylicious-proxy" ] +--- + +# Kroxylicious 0.24.0: SASL Termination & API Evolution for 1.0 + +Kroxylicious 0.24.0 has snapped 🐊 into existence! + +As a maintainer group, introducing breaking API changes is something we are genuinely disappointed to do. We never take breaking changes lightly, but after careful consideration, we determined this shift was unavoidable to secure the long-term stability of the project. + +Alongside this API update, 0.24.0 also delivers a major new feature we're excited to share: **SASL Termination**. + +> **Release Highlights at a Glance:** +> * **New Feature:** SASL Termination (OAUTHBEARER, SCRAM-SHA-256/512) directly at the proxy layer. +> * **API Change:** Relocated core API types from `org.apache.kafka.*` to `io.kroxylicious.kafka.*`. +> * **Automated Upgrade:** OpenRewrite recipes available to migrate your codebase automatically. +> * **Community Feedback:** Active discussion open on upcoming design proposals. + +--- + +### Introducing SASL Termination + +Kroxylicious 0.24.0 adds built-in **SASL Termination**, allowing the proxy to authenticate incoming client connections directly rather than forwarding authentication handshakes to upstream Kafka brokers. + +**Why SASL Termination?** + +Filters that rely on client identity—such as custom authorization or dynamic routing—need to know *who* is making a request. However, challenge-response mechanisms like SCRAM generate unique per-server nonces, making passthrough inspection impossible. + +By terminating SASL at the proxy level: + +* **Verified Client Identity:** Kroxylicious authenticates the client directly and exposes a verified `ClientSaslContext` down the filter chain. +* **Supported Mechanisms:** 0.24.0 ships with support for **OAUTHBEARER**, **SCRAM-SHA-256**, and **SCRAM-SHA-512**. +* **Credential Offloading:** It lays the groundwork for authentication swapping, letting client-facing security policies differ from upstream cluster configurations. + +To explore the architecture and design behind this feature, take a look at [Design Proposal 124](https://github.com/kroxylicious/design/blob/main/proposals/124-sasl-termination.md). + +--- + +### The API Shift: Decoupling for 1.0 + +In previous releases, our API JAR relied on Apache Kafka’s `kafka-clients` JAR. In Kafka 4.3, upstream maintainers restructured what they consider non-public APIs into internal packages to make that distinction clearer. + +Because our API relied on those types, this upstream refactoring automatically introduced a breaking change for us. Rather than risk a trickle of unpredictable breaking changes in future upstream releases, we chose to take control now with a single, deliberate update. Our goal for Kroxylicious 1.0 is to offer a stable, predictable API completely under our control. + +**What Changed?** + +We relocated a minimal subset of classes previously imported from `org.apache.kafka.*` into our own namespace: `io.kroxylicious.kafka.*`. + +We moved **only** the specific classes required to keep our API functional. If you encounter any gaps or missing classes in your custom filters after upgrading, please [open an issue on GitHub](https://github.com/kroxylicious/kroxylicious/issues) so we can address it. + +--- + +### Automated Migration with OpenRewrite + +To make updating your codebase as smooth as possible, we are providing automated migration recipes powered by **OpenRewrite**. While we make it sound like a simple `x -> y` transition, a bash one-liner isn't going to cut it (no matter how good your Perl is—yes, I'm looking at you, Claude). + +**Maven** + +```bash +mvn org.openrewrite.maven:rewrite-maven-plugin:run \ + -Drewrite.recipeArtifactCoordinates=io.kroxylicious:kroxylicious-rewrite:0.24.0 \ + -Drewrite.activeRecipes=io.kroxylicious.migrations.rewrite.v0_24.MigrateTo0_24 +``` + +**Gradle** + +```groovy +plugins { + id("org.openrewrite.rewrite") version "6.x.x" +} + +rewrite { + activeRecipe("io.kroxylicious.migrations.rewrite.v0_24.MigrateTo0_24") +} +``` + +```bash +gradle rewriteRun +``` + +Further details around running these migrations can be found on (GitHub)[https://github.com/kroxylicious/kroxylicious/tree/main/kroxylicious-proxy-core/kroxylicious-migrations]. + +--- + +### Design Proposals & Shaping the Future + +We explored the trade-offs and options for this migration in [Design Proposal 116](https://github.com/kroxylicious/design/blob/main/proposals/116-kafka-api-migration.md). + +If you read Proposal 116, you will notice it originally suggested a simple `sed` script for migration. Design proposals are point-in-time snapshots of our thinking, not unchangeable commandments. As we got into implementation, OpenRewrite offered a far superior, AST-aware refactoring experience. + +This shift highlights the importance of our proposal process. If API stability or new proxy features impact how you use Kroxylicious, we strongly encourage you to review and participate in active design proposals on the [Kroxylicious Design Repository](https://github.com/kroxylicious/design). Your feedback helps shape the project as we head toward 1.0. + +### Community Contributions + +This release included commits from: +- [AdityaThakur1998](https://github.com/AdityaThakur1998) +- [DragonFSKY](https://github.com/DragonFSKY) +- [jjj-n](https://github.com/jjj-n) +- [k-wall](https://github.com/k-wall) +- [lntutor](https://github.com/lntutor) +- [lukman48](https://github.com/lukman48) +- [matheusandre1](https://github.com/matheusandre1) +- [oozan](https://github.com/oozan) +- [piotrpdev](https://github.com/piotrpdev) +- [RafaelReia](https://github.com/RafaelReia) +- [robobario](https://github.com/robobario) +- [SamBarker](https://github.com/SamBarker) +- [tamikikoz](https://github.com/tamikikoz) +- [TheDarkniteFalls](https://github.com/TheDarkniteFalls) +- [tombentley](https://github.com/tombentley) + +Thank you all, your hard work is massively appreciated by the PMC! + +### Artefacts + +Download binary distributions and container images from the [download](https://kroxylicious.io/download/0.23.0/) page. + +### Feedback + +Drop by and say hello on [Slack](https://kroxylicious.slack.com), [GitHub](https://github.com/kroxylicious/kroxylicious/issues), or [bsky](https://bsky.app/profile/kroxylicious.io). You can also join us in person at a [community call]({% link join-us/community-call/index.md %}). From 65515cca438675e31d36161aee4291f49df87f8e Mon Sep 17 00:00:00 2001 From: Sam Barker Date: Thu, 3 Sep 2026 10:52:45 +1200 Subject: [PATCH 2/5] apply Keith's feedback Signed-off-by: Sam Barker --- _posts/2026-09-02-release-0_24_0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2026-09-02-release-0_24_0.md b/_posts/2026-09-02-release-0_24_0.md index c76c009..0bfcbe3 100644 --- a/_posts/2026-09-02-release-0_24_0.md +++ b/_posts/2026-09-02-release-0_24_0.md @@ -31,7 +31,7 @@ Kroxylicious 0.24.0 adds built-in **SASL Termination**, allowing the proxy to au **Why SASL Termination?** -Filters that rely on client identity—such as custom authorization or dynamic routing—need to know *who* is making a request. However, challenge-response mechanisms like SCRAM generate unique per-server nonces, making passthrough inspection impossible. +Filters that rely on client identity—such as custom authorization or dynamic routing—need to know *who* is making a request. While SASL Inspection can sniff the identity for single-upstream deployments, it breaks down when routing across **multiple upstreams**: for PLAIN and OAUTHBEARER that works only if credentials are consistent across clusters, and for SCRAM it fails entirely because two independent server nonces are generated. SASL Termination solves this by handling authentication at the proxy. It is also useful in single-upstream scenarios where client-facing and upstream authentication mechanisms differ—for example, clients using SASL with a cluster that only supports TLS client authentication. By terminating SASL at the proxy level: From 89173b48866fb556bbfe621fe0768834fd4f3e5f Mon Sep 17 00:00:00 2001 From: Sam Barker Date: Thu, 3 Sep 2026 11:02:38 +1200 Subject: [PATCH 3/5] More details around SASL termination Signed-off-by: Sam Barker --- _posts/2026-09-02-release-0_24_0.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/_posts/2026-09-02-release-0_24_0.md b/_posts/2026-09-02-release-0_24_0.md index 0bfcbe3..3097733 100644 --- a/_posts/2026-09-02-release-0_24_0.md +++ b/_posts/2026-09-02-release-0_24_0.md @@ -31,15 +31,25 @@ Kroxylicious 0.24.0 adds built-in **SASL Termination**, allowing the proxy to au **Why SASL Termination?** -Filters that rely on client identity—such as custom authorization or dynamic routing—need to know *who* is making a request. While SASL Inspection can sniff the identity for single-upstream deployments, it breaks down when routing across **multiple upstreams**: for PLAIN and OAUTHBEARER that works only if credentials are consistent across clusters, and for SCRAM it fails entirely because two independent server nonces are generated. SASL Termination solves this by handling authentication at the proxy. It is also useful in single-upstream scenarios where client-facing and upstream authentication mechanisms differ—for example, clients using SASL with a cluster that only supports TLS client authentication. +Filters that rely on client identity—such as custom authorization or dynamic routing—need to know *who* is making a request. While SASL Inspection can sniff the identity for single-upstream deployments, it breaks down when routing across **multiple upstreams**: for PLAIN and OAUTHBEARER that works only if credentials are consistent across clusters, and for SCRAM it fails entirely because two independent server nonces are generated. SASL Termination solves this by handling authentication at the proxy. It is also useful in single-upstream scenarios where client-facing and upstream authentication mechanisms differ—for example, clients using SASL with a cluster that only supports TLS client authentication. Authentication swapping—where the proxy connects to the broker using a different mechanism than the client used—is not yet supported, but termination is the necessary foundation for it. -By terminating SASL at the proxy level: +**What it does in 0.24.0:** -* **Verified Client Identity:** Kroxylicious authenticates the client directly and exposes a verified `ClientSaslContext` down the filter chain. -* **Supported Mechanisms:** 0.24.0 ships with support for **OAUTHBEARER**, **SCRAM-SHA-256**, and **SCRAM-SHA-512**. -* **Credential Offloading:** It lays the groundwork for authentication swapping, letting client-facing security policies differ from upstream cluster configurations. +* **Authenticates clients directly** — terminates SASL at the proxy for OAUTHBEARER, SCRAM-SHA-256, and SCRAM-SHA-512. The broker never sees the client's authentication exchange. +* **Exposes a verified principal to downstream filters** — after authentication, the identity is available to any filter further down the chain, such as the new [Authorization filter](https://kroxylicious.io/documentation/0.24.0/html/authorization-guide). +* **Works without a broker connection** — authentication completes before any upstream connection is made, which is a prerequisite for routing decisions that depend on who the client is. +* **Credential isolation** — the proxy holds only PBKDF2-derived keys, not plaintext passwords. Client credentials never reach the broker. +* **KIP-368 reauthentication** — enforces session lifetimes and handles periodic reauthentication transparently. -To explore the architecture and design behind this feature, take a look at [Design Proposal 124](https://github.com/kroxylicious/design/blob/main/proposals/124-sasl-termination.md). +**Getting started** + +For SCRAM mechanisms, credentials are managed using the bundled `scram-credential-tool`, which ships in the Kroxylicious distribution at `bin/scram-credential-tool.sh`. The tool creates and manages a proxy SCRAM credential file—a PKCS#12 file containing only derived keys, never plaintext passwords. Use it to create the credential file and provision users before starting the proxy. + +The filter is also hardened against timing side-channel attacks by default: a `fixedAuthDelay` (200 ms by default) ensures authentication responses take consistent time regardless of whether a username exists or a password is correct. Phantom SCRAM challenges are generated for unknown usernames so that an attacker cannot distinguish a missing user from a failed authentication by counting protocol round-trips. + +The filter works in both standalone and Kubernetes deployments. In Kubernetes, credentials are stored in a Secret and referenced using `${secret:...}` interpolation in the `KafkaProtocolFilter` resource—the operator mounts the secret entries automatically. + +For full configuration details, see the [Authentication Guide](https://kroxylicious.io/documentation/0.24.0/html/authentication-guide). To explore the architecture and design behind this feature, see [Design Proposal 124](https://github.com/kroxylicious/design/blob/main/proposals/124-sasl-termination.md). --- From cddac43b474f9390285afa67ccf3a5311dfbdc2b Mon Sep 17 00:00:00 2001 From: Sam Barker Date: Thu, 3 Sep 2026 11:30:19 +1200 Subject: [PATCH 4/5] Add section on wire compatability Signed-off-by: Sam Barker --- _posts/2026-09-02-release-0_24_0.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/_posts/2026-09-02-release-0_24_0.md b/_posts/2026-09-02-release-0_24_0.md index 3097733..52eeb7e 100644 --- a/_posts/2026-09-02-release-0_24_0.md +++ b/_posts/2026-09-02-release-0_24_0.md @@ -55,15 +55,23 @@ For full configuration details, see the [Authentication Guide](https://kroxylici ### The API Shift: Decoupling for 1.0 -In previous releases, our API JAR relied on Apache Kafka’s `kafka-clients` JAR. In Kafka 4.3, upstream maintainers restructured what they consider non-public APIs into internal packages to make that distinction clearer. +Until now, the Kroxylicious filter API has depended directly on classes from Apache Kafka's `kafka-clients` JAR — types like `*Data` message classes, protocol infrastructure, and record classes that appear directly in filter method signatures. In Kafka 4.3, the Kafka maintainers did something entirely reasonable: they moved classes they consider implementation details into internal packages to make that boundary explicit. That's good API hygiene on their part. The uncomfortable truth it forced us to confront is that Kroxylicious was depending on things Kafka never intended as public API — and those classes lived in Kafka's codebase, under Kafka's package naming, on Kafka's timeline. That's not a stable foundation for a 1.0. -Because our API relied on those types, this upstream refactoring automatically introduced a breaking change for us. Rather than risk a trickle of unpredictable breaking changes in future upstream releases, we chose to take control now with a single, deliberate update. Our goal for Kroxylicious 1.0 is to offer a stable, predictable API completely under our control. +So in 0.24.0 we took source ownership of every type that appears in the Kroxylicious public API. The classes previously imported from `org.apache.kafka.*` now live under `io.kroxylicious.kafka.*`, mechanically translated so the sub-package hierarchy is preserved. The original Apache Software Foundation copyright headers are retained, as is appropriate for code redistributed under the Apache 2.0 licence. We now own the source, which means we can evolve it where appropriate for Kroxylicious going forward. -**What Changed?** +The practical upshot for filter developers: `kafka-clients` is no longer a compile or runtime dependency of `kroxylicious-api`. If your filter depends on `kafka-clients` directly, that's still fine — it's your dependency to manage. But the proxy core no longer drags it in transitively. The separation is real at the artifact level, not just cosmetic. -We relocated a minimal subset of classes previously imported from `org.apache.kafka.*` into our own namespace: `io.kroxylicious.kafka.*`. +**One break, then stable** -We moved **only** the specific classes required to keep our API functional. If you encounter any gaps or missing classes in your custom filters after upgrading, please [open an issue on GitHub](https://github.com/kroxylicious/kroxylicious/issues) so we can address it. +We made a deliberate choice to do this in a single, clean migration rather than absorb the changes piecemeal. Drip-feeding breaking changes across releases is worse than one honest "here's what moved, here's how to update." Our goal for 1.0 is a stable, predictable API that we control — this is the last time we expect to move these types. + +**Wire compatibility** + +Owning the source raises an obvious question: how do you know the classes you copied still produce byte-identical wire output to Kafka's originals? The answer is that we prove it on every build. We ship a suite of byte-level round-trip fidelity tests that serialise a message with our generated classes, deserialise with Kafka's, and assert equality — then run the same test in reverse. Every protocol message type, every supported protocol version. If something drifts, CI breaks before it ships. + +The wire format is governed by KIPs and is genuinely stable. The Java classes representing it were not. We've separated those two concerns. + +If you encounter any gaps or missing classes in your custom filters after upgrading, please [open an issue on GitHub](https://github.com/kroxylicious/kroxylicious/issues) — we moved what was required, but we may have missed something in the long tail. --- @@ -95,7 +103,7 @@ rewrite { gradle rewriteRun ``` -Further details around running these migrations can be found on (GitHub)[https://github.com/kroxylicious/kroxylicious/tree/main/kroxylicious-proxy-core/kroxylicious-migrations]. +Further details around running these migrations can be found on [GitHub](https://github.com/kroxylicious/kroxylicious/tree/main/kroxylicious-proxy-core/kroxylicious-migrations). --- From a0a5518d08684f3de084e6f83a14449c06450aa5 Mon Sep 17 00:00:00 2001 From: Sam Barker Date: Thu, 3 Sep 2026 12:07:17 +1200 Subject: [PATCH 5/5] migration fixes Signed-off-by: Sam Barker --- _posts/2026-09-02-release-0_24_0.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/_posts/2026-09-02-release-0_24_0.md b/_posts/2026-09-02-release-0_24_0.md index 52eeb7e..8ee4315 100644 --- a/_posts/2026-09-02-release-0_24_0.md +++ b/_posts/2026-09-02-release-0_24_0.md @@ -83,24 +83,24 @@ To make updating your codebase as smooth as possible, we are providing automated ```bash mvn org.openrewrite.maven:rewrite-maven-plugin:run \ - -Drewrite.recipeArtifactCoordinates=io.kroxylicious:kroxylicious-rewrite:0.24.0 \ + -Drewrite.recipeArtifactCoordinates=io.kroxylicious:kroxylicious-migrations:0.24.0 \ -Drewrite.activeRecipes=io.kroxylicious.migrations.rewrite.v0_24.MigrateTo0_24 ``` **Gradle** -```groovy +```kotlin plugins { id("org.openrewrite.rewrite") version "6.x.x" } -rewrite { - activeRecipe("io.kroxylicious.migrations.rewrite.v0_24.MigrateTo0_24") +dependencies { + rewrite("io.kroxylicious:kroxylicious-migrations:0.24.0") } ``` ```bash -gradle rewriteRun +./gradlew rewriteRun -Drewrite.activeRecipe=io.kroxylicious.migrations.rewrite.v0_24.MigrateTo0_24 ``` Further details around running these migrations can be found on [GitHub](https://github.com/kroxylicious/kroxylicious/tree/main/kroxylicious-proxy-core/kroxylicious-migrations).