How to Enable Username/Password Authentication (SASL/PLAIN) in Kafka 4.0 with HAProxy SSL Termination

In the previous post, How to Install Kafka 4.0 on a Single Ubuntu 24.04 Node with HAProxy SSL Termination, we set up a Kafka 4.0 server in KRaft mode with SSL termination using HAProxy. However, that setup allowed unauthenticated client access to the Kafka broker.

In this post, we’ll extend that configuration by enabling username and password-based authentication using SASL/PLAIN. This provides a simple but effective mechanism to restrict access to authenticated clients.


Architecture Recap

ComponentAddressProtocolPurpose
Kafka0.0.0.0:9092SASL_PLAINTEXTInternal broker listener
Kafka127.0.0.1:9093CONTROLLERInternal controller listener
HAProxy192.168.0.127:9093SSL (public)TLS termination
Clientkafka.maksonlee.com:9093SASL_SSLAuthenticated + encrypted

  1. Enable SASL/PLAIN on Kafka Broker

Edit your Kafka configuration file at:

sudo vi /opt/kafka/config/server.properties

Apply the following changes:

# 1. Protocol mapping
- listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,SSL:PLAINTEXT
+ listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,SSL:SASL_PLAINTEXT

# 2. SASL mechanism
+ sasl.enabled.mechanisms=PLAIN
+ sasl.mechanism.inter.broker.protocol=PLAIN

# 3. JAAS configuration (inline)
+ listener.name.ssl.plain.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \
+   username="admin" \
+   password="admin-secret" \
+   user_admin="admin-secret" \
+   user_user1="password1";

Explanation:

  • user_admin, user_user1 define valid Kafka client usernames and passwords.
  • The username and password fields are used for internal broker-to-broker communication (still required even in single-node mode).

  1. Restart Kafka

Apply the configuration changes by restarting Kafka:

sudo systemctl restart kafka

Verify that Kafka starts without errors using:

journalctl -u kafka -f

  1. No Changes Needed to HAProxy

Since Kafka still listens internally on SSL://0.0.0.0:9092, and HAProxy terminates TLS and forwards PLAINTEXT, no change is required in /etc/haproxy/haproxy.cfg.

Your Kafka SSL listener now uses SASL/PLAIN internally, while HAProxy continues to expose SSL://kafka.maksonlee.com:9093 externally.


  1. Test with Python (confluent_kafka)

produce_kafka.py

from confluent_kafka import Producer

conf = {
    'bootstrap.servers': 'kafka.maksonlee.com:9093',
    'security.protocol': 'sasl_ssl',
    'sasl.mechanism': 'PLAIN',
    'sasl.username': 'user1',
    'sasl.password': 'password1',
}

producer = Producer(conf)

def delivery_report(err, msg):
    if err:
        print('Delivery failed:', err)
    else:
        print(f'Message delivered to {msg.topic()} [{msg.partition()}]')

producer.produce('test-topic', key='key1', value='Hello Kafka!', callback=delivery_report)
producer.flush()

consume_kafka.py

from confluent_kafka import Consumer

conf = {
    'bootstrap.servers': 'kafka.maksonlee.com:9093',
    'security.protocol': 'sasl_ssl',
    'sasl.mechanism': 'PLAIN',
    'sasl.username': 'user1',
    'sasl.password': 'password1',
    'group.id': 'test-group',
    'auto.offset.reset': 'earliest'
}

consumer = Consumer(conf)
consumer.subscribe(['test-topic'])

print('Waiting for messages...')
msg = consumer.poll(10.0)

if msg is None:
    print("No message received.")
elif msg.error():
    print("Error:", msg.error())
else:
    print(f'Received: {msg.value().decode("utf-8")}')

consumer.close()

Summary

You’ve now added simple username/password authentication to your Kafka 4.0 setup with HAProxy SSL termination. This ensures that only valid clients with credentials can publish or consume messages.

If you want more secure password storage or LDAP integration in the future, consider using SCRAM-SHA-256 or delegating auth to external identity providers.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top