DEV Community

Ângelo Galvão
Ângelo Galvão

Posted on

Strimzi: Create a simple Mutual TLS (mTLS) authentication


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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Verify the creation of Secret with the same name of the created KafkaUser:

oc describe secret test-kafka-user
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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=
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then, upload it to Kafka cluster:

oc create secret tls custom-certificate --cert=custom-cert.crt --key=custom-key.key
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then invoke the following command before calling the scripts:

export KAFKA_LOG4J_OPTS="-Dlog4j2.configurationFile=file:/path/to/client-log4j2.properties"  
Enter fullscreen mode Exit fullscreen mode

Top comments (0)