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 broker in KRaft mode with:
- Kafka EXTERNAL listener on 127.0.0.1:9093 (PLAINTEXT)
- Kafka CONTROLLER listener on 127.0.0.1:9094 (PLAINTEXT)
- HAProxy listening on 192.168.0.73:9093 (TLS) and forwarding to 127.0.0.1:9093
That setup used TLS (via HAProxy) but allowed unauthenticated access to Kafka.
In this post, we’ll extend that configuration by enabling username/password authentication using SASL/PLAIN on the Kafka EXTERNAL listener. Clients will connect with SASL_SSL to HAProxy, and Kafka will enforce SASL_PLAINTEXT behind HAProxy.
Architecture Recap
After enabling SASL/PLAIN, the architecture looks like this:
| Component | Address / Host | Protocol | Purpose |
|---|---|---|---|
| Kafka EXTERNAL | 127.0.0.1:9093 | SASL_PLAINTEXT | Broker listener behind HAProxy |
| Kafka CONTROLLER | 127.0.0.1:9094 | PLAINTEXT | KRaft controller listener |
| HAProxy frontend | 192.168.0.73:9093 | SSL (public) | TLS termination, forwards to 127.0.0.1:9093 |
| Client | kafka.maksonlee.com:9093 | SASL_SSL | Authenticated + encrypted client access |
- Clients → HAProxy: TLS + SASL (SASL_SSL)
- HAProxy → Kafka: Plain TCP, but Kafka requires SASL on the EXTERNAL listener (SASL_PLAINTEXT)
- Controller listener stays PLAINTEXT and unauthenticated (internal use only)
- Enable SASL/PLAIN on Kafka Broker
Edit the KRaft server configuration:
sudo vi /opt/kafka/config/kraft/server.properties
Apply the following changes:
# 1. Protocol mapping
- listener.security.protocol.map=EXTERNAL:PLAINTEXT,CONTROLLER:PLAINTEXT
+ listener.security.protocol.map=EXTERNAL:SASL_PLAINTEXT,CONTROLLER:PLAINTEXT
# 2. SASL mechanism
+ sasl.enabled.mechanisms=PLAIN
+ sasl.mechanism.inter.broker.protocol=PLAIN
# 3. JAAS configuration (inline)
+ listener.name.external.plain.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \
+ username="admin" \
+ password="admin-secret" \
+ user_admin="admin-secret" \
+ user_user1="password1";
That’s all you need to add/change for SASL/PLAIN.
- Restart Kafka
Apply the configuration changes by restarting Kafka:
sudo systemctl restart kafka
Verify that Kafka starts without errors using:
journalctl -u kafka -f
- No Changes Needed to HAProxy
You do not need to touch /etc/haproxy/haproxy.cfg.
From HAProxy’s perspective, nothing changed:
- It still terminates TLS on 192.168.0.73:9093
- It still forwards raw TCP to 127.0.0.1:9093
From Kafka’s perspective:
- Incoming connections on EXTERNAL must now authenticate with SASL/PLAIN.
- The connection between HAProxy and Kafka is still unencrypted (loopback), but SASL_PLAINTEXT is enforced.
Clients must now:
- Use
security.protocol=sasl_ssl - Provide the correct SASL username/password
- 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.
Did this guide save you time?
Support this site