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
Component | Address | Protocol | Purpose |
---|---|---|---|
Kafka | 0.0.0.0:9092 | SASL_PLAINTEXT | Internal broker listener |
Kafka | 127.0.0.1:9093 | CONTROLLER | Internal controller listener |
HAProxy | 192.168.0.127:9093 | SSL (public) | TLS termination |
Client | kafka.maksonlee.com:9093 | SASL_SSL | Authenticated + encrypted |
- 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
andpassword
fields are used for internal broker-to-broker communication (still required even in single-node mode).
- 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
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.
- 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.