Kafka · lesson 9 of 12
Also part of: Security Fundamentals
Kafka Security: SASL Authentication and a Real ACL Denial
A default Kafka cluster trusts every connection completely. Turn on SASL/PLAIN and an ACL authorizer, and watch an unauthorized client fail with the exact exception Kafka throws — for a topic, then for a consumer group.
By SkillByExample · Published
A default Kafka broker authenticates nobody and authorizes everything — any TCP connection can produce, consume, or delete any topic. SASL adds authentication; an authorizer adds per-resource permission checks on top of it.
Starting a broker with SASL and an authorizer
This needs three broker settings beyond a plain cluster: a JAAS login config naming valid
users, SASL_PLAINTEXT as the client listener’s protocol, and an authorizer class:
# server.properties (relevant lines)
listeners=SASL_PLAINTEXT://:9092,CONTROLLER://:9093
sasl.enabled.mechanisms=PLAIN
sasl.mechanism.inter.broker.protocol=PLAIN
authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer
super.users=User:admin
# jaas.conf — passed via KAFKA_OPTS=-Djava.security.auth.login.config=...
KafkaServer {
org.apache.kafka.common.security.plain.PlainLoginModule required
username="admin"
password="admin-secret"
user_admin="admin-secret"
user_alice="alice-secret";
};
Two users exist: admin (a designated super user, exempt from every ACL check) and alice
(an ordinary authenticated principal with no permissions until granted some).
admin bypasses every check
bin/kafka-topics.sh --bootstrap-server localhost:9092 \
--command-config admin.properties \
--create --topic secure-orders --partitions 1 --replication-factor 1
Created topic secure-orders.
No ACL exists yet for this topic at all — admin doesn’t need one, because super.users
exempts it from authorization entirely.
alice is denied — a real TopicAuthorizationException
echo "hello from alice" | bin/kafka-console-producer.sh --bootstrap-server localhost:9092 \
--producer.config alice.properties --topic secure-orders
[...] ERROR [Producer clientId=console-producer] Topic authorization failed for topics [secure-orders] (org.apache.kafka.clients.Metadata)
org.apache.kafka.common.errors.TopicAuthorizationException: Not authorized to access topics: [secure-orders]
alice authenticated successfully — SASL isn’t the failure here. She’s a known, valid user
with zero grants on secure-orders, and the authorizer refuses the produce outright.
Granting a Write ACL
bin/kafka-acls.sh --bootstrap-server localhost:9092 --command-config admin.properties \
--add --allow-principal User:alice --operation Write --operation Describe \
--topic secure-orders
Adding ACLs for resource `ResourcePattern(resourceType=TOPIC, name=secure-orders, patternType=LITERAL)`:
(principal=User:alice, host=*, operation=DESCRIBE, permissionType=ALLOW)
(principal=User:alice, host=*, operation=WRITE, permissionType=ALLOW)
Describe is needed alongside Write — a producer fetches topic metadata (partition count,
leader) before it can send, and that’s a separate checked operation from the write itself.
echo "hello from alice" | bin/kafka-console-producer.sh --bootstrap-server localhost:9092 \
--producer.config alice.properties --topic secure-orders
No error — the produce succeeds silently. Confirmed by having admin (super user, unaffected
by any of alice’s grants) read it back:
$ bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --consumer.config admin.properties --topic secure-orders --from-beginning --timeout-ms 6000 --group admin-verify-group
hello from alice
Processed a total of 1 messages
Consuming needs a second, independent ACL check
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 \
--consumer.config alice.properties --topic secure-orders --from-beginning \
--timeout-ms 6000 --group alice-group
ERROR Error processing message, terminating consumer process: (org.apache.kafka.tools.consumer.ConsoleConsumer)
org.apache.kafka.common.errors.GroupAuthorizationException: Not authorized to access group: alice-group
Processed a total of 0 messages
Different exception, different resource: alice has Write/Describe on the topic, but
consuming also requires Read on the consumer group she’s joining — a completely separate
ACL that doesn’t exist yet, so Kafka names precisely which check failed.
bin/kafka-acls.sh --bootstrap-server localhost:9092 --command-config admin.properties \
--add --allow-principal User:alice --operation Read --topic secure-orders
bin/kafka-acls.sh --bootstrap-server localhost:9092 --command-config admin.properties \
--add --allow-principal User:alice --operation Read --group alice-group
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 \
--consumer.config alice.properties --topic secure-orders --from-beginning \
--timeout-ms 8000 --group alice-group
hello from alice
[...] org.apache.kafka.common.errors.TimeoutException
Processed a total of 1 messages
Both the topic-Read and group-Read grants in place, and the same message finally reaches
alice’s own consumer. The trailing TimeoutException is the console consumer’s normal exit
after its idle timeout, not a failure — Processed a total of 1 messages confirms it worked.
Listing what’s actually granted
bin/kafka-acls.sh --bootstrap-server localhost:9092 --command-config admin.properties \
--list --topic secure-orders
Current ACLs for resource `ResourcePattern(resourceType=TOPIC, name=secure-orders, patternType=LITERAL)`:
(principal=User:alice, host=*, operation=WRITE, permissionType=ALLOW)
(principal=User:alice, host=*, operation=DESCRIBE, permissionType=ALLOW)
(principal=User:alice, host=*, operation=READ, permissionType=ALLOW)
Three separate grants, accumulated one --add at a time — kafka-acls.sh has no bundled
“read-write” shortcut; every operation is its own explicit rule.
Practice
1. Try creating a topic as alice, who has no Create ACL on the cluster.
bin/kafka-topics.sh --bootstrap-server localhost:9092 --command-config alice.properties \
--create --topic alice-topic --partitions 1 --replication-factor 1
Error while executing topic command : Authorization failed.
[...] ERROR org.apache.kafka.common.errors.TopicAuthorizationException: Authorization failed.
Creating a topic checks a Create permission alice was never granted — same exception
class as the produce failure, different underlying operation.
2. Remove alice's Write ACL and try producing again.
bin/kafka-acls.sh --bootstrap-server localhost:9092 --command-config admin.properties \
--remove --allow-principal User:alice --operation Write --topic secure-orders --force
echo "second try" | bin/kafka-console-producer.sh --bootstrap-server localhost:9092 \
--producer.config alice.properties --topic secure-orders
[...] ERROR [Producer clientId=console-producer] Aborting producer batches due to fatal error (org.apache.kafka.clients.producer.internals.Sender)
org.apache.kafka.common.errors.ClusterAuthorizationException: Cluster authorization failed.
Not TopicAuthorizationException this time — ClusterAuthorizationException. The modern
console producer defaults to idempotent delivery, which needs a cluster-level permission
before it even gets to the per-topic Write check; losing Write trips that broader
cluster-level gate first. alice still keeps Read/Describe on the topic — only the
produce path is affected.
3. What happens to a client with no SASL config at all, pointed at the SASL_PLAINTEXT listener?
bin/kafka-topics.sh --bootstrap-server localhost:9092 --list
Error while executing topic command : Timed out waiting for a node assignment. Call: listTopics
[...] ERROR org.apache.kafka.common.errors.TimeoutException: Timed out waiting for a node assignment. Call: listTopics
No authorization exception at all — the client can’t even complete the SASL handshake the
broker expects, so it just hangs until its own request timeout gives up. There’s no anonymous
fallback identity on a SASL_PLAINTEXT listener the way there is on plain PLAINTEXT; a
missing SASL config looks like the cluster is unreachable, not like a permissions error.
4. Grant alice a prefixed ACL instead of a literal one — --resource-pattern-type prefixed --topic secure-.
bin/kafka-acls.sh --bootstrap-server localhost:9092 --command-config admin.properties \
--add --allow-principal User:alice --operation Read --operation Write \
--topic secure- --resource-pattern-type prefixed
Adding ACLs for resource `ResourcePattern(resourceType=TOPIC, name=secure-, patternType=PREFIXED)`:
(principal=User:alice, host=*, operation=READ, permissionType=ALLOW)
(principal=User:alice, host=*, operation=WRITE, permissionType=ALLOW)
patternType=PREFIXED instead of LITERAL — this single grant matches every topic name
starting with secure- (secure-orders, secure-payments, any future topic in that
namespace) without a new grant per topic. Every ACL earlier in this lesson was LITERAL,
matching one exact name only.
Next: how records survive broker restarts and disk failures — replication, ISR, and what retention actually deletes.