KubeMQ
ConnectorsKafkaHow-to guides

TLS and mTLS

Secure the Kafka connector with TLS — the 9093 encrypted listener, server certificates, and mutual TLS where the certificate common name is the principal.

The Kafka connector opens two listeners: 9092 (plaintext) and 9093 (TLS). This guide covers the TLS listener — server-authentication TLS, mutual TLS, and how the client certificate's common name becomes the authenticated principal when SASL isn't used.

Port stays required, even if you only want TLS. A TLS-only configuration — TlsPort set, Port left empty — is rejected: the TLS accept path isn't wired independently of the plaintext one yet, so Port must stay set (and differ from TlsPort). Restrict access to the plaintext listener at your network layer (firewall rules, a ClusterIP-only Service) instead of by unsetting Port.

The 9093 TLS listener

The TLS listener has no certificate configuration of its own — it reuses the server's global Security block (Cert/Key/Ca), the same block every other TCP connector (gRPC, REST, STOMP, AMQP, RabbitMQ) shares. There is no Connectors.Kafka.Tls*Cert field to set.

SettingEnv varDefaultNotes
Plaintext portCONNECTORS_KAFKA_PORT9092Must stay set — see the callout above.
TLS portCONNECTORS_KAFKA_TLS_PORT9093Set to "" (empty) to disable the TLS listener; otherwise must be 1–65535.
Server certSECURITY_CERT_DATA / SECURITY_CERT_FILENAME""Required for TLS and mTLS.
Server keySECURITY_KEY_DATA / SECURITY_KEY_FILENAME""Required for TLS and mTLS.
Client CASECURITY_CA_DATA / SECURITY_CA_FILENAME""Presence promotes the mode to mTLS — see below.

The mode is auto-derived, not a flag: Cert + Key alone is server-authentication TLS; adding Ca promotes it to mTLS. See Security → TLS / mTLS for the full field reference and Docker/Helm examples.

If you expose 9093 externally, the certificate must cover AdvertisedHost. The connector serves one certificate with no per-SNI selection — a certificate whose SAN covers only the in-cluster Service DNS fails TLS hostname verification for an external client connecting through the advertised address.

Server-authentication TLS

With Cert + Key set (no Ca), the server presents its certificate and the minimum negotiated protocol is TLS 1.2; no client certificate is requested. The client authenticates separately — with SASL/PLAIN, SCRAM, or OAUTHBEARER over the now-encrypted channel — see Authentication.

kcat -b localhost:9093 -L \
  -X security.protocol=SSL \
  -X ssl.ca.location=/path/to/ca.pem
import (
    "crypto/tls"
    "crypto/x509"
    "os"

    "github.com/twmb/franz-go/pkg/kgo"
)

caPEM, _ := os.ReadFile("/path/to/ca.pem")
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(caPEM)

cl, err := kgo.NewClient(
    kgo.SeedBrokers("localhost:9093"),
    kgo.DialTLSConfig(&tls.Config{RootCAs: pool}),
)
security.protocol=SSL
ssl.truststore.location=/path/to/truststore.jks
ssl.truststore.password=changeit

Mutual TLS — certificate identity

Adding Ca to the Security block promotes the listener to mTLS: the server now requires and verifies a client certificate (RequireAndVerifyClientCert) using that CA pool, on every connection to 9093. A client that fails the chain check never completes the handshake.

When the connection carries no SASL layer, the connector reads the verified certificate's leaf Subject.CommonName as the authenticated principal — the same identity the Casbin ACL then authorizes against (see Authentication). An empty CN, or a certificate whose chain didn't verify, never becomes a principal.

kcat -b localhost:9093 -L \
  -X security.protocol=SSL \
  -X ssl.ca.location=/path/to/ca.pem \
  -X ssl.key.location=/path/to/client.key \
  -X ssl.certificate.location=/path/to/client.crt
clientCert, _ := tls.LoadX509KeyPair("/path/to/client.crt", "/path/to/client.key")

cl, err := kgo.NewClient(
    kgo.SeedBrokers("localhost:9093"),
    kgo.DialTLSConfig(&tls.Config{
        RootCAs:      pool, // the CA that signed the SERVER certificate
        Certificates: []tls.Certificate{clientCert},
    }),
)
security.protocol=SSL
ssl.truststore.location=/path/to/truststore.jks
ssl.truststore.password=changeit
ssl.keystore.location=/path/to/client-keystore.jks
ssl.keystore.password=changeit
ssl.key.password=changeit

mTLS and SASL don't stack. Only one identity source wins per connection: the certificate CN is used only when SASL isn't required. If the listener also has Credentials configured, the SASL identity takes precedence and the CN is never consulted — see Authentication.

Was this page helpful?

On this page