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..8ee4315 --- /dev/null +++ b/_posts/2026-09-02-release-0_24_0.md @@ -0,0 +1,145 @@ +--- +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. 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. + +**What it does in 0.24.0:** + +* **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. + +**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). + +--- + +### The API Shift: Decoupling for 1.0 + +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. + +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. + +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. + +**One break, then stable** + +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. + +--- + +### 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-migrations:0.24.0 \ + -Drewrite.activeRecipes=io.kroxylicious.migrations.rewrite.v0_24.MigrateTo0_24 +``` + +**Gradle** + +```kotlin +plugins { + id("org.openrewrite.rewrite") version "6.x.x" +} + +dependencies { + rewrite("io.kroxylicious:kroxylicious-migrations:0.24.0") +} +``` + +```bash +./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). + +--- + +### 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 %}).