strimzi / strimzi-kafka-oauth

OAuth2 support for Apache Kafka® to work with many OAuth2 authorization servers
Apache License 2.0
147 stars 90 forks source link

Read trustore configuration from client properties #175

Closed fvaleri closed 2 years ago

fvaleri commented 2 years ago

Currently, we document the following configuration for OAuth client authentication:

cat > /tmp/team-a-client.properties << EOF
security.protocol=SASL_SSL
ssl.truststore.location=/tmp/truststore.p12
ssl.truststore.password=$STOREPASS
ssl.truststore.type=PKCS12
sasl.mechanism=OAUTHBEARER
sasl.jaas.config=org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required \
  oauth.client.id="team-a-client" \
  oauth.client.secret="team-a-client-secret" \
  oauth.ssl.truststore.location="/tmp/truststore.p12" \
  oauth.ssl.truststore.password="$STOREPASS" \
  oauth.ssl.truststore.type="PKCS12" \
  oauth.token.endpoint.uri="https://$SSO_HOST/auth/realms/kafka-authz/protocol/openid-connect/token" ;
sasl.login.callback.handler.class=io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler
EOF

In some cases, users create a single truststore file containing both the Kafka and the authorization server CA public certificates. In this case the truststore configuration is effectively duplicated.

The environment variables configuration is the only way to avoid that duplication:

export KAFKA_OPTS="-Djavax.net.ssl.trustStore=/tmp/truststore.p12 \
  -Djavax.net.ssl.trustStorePassword=changeit -Djavax.net.ssl.trustStoreType=PKCS12"
$KAFKA_HOME/bin/kafka-console-producer.sh --bootstrap-server my-cluster-kafka-bootstrap:9093 \
  --topic my-topic --producer.config /tmp/client.properties

It would be handy if the OAuth plugin could get the truststore configuration from the Kafka client property file, in addition to sasl.jaas.config and environment variables.

scholzj commented 2 years ago

I don't think this should be mixed. These are completely unrelated settings, so it IMHO does not make sense. But also the idea itself to put everything into a single truststore is wrong and insecure (unless there is actually only a single CA which signed both) because it means that if someone can get to the Kafka CA, it canbe used to impersonate the OAuth server or the other way around.

fvaleri commented 2 years ago

The idea itself to put everything into a single truststore is wrong and insecure

I don't get why it is insecure to have two or more different CA public certificates in the same client truststore. Can you elaborate on that?

In the JDK truststore you have a lot of well known CAs and that's considered secure. What if my client needs to talk to 10 different trusted external systems? Do I need to create 10 different truststores?

if someone can get to the Kafka CA, it canbe used to impersonate the OAuth server or the other way around.

They are different CAs, one for Kafka and another for the authorization server.

It's same approach showcased in the example of the following blog post:

https://strimzi.io/blog/2019/10/25/kafka-authentication-using-oauth-2.0

mstruk commented 2 years ago

I agree that if we want to be opinionated about this we should give an explanation that makes the vulnerabilities of a single truststore in this context more obvious.

Can we think of a hypothetical case that makes this obvious? I'm interested in this as well as I wouldn't really consider it problematic.

The reason this is currently not supported is that at the time I couldn't get to non-jaas configuration when initialising the OAuth callback handler. Otherwise I would probably support using the proposed config as a fallback.

Maybe setting multiple truststores for different backend servers may be a better practice in Kubernetes environment?

scholzj commented 2 years ago

I don't get why it is insecure to have two or more different CA public certificates in the same client truststore. Can you elaborate on that?

The Kafka truststore is used to verify the identtity of the Kafka cluster. So it should have the Kafka CA in it. If it has more CAs, which are not used in Kafka, it means that somone who gets access to these CAs can use it and for example setup a fake Kafka broker and trick the client to connect to it (TLS will pass becaue the CA is in the truststore) and to produce some messages to this fake broker.

Or the other way around and probably even worse. If someone gets access to the Kafka CA which is stored in a shared truststore with OAuth CA, you can use it to setup fake OAuth server and trick the client to use it to get token and get the client ID and secret through this.

If you use separate truststores, then this is more complicated because it is not only about tricking the client to connect somewhere else, but also subverting a modified truststore.


There are also some other obvious issues:

So you would likely need a flag in the OAuth configuration to enable this. To sum it up, you get very questionable improvement of a user configuring two less option and saving what? 100 characters in the config? And even that would be very questionable usability improvement if it needs to be explicitely enabled.


In the JDK truststore you have a lot of well known CAs and that's considered secure. What if my client needs to talk to 10 different trusted external systems? Do I need to create 10 different truststores?

That is comparing apples to oranges. These are public CAs and they are used in a different way then private CAs. If you use Kafka and OAuth server with certs signed by the public CAs, you should use the JVM's default truststore and not worry about Kafka's truststore configuration.

fvaleri commented 2 years ago

I'm just trying to understand, because the current behavior was unexpected to me.

If someone gets access to the Kafka CA which is stored in a shared truststore with OAuth CA

It's not a shared truststore, but the client's truststore, which is protected by a very strong password and only used by this client.

It is not backwards compatible change, because suddenly, the Kafka truststore which was ignored by the OAuth client might be used by default.

If the sasl.jaas.config has always the precedence, there would be no backwards incompatible change IMO. Today, if you use KAFKA_OPTS the Kafka truststore is not ignored and the same truststore config is used if you don't configure it in sasl.jaas.config.

scholzj commented 2 years ago

It's not a shared truststore, but the client's truststore, which is protected by a very strong password and only used by this client.

You suggest it to be shared - between the Kafka client and the OAuth client.

If the sasl.jaas.config has always the precedence, there would be no backwards incompatible change IMO. Today, if you use KAFKA_OPTS the Kafka truststore is not ignored and the same truststore config is used if you don't configure it in sasl.jaas.config.

There will be a backwards incompatible change for all users who did not defined the truststore in the JAAS configuration which AFAIK is not mandatory.

fvaleri commented 2 years ago

There will be a backwards incompatible change for all users who did not defined the truststore in the JAAS configuration which AFAIK is not mandatory.

That's true.

I don't know if it's just me or this confused other users. Maybe we can simply add a documentation note which says that the Kafka truststore configuration is not shared with sasl.jaas.config configuration.

scholzj commented 2 years ago

For me that is obvious ... but sure, that can be improved, never hurts to have the docs more clear.