
You probably already know about Strimzi. Strimzi enables Kafka on Kubernetes, simple as that. So, in this post, I will not talk about Strimzi, but on how to create a simple mTLS authentication on top of it. Follow this post to learn how, I will go directly to the point.
Prerequisites
- streams for Apache Kafka operator installed in Openshift Container Platform (it can be the Strimzi community version on vannila Kubernetes also);
- openssl tool installed in your machine;
- KafkaNodePool already created in the Openshift project;
- Kafka distribution/clients in your local environment.
Configure Kafka cluster
The Kafka cluster is created by configuring the Kafka custom resource (CR) in YAML, and applying it to the Openshift cluster. The relevant part of this configuration, for this example, is the listener configuration.
## This is just a snippet of Kafka custom resource
apiVersion: kafka.strimzi.io/v1
kind: Kafka
metadata:
name: test-kafka-cluster
spec:
kafka:
listeners:
- name: tls
port: 9093
type: route
tls: true
authentication:
type: tls
Notice that in the above configuration, I enabled tls and configured the authentication type as tls. The first enables regular TLS encryption, the second enables TLS authentication, or Mutual TLS, the subject of this post.
When using Strimzi, you are not required to create the user certificate by yourself. Just create the KafkaUser custom resource (CR) that all the certificates will be created automatically by the Strimzi operator. Here is an example of a KafkaUser CR (notice that authentication type is also tls):
apiVersion: kafka.strimzi.io/v1
kind: KafkaUser
metadata:
labels:
strimzi.io/cluster: test-kafka-cluster
name: test-kafka-user
spec:
authentication:
type: tls
Verify the creation of Secret with the same name of the created KafkaUser:
oc describe secret test-kafka-user
Five files were created in the Secret by the operator. The output of the command will be something like this:
Name: test-kafka-user
Namespace: test-kafka-project
Labels: app.kubernetes.io/instance=test-kafka-user
app.kubernetes.io/managed-by=strimzi-user-operator
app.kubernetes.io/name=strimzi-user-operator
app.kubernetes.io/part-of=strimzi-test-kafka-user
strimzi.io/cluster=test-kafka-cluster
strimzi.io/kind=KafkaUser
Annotations: <none>
Type: Opaque
Data
====
ca.crt: 1854 bytes
user.crt: 1521 bytes
user.key: 1704 bytes
user.p12: 2974 bytes
user.password: 32 bytes
That's all for the Kafka cluster side. Now let's create the client.
Running the client
To run the client, we will use both standard kafka-console-producer.sh and kafka-console-consumer.sh scripts. These scripts are available in the standard Kafka distribution, so you need to download it first.
Before we can run the scripts, we need to get the relevant certificates for the clients. We need the Kafka cluster certificate, that is on the -cluster-ca-cert Secret, and the KafkaUser certificate, that is on the Secret with the same name. The name of the Kafka cluster is this example is test-kafka-cluster, so the commands to get the certificates from Openshift cluster are:
# Get the Kafka cluster certificate
oc get secret test-kafka-cluster-cluster-ca-cert -o jsonpath='{.data.ca\.p12}' | base64 -d > test-kafka-cluster-ca.p12
# Get the KafkaUser certificate
oc get secret test-kafka-user -o jsonpath='{.data.user\.p12}' | base64 -d > test-kakfa-user.p12
Notice that I choose the P12 file, or PKCS 12 file, which is an encrypted archive file format to store certificates on it, is not the certificates itself, but a certificate store (Remember Java KeyStore?).
With both certificates in hand, create the client.property file. This file configure the mTLS, the truststore and the keystore that will be used to communicate with Kafka.
security.protocol=SSL
# Truststore contains the Kafka cluster certificate
ssl.truststore.location=test-kafka-cluster-ca.p12
ssl.truststore.password=PasswdFromSecret
ssl.truststore.type=PKCS12
# Keystore contains the KafkaUser certificate
ssl.keystore.location=angelo-kakfa-user.p12
ssl.keystore.password=PasswdFromSecret
ssl.keystore.type=PKCS12
# Enable mTLS
ssl.client.auth=required
ssl.endpoint.identification.algorithm=
Notice that the Kafka cluster certificate is configured as truststore, and the KafkaUser certificate is configured as keystore.
Last, but no least, we need to get the Route URL for accessing the Kafka cluster outside Openshift. We are interested in the bootstrap route. So, to get the route URL, run the following command:
oc get routes
Now we have everything ready to run the client scripts. For producing messages, run the following command:
./kafka-console-producer.sh --bootstrap-server test-kafka-cluster-kafka-tls-bootstrap-test-kafka-project.apps-crc.testing:443 --topic test-topic --command-config client.properties
For consuming messages, run the following command:
./kafka-console-consumer.sh --bootstrap-server test-kafka-cluster-kafka-tls-bootstrap-test-kafka-project.apps-crc.testing:443 --topic test-topic --command-config client.properties --from-beginning
Extra: Using custom certificate
Maybe you decide to use a custom certificate for your listener. You may be wondering: How can you configure that? How you configure the client to use it?
So, the first thing to do is to create the custom certificate:
# I am using openssl, but you can use Java keytool instead.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout custom-key.key -out custom-cert.crt
Then, upload it to Kafka cluster:
oc create secret tls custom-certificate --cert=custom-cert.crt --key=custom-key.key
The Kafka CR configuration will be very similar as before, but now with the custom certificate configuration:
## Remember, this is just a snippet of Kafka custom resource, not the full CR
apiVersion: kafka.strimzi.io/v1
kind: Kafka
metadata:
name: kafka-cluster
spec:
kafka:
listeners:
- name: tls
port: 9093
type: route
tls: true
authentication:
type: tls
configuration:
brokerCertChainAndKey:
certificate: tls.crt
key: tls.key
secretName: custom-certificate
If you have good eyes, you noticed that the certificate names are not the same names that we use to create the Secret, but tls.crt and tls.key. This is normal, Openshift change its names, so, don't worry about it.
On the client side, before you can run the clients, create the PKCS 12 truststore with the custom certificates. I already have them in my working directory, so I don't need to download them from Openshift again, however, if you don't have them in your working directory, download both tls.crt and tls.key from the configured Secret.
Now, with the custom certificates in hand, create the PKCS 12 truststore using openssl (you will be asked to choose a password):
openssl pkcs12 -export -out custom-certificate.p12 -inkey custom-key.key -in custom-cert.crt
The modify the client.property file with the new truststore:
# Snippet of client.property file
# Truststore contains the new custom Kafka cluster certificate
ssl.truststore.location=custom-certificate.p12
ssl.truststore.password=passwd
ssl.truststore.type=PKCS12
Now, just run the client script as before. Done!
The source code for this post can be found in my personal GitHub here.
Some extra tips
If you are having trouble to configure the certificates, or receiving errors of missing certificates, and need to troubleshooting the connection to the Kafka cluster, here are some tips to help your overcome it:
First, enable SSL debug, by running the following command before calling the client scripts:
# This will enable SSL debuging.
export KAFKA_OPTS="-Djavax.net.debug=ssl"
If, instead of debug the SSL handshake, you want to debug the script itself, you can enable DEBUG logging by doing 2 things.
First, create a Log4J2 configuration file named client.log4j2.properties with the following content:
rootLogger=DEBUG, console
appender.console.type = Console
appender.console.name = console
Then invoke the following command before calling the scripts:
export KAFKA_LOG4J_OPTS="-Dlog4j2.configurationFile=file:/path/to/client-log4j2.properties"
Top comments (0)