blog by OSO

How to secure your Apache Kafka cluster

Sion Smith
Sion Smith 17 January 2025
OSO guide to securing an Apache Kafka cluster with SASL, mTLS and OAuth authentication, with white title text on a navy panel and a lime Blogs label.

Securing a Kafka cluster comes down to two questions. Authentication confirms who a client or broker is. Authorisation decides what that identity is allowed to do once it is connected. Think of a restaurant: you show your ID to get through the door (authentication), and the host checks your reservation before seating you in the VIP area (authorisation). Kafka needs both, plus encryption in transit, before it is safe to expose to real workloads.

This guide covers each authentication option Kafka supports, how to configure mTLS and SASL/SCRAM, where OAuth fits, how to point the Kafka command-line tools at a secured cluster, and how to lock everything down with ACLs. Every configuration example is written for Apache Kafka 4.3 running in KRaft mode; property names can differ in older releases, so check the security documentation for your version.

How does authentication work in Kafka?

Kafka applies security per listener. Each listener is a network endpoint on a broker or controller, and listener.security.protocol.map assigns it one of four security protocols:

Security protocolEncryption in transitClient authentication
PLAINTEXTNoneNone
SSLTLSOptional TLS client certificates (mTLS)
SASL_PLAINTEXTNoneSASL mechanism
SASL_SSLTLSSASL mechanism

SASL (Simple Authentication and Security Layer) is a pluggable framework, so a SASL listener can enable one or more mechanisms through sasl.enabled.mechanisms: GSSAPI (Kerberos), PLAIN, SCRAM-SHA-256, SCRAM-SHA-512 and OAUTHBEARER. Clients choose the matching security.protocol and sasl.mechanism.

Whatever mechanism you use, a successful handshake produces a Kafka principal, such as User:alice. That principal is what ACLs are written against. Most production clusters use explicitly named listeners, for example a CLIENT listener on SASL_SSL for applications, a separate listener for broker-to-broker traffic (inter.broker.listener.name) and the controller listener named in controller.listener.names for KRaft quorum traffic. Brokers authenticate to each other and to the controllers too, so secure those listeners as well.

listeners=CLIENT://:9092,BROKER://:9093
listener.security.protocol.map=CLIENT:SASL_SSL,BROKER:SSL,CONTROLLER:SSL
inter.broker.listener.name=BROKER
sasl.enabled.mechanisms=SCRAM-SHA-512

One limitation is worth knowing early: Kafka has no native support for certificate revocation lists (KAFKA-3700). To revoke access you rely on JVM-level mechanisms, short certificate lifetimes, or removing the principal’s ACLs.

Which authentication mechanism should you use?

The right mechanism depends on the identity systems you already run. The comparison below is a relative assessment from our production work, not a Kafka guarantee; your certificate tooling or identity provider can shift it.

MechanismSet-up effortCredential rotationIdentity-provider fitUse it when
mTLS (SSL)Medium: you need a CA and per-client certificatesReissue certificates before they expire; no native revocationPKI or a service meshYou already manage certificates, or want encryption and identity in one step
SASL/SCRAMLow to mediumChange passwords with kafka-configs; new connections pick them upKafka stores credentials in cluster metadataYou want username and password auth without external systems
SASL/OAUTHBEARERMedium to highShort-lived tokens issued by your identity providerOkta, Entra ID, Keycloak and other OIDC providersYou already run a central identity provider
SASL/GSSAPI (Kerberos)HighTickets expire and renew; revoke by disabling the principalActive Directory or an MIT KDCYour enterprise already standardises on Kerberos
SASL/PLAINLowDepends on the callback handler or credential store you plug inCustom callback handlersYou need to delegate password checks to an external store, always over TLS

A few practical notes from running these in production:

  • mTLS gives you encryption and authentication together. Forgetting to renew certificates is the classic outage, so automate issuance and alert on expiry.
  • SCRAM never sends the password over the wire and uses a salted challenge-response with a random nonce, which protects against replayed authentication messages. Use it only on SASL_SSL listeners.
  • Kerberos gives fine-grained control over expiry and revocation, but troubleshooting is harder, and exposing a Kerberos-secured cluster to the internet means exposing the KDC as well. Avoid that when Active Directory is your KDC.
  • PLAIN sends the password itself, protected only by TLS. Never enable it on a SASL_PLAINTEXT listener.

Key takeaway: Start with mTLS or SCRAM over TLS, use OAuth when you already run a central identity provider, never run PLAIN without TLS, and authorise every principal with ACLs.

How do you configure mTLS for Kafka?

mTLS means the broker verifies a certificate from every client as well as presenting its own. On the broker, enable an SSL listener and require client certificates (Apache Kafka 4.3):

listeners=CLIENT://:9094
listener.security.protocol.map=CLIENT:SSL
ssl.keystore.location=/var/private/ssl/kafka.server.keystore.jks
ssl.keystore.password=<keystore-password>
ssl.key.password=<key-password>
ssl.truststore.location=/var/private/ssl/kafka.server.truststore.jks
ssl.truststore.password=<truststore-password>
ssl.client.auth=required

When ssl.client.auth is required (or requested), the broker truststore must contain every CA that signs client certificates. Each client then needs its own keystore plus a truststore holding the broker CA:

security.protocol=SSL
ssl.keystore.location=/var/private/ssl/client.keystore.jks
ssl.keystore.password=<keystore-password>
ssl.key.password=<key-password>
ssl.truststore.location=/var/private/ssl/client.truststore.jks
ssl.truststore.password=<truststore-password>

Hostname verification is on by default for client and inter-broker connections. Leave ssl.endpoint.identification.algorithm at its default rather than setting it to an empty string to “just get it working”.

By default, a certificate principal is the full distinguished name, for example CN=producer,OU=Unknown,O=Unknown,L=Unknown,ST=Unknown,C=Unknown. ssl.principal.mapping.rules maps that to a short name. Rules are evaluated in order and the first match wins:

ssl.principal.mapping.rules=RULE:^CN=(.*?),OU=ServiceUsers.*$/$1/,RULE:^CN=(.*?),OU=(.*?),O=(.*?),L=(.*?),ST=(.*?),C=(.*?)$/$1@$2/,RULE:^cn=(.*?),ou=(.*?),dc=(.*?),dc=(.*?)$/$1@$2/L,RULE:^.*[Cc][Nn]=([a-zA-Z0-9.]*).*$/$1/L,DEFAULT

With the first rule, a certificate for CN=producer,OU=ServiceUsers,... maps to the principal User:producer. For anything a rule cannot express, set a custom principal.builder.class. Plan certificate rotation up front: Kafka can reload updated keystores through dynamic broker configuration with kafka-configs, or you can roll brokers one at a time, so document which method you use.

How do you configure SASL/SCRAM?

In KRaft mode, SCRAM credentials are stored in the cluster metadata log, not in ZooKeeper. Credentials that brokers use to talk to each other must exist before the brokers start, so add them when you format storage (Apache Kafka 4.3):

bin/kafka-storage.sh format -t $(bin/kafka-storage.sh random-uuid) \
  -c config/server.properties \
  --add-scram 'SCRAM-SHA-512=[name="admin",password="admin-secret"]'

Then configure a SASL_SSL listener with the SCRAM login module:

listeners=CLIENT://:9092
listener.security.protocol.map=CLIENT:SASL_SSL
sasl.enabled.mechanisms=SCRAM-SHA-512
listener.name.client.scram-sha-512.sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required;

Create application users once the cluster is running. Updated credentials apply to new connections without a restart:

bin/kafka-configs.sh --bootstrap-server broker1:9092 --command-config admin.properties \
  --alter --add-config 'SCRAM-SHA-512=[iterations=8192,password=alice-secret]' \
  --entity-type users --entity-name alice

Clients connect like this:

security.protocol=SASL_SSL
sasl.mechanism=SCRAM-SHA-512
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="alice" password="alice-secret";
ssl.truststore.location=/var/private/ssl/client.truststore.jks
ssl.truststore.password=<truststore-password>

Run SCRAM only over SASL_SSL. On SASL_PLAINTEXT the handshake is protected but every message afterwards travels unencrypted.

How does OAuth (OAUTHBEARER) work with Kafka?

OAUTHBEARER lets clients authenticate with OAuth 2.0 bearer tokens. Kafka’s default OAUTHBEARER implementation creates and validates unsecured JSON Web Tokens, and the Kafka documentation says it is only suitable for non-production use. For production, KIP-768 (Kafka 3.1 onwards) added built-in OIDC support: clients fetch tokens from your identity provider, and brokers validate them against its JWKS endpoint.

A client using the client-credentials grant looks like this (Apache Kafka 4.3):

security.protocol=SASL_SSL
sasl.mechanism=OAUTHBEARER
sasl.jaas.config=org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required ;
sasl.oauthbearer.token.endpoint.url=https://idp.example.com/oauth2/token
sasl.oauthbearer.client.credentials.client.id=orders-service
sasl.oauthbearer.client.credentials.client.secret=<client-secret>
sasl.oauthbearer.scope=kafka

On the broker, set OAuthBearerValidatorCallbackHandler as the listener’s server callback handler and point sasl.oauthbearer.jwks.endpoint.url at your provider, ideally with sasl.oauthbearer.expected.issuer and sasl.oauthbearer.expected.audience set. OAuth property names have changed between releases, so match them to your Kafka version. For a full walkthrough with an identity provider, read our guide to Apache Kafka security using OAuth.

How do you use Kafka command-line tools with SASL and SSL clusters?

The Kafka CLI tools are ordinary Kafka clients, so they need the same security settings as your applications. Put them in a properties file and pass it to each tool. For a SCRAM cluster, client.properties looks like this:

security.protocol=SASL_SSL
sasl.mechanism=SCRAM-SHA-512
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="admin" password="admin-secret";
ssl.truststore.location=/var/private/ssl/client.truststore.jks
ssl.truststore.password=<truststore-password>

For an mTLS cluster, use the SSL client settings from the mTLS section instead. Admin tools take the file with --command-config:

bin/kafka-topics.sh --bootstrap-server broker1:9092 --command-config client.properties --list
bin/kafka-acls.sh --bootstrap-server broker1:9092 --command-config client.properties --list
bin/kafka-configs.sh --bootstrap-server broker1:9092 --command-config client.properties \
  --describe --entity-type users --entity-name alice

The console producer and consumer use their own flags:

bin/kafka-console-producer.sh --bootstrap-server broker1:9092 --topic orders \
  --producer.config client.properties
bin/kafka-console-consumer.sh --bootstrap-server broker1:9092 --topic orders \
  --consumer.config client.properties --from-beginning

If a tool hangs or reports disconnections, the usual causes are a wrong security.protocol for that port, a truststore missing the broker CA, or a principal with no ACLs on the resource. Keep these files readable only by their owner, because they contain credentials.

How do you authorise clients with ACLs?

Authentication proves identity; ACLs decide what each principal can do. In KRaft clusters, enable the built-in authoriser on every broker and controller:

authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer
super.users=User:admin;User:broker

An ACL reads as: principal P is allowed or denied operation O from host H on resources matching pattern R. Operations include Read, Write, Create, Delete, Alter and Describe; resources include topics, consumer groups, the cluster and transactional IDs. Once an authoriser is set, any principal without a matching allow ACL is denied, unless you set allow.everyone.if.no.acl.found=true, which we do not recommend. Deny rules take precedence over allow rules.

Make broker and admin principals super users so they do not need individual ACLs. super.users is separated by semicolons because certificate names contain commas. Then grant applications only what they need:

bin/kafka-acls.sh --bootstrap-server broker1:9092 --command-config client.properties \
  --add --allow-principal User:orders-service --operation Write --topic orders

bin/kafka-acls.sh --bootstrap-server broker1:9092 --command-config client.properties \
  --add --allow-principal User:billing --operation Read --topic orders --group billing

If you use full certificate names as principals, quote the whole principal: --allow-principal "User:CN=producer,OU=OSO,O=Kafka,L=London,ST=UK,C=UK". Mapping certificates to short names with ssl.principal.mapping.rules keeps ACLs far easier to read. Avoid IP-based identification on its own: it is not secure.

What about Schema Registry and Kafka Connect?

Kafka client authentication secures the broker protocol only. It does not secure HTTP endpoints such as the Kafka Connect REST API or Schema Registry. Anyone who can reach an unsecured Connect REST port can create connectors that read or write your data, using the worker’s own Kafka credentials.

Secure each REST layer separately with TLS and either basic authentication or mTLS, restrict network access to those ports, and give Connect workers and Schema Registry their own Kafka principals with ACLs scoped to their internal topics. For the schema side, see our guide to managing schemas in Kafka.

If you want working examples of these set-ups, our confluent-kubernetes-playground repository has them. If you would rather have engineers who run secured clusters every day do it with you, see our Kafka support and Kafka services.

Frequently asked questions

What authentication does Kafka support?

Kafka supports TLS client certificates (mTLS) and SASL with five mechanisms: GSSAPI (Kerberos), PLAIN, SCRAM-SHA-256, SCRAM-SHA-512 and OAUTHBEARER. You enable them per listener, and each listener can allow more than one SASL mechanism.

What is the difference between SSL and SASL_SSL in Kafka?

SSL encrypts the connection with TLS and can authenticate clients with certificates. SASL_SSL also encrypts with TLS, but authenticates clients with a SASL mechanism such as SCRAM or OAUTHBEARER, so clients need a username and password or a token instead of their own certificate.

How do I enable mTLS in Kafka?

Enable an SSL listener with a keystore and truststore on each broker, then set ssl.client.auth=required. Give every client its own certificate signed by a CA the broker trusts, and use ssl.principal.mapping.rules to turn certificate names into short principals for ACLs.

Does Kafka support OAuth?

Yes. Kafka supports OAuth 2.0 through the SASL/OAUTHBEARER mechanism, with built-in OIDC support since Kafka 3.1 (KIP-768). The default unsecured-token implementation is for development only; production clusters should validate tokens from a real identity provider.

How do I use Kafka CLI tools against a SASL_SSL cluster?

Put security.protocol, sasl.mechanism, sasl.jaas.config and your truststore settings in a properties file. Pass it with --command-config to admin tools such as kafka-topics.sh and kafka-acls.sh, and with --producer.config or --consumer.config to the console clients.

Do I need Kerberos for Kafka?

No. Kerberos (SASL/GSSAPI) is only worth it when your organisation already runs it, for example through Active Directory. Most teams get strong security with less effort from mTLS or SCRAM over TLS, or from OAuth if they already have an identity provider.

Rolling out Kafka authentication?

OSO engineers run Apache Kafka in production for banks, retailers and the public sector. Tell us what you are weighing up and we will give you a straight answer.

CONTACT US
OSO
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.