Skip to content

Conversation

@mstruk
Copy link
Contributor

@mstruk mstruk commented Oct 22, 2025

This PR fixes the issue when using KeycloakAuthorizer in Kafka 4.1.0 when delegation to StandardAuthorizer is enabled (strimzi.authorization.delegate.to.kafka.acl=true).

Whenever the authorization decision is delegated to StandardAuthorizer the following exception happens:

java.lang.NullPointerException: Cannot invoke "org.apache.kafka.metadata.authorizer.StandardAuthorizer$AuthorizerMetrics.recordAuthorizerMetrics(org.apache.kafka.server.authorizer.AuthorizationResult)" because "this.authorizerMetrics" is null
	at org.apache.kafka.metadata.authorizer.StandardAuthorizer.authorize(StandardAuthorizer.java:153) ~[kafka-metadata-4.1.0.jar:?]
	at io.strimzi.kafka.oauth.server.authorizer.KeycloakRBACAuthorizer.delegateIfRequested(KeycloakRBACAuthorizer.java:469) ~[kafka-oauth-keycloak-authorizer-0.17.0.jar:?]
	at io.strimzi.kafka.oauth.server.authorizer.KeycloakRBACAuthorizer.authorize(KeycloakRBACAuthorizer.java:340) ~[kafka-oauth-keycloak-authorizer-0.17.0.jar:?]
	at io.strimzi.kafka.oauth.server.authorizer.KeycloakAuthorizer.authorize(KeycloakAuthorizer.java:227) ~[kafka-oauth-keycloak-authorizer-0.17.0.jar:?]
...

Kafka 4.1.0 added new metrics pluggability mechanism and updated StandardAuthorizer to implement Monitorable interface and expect a PluginMetrics object to be provided by instantiating server via Monitorable.withPluginMetrics(PluginMetrics pluginMetrics).

For KeycloakAuthorizer to be deployable to Kafka 4.0.0 where Monitorable interface is not present, and to also properly work on Kafka 4.1.0 where Monitorable.withPluginMetrics() is expected to be called on StandardAuthorizer the only way to address this without having multiple versions of KeycloakAuthorizer is to use reflection.

It is not possible for KeycloakAuthorizer to implement Monitorable and still be compatible with Kafka 4.0.0, thus the PluginMetrics instance that we set on StandardAuthorizer when running in Kafka 4.1.0 is not passed in and backed by the Metrics object of BrokerServer or ControllerServer but by a separate internally managed Metrics object used by all the Strimzi OAuth pluggable classes.

@mstruk mstruk requested a review from mimaison October 22, 2025 18:43
@mstruk mstruk added this to the 0.17.1 milestone Oct 23, 2025
Copy link
Contributor

@mimaison mimaison left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, I left a few comments.

How much effort would it take to stop using non-public Kafka APIs?

private void initPluginMetricsIfNeeded() {
Class<?> monitorableClass = null;
try {
// Check if StandardAuthorizer implements Monitorable, which can only be true if running on Kafka 4.1.0+
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is a bit misleading. This block checks if the Monitorable class is present, ie, we're running >=4.1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a suggestion how to rephrase?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about // Check if Monitorable is present. This indicates if we're running on Kafka 4.1.0+?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

pluginMetrics = method.invoke(null, delegate, metrics, "authorizer.class.name", "role", determineRole());
}
} catch (Throwable e) {
log.warn("Failed to initialise PluginMetrics on StandardAuthorizer", e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it fatal if we reach this point?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it will fail later during StandardAuthorizer.authorize() calls with 'this.authorizerMetrics is null' rather than at bootup in KeycloakAuthorizer.configure(). It a choice here whether to fail fast at boot or later on. I can change it to fail fast.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know the project enough to advise where it's best to fail. In theory failing fast is often preferred but obviously it depends, I trust you to make the right call. But if it's fatal, maybe log it as fatal or error rather than warn?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified so it fails fast.

@mstruk
Copy link
Contributor Author

mstruk commented Oct 23, 2025

How much effort would it take to stop using non-public Kafka APIs?

Can you be more specific here? We wrap StandardAuthorizer and take control of its lifecycle. StandardAuthorizer is not considered public API based on its package. But we need a way to delegate to it and control its lifecycle. So what are even the options?

@mstruk mstruk requested a review from mimaison October 23, 2025 15:04
@mimaison
Copy link
Contributor

Can you be more specific here? We wrap StandardAuthorizer and take control of its lifecycle. StandardAuthorizer is not considered public API based on its package. But we need a way to delegate to it and control its lifecycle. So what are even the options?

I don't know enough about this component to tell. We can discuss this after this is resolved.

@mstruk mstruk force-pushed the fix-for-kafka-4.1.0 branch from 1bca209 to f194b97 Compare October 24, 2025 10:36
Copy link
Contributor

@mimaison mimaison left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mstruk mstruk requested a review from scholzj October 24, 2025 11:41
Copy link
Member

@scholzj scholzj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @mstruk

@mstruk mstruk merged commit ed1f658 into strimzi:main Oct 24, 2025
6 checks passed
mstruk added a commit to mstruk/strimzi-kafka-oauth that referenced this pull request Oct 28, 2025
@StevenJDH
Copy link

@mstruk and @mimaison is there a workaround for this? I'm currently using Strimzi 0.48.0 and Kakfa 4.1.0 which doesn't have yet this fix included.

@mstruk
Copy link
Contributor Author

mstruk commented Nov 6, 2025

Until Strimzi 0.49.0 is released what you can do is make your own Kafka 4.1.0 image based on 0.48.0 and replace in the libs directory thekafka-oauth-*-0.17.0.jar files with kafka-oauth-*-0.17.1.jar files.
You really only need to replace kafka-oauth-common and kafka-oauth-keycloak-authorizer.

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.

4 participants