Kafka ACLs

Access Control Lists are how Kafka decides who's allowed to do what. They work, they're native, and they scale badly. Here's how to configure them, the defaults that catch most teams out, and when it's time to layer RBAC on top.

What a Kafka ACL actually is

An ACL (Access Control List) in Kafka is an allow rule. Each entry maps a principal (like User:alice) to an operation (Read, Write, Create, Describe, Alter) on a specific resource (Topic, ConsumerGroup, Cluster, TransactionalId). If a request doesn't match at least one allow rule, the broker denies it.

ACLs live inside the Kafka cluster itself. They're stored in KRaft (or ZooKeeper on older deployments) and enforced by the broker on every request.

Turning ACLs on

ACLs are off by default. Without them, every authenticated principal can do anything. Turn them on in broker.properties before connecting any clients you actually care about:

# broker.properties
authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer
super.users=User:admin
allow.everyone.if.no.acl.found=false

The most common mistake here is leaving allow.everyone.if.no.acl.found=true. That flag makes Kafka fall back to "allow" when no ACL exists, which defeats the whole point. Set it to false so the broker denies any request that isn't explicitly permitted.

Adding ACLs with the CLI

Kafka ships with kafka-acls.sh for adding, listing, and removing rules:

# Grant alice read access to a topic
bin/kafka-acls.sh --bootstrap-server localhost:9092 \
  --add --allow-principal User:alice \
  --operation Read --topic customer-events

# List all ACLs on a cluster
bin/kafka-acls.sh --bootstrap-server localhost:9092 --list

# Remove an ACL
bin/kafka-acls.sh --bootstrap-server localhost:9092 \
  --remove --allow-principal User:alice \
  --operation Read --topic customer-events

Each grant is per-principal, per-operation, per-resource. No groups, no role inheritance, no bulk patterns beyond wildcard topic matching.

Where ACLs stop working

For small clusters with a handful of users, ACLs are fine. Past that, four things start to hurt:

Most teams start with raw ACLs, scale past the point where they're manageable, and end up layering RBAC on top. The ACLs stay. They're still the enforcement layer at the broker. Something else just manages them.

Managing Kafka ACLs at scale

The four limits above all start to matter at roughly the same scale. Most teams don't notice how fast the entries multiply: 20 teams, 500 topics, three permissions per topic per team is 30,000 ACL entries before you count service accounts, consumer groups, or non-prod environments. Each one is a separate ACL binding to create, review, and remove. Each cluster gets its own copy.

In practice, teams hit three failure modes:

  1. The CLI script that becomes a load-bearing artefact. Someone writes a bash wrapper around kafka-acls.sh that iterates over a YAML file of grants. It works. Then it lives in a Git repo nobody owns, runs from one engineer's laptop, and drifts from production whenever a manual ACL is added during an incident.
  2. The audit question with no answer. When a compliance reviewer asks "who has produced to payments-events in the last 90 days, and who granted them access?", kafka-acls.sh --list answers half the first part and none of the second. Broker logs are not an audit trail.
  3. Off-boarding, principal by principal. A developer leaves the team. Revoking their access means finding every principal they own, across every cluster, and running a --remove for each. Until someone finishes that sweep, their credentials still authorize requests at the broker.

What scale looks like

The fix has the same shape regardless of which platform you use to deliver it. Grants are expressed against groups or roles, not individual principals. Changes go through a reviewable, audited interface, not ad-hoc CLI on a laptop. And the same definition applies across every cluster, not one config per environment.

Conduktor delivers this as a layer on top of native Kafka ACLs — the broker still enforces ACLs the way it always has; Conduktor manages them:

# payments-developers.yaml — grants the "payments-developers" group
# read on any topic with the "payments-" prefix, across every connected
# cluster, with an audit entry.
apiVersion: v2
kind: Group
metadata:
  name: payments-developers
spec:
  displayName: Payments Developers
  externalGroups:
    - payments-developers
  permissions:
    - resourceType: TOPIC
      cluster: "*"
      name: payments-
      patternType: PREFIXED
      permissions:
        - topicConsume

Applied through the Conduktor CLI:

conduktor apply -f payments-developers.yaml

A single declarative resource replaces dozens of kafka-acls.sh bindings. The same definition lives in Git, runs in CI, and is also available as a Terraform provider.

kafka-acls.sh vs Conduktor vs Confluent RBAC

Concernkafka-acls.shConduktorConfluent RBAC
GranularityPer-principal, per-resourceGroup / role with pattern matchingRole-based, Confluent-only
Multi-clusterOne invocation per clusterSingle command across every connected clusterConfluent Platform / Cloud only
Change auditNone natively (Log4j only)Audit history of direct changes; request/approval metadata for self-service accessConfluent audit logs
Off-boardingManual --remove per principalRevoke at the group / IdP level, all permissions followTied to Confluent identity
Works against any KafkaYesYes (MSK, Confluent, Redpanda, Aiven, self-managed)No — Confluent only
Self-serviceNoneTopic / access requests with approval workflowLimited
Conduktor does not replace the broker's ACL enforcement; it replaces the spreadsheet and the bash wrapper around it. Underneath, the broker still sees standard Kafka ACLs.

ACLs vs RBAC

ACLs and RBAC aren't competing technologies; they stack. RBAC handles the who and what role at the identity layer (users, groups, roles), then translates those assignments into the ACLs the broker actually enforces. You get the usability of roles and groups, and the broker still enforces the low-level rules it's always enforced.

For the full picture (how ACLs fit alongside encryption, authentication, and auditing), read the Kafka Security Guide or the deeper Apache Kafka Security Playbook. For the RBAC side specifically, see Kafka RBAC.

Frequently asked questions

Are Kafka ACLs enabled by default?

No. Out of the box, Kafka has no authorizer configured, so any authenticated principal can do anything. Set authorizer.class.name in broker.properties to turn them on.

What happens if I turn on ACLs without defining any?

With allow.everyone.if.no.acl.found=true (the old default), the broker allows everything. That defeats the point. With allow.everyone.if.no.acl.found=false, the broker denies everything, including your own admin user, so add a super.users entry first.

Can I use ACLs without authentication?

Technically yes, but it's pointless. The broker can't enforce a per-principal rule if no principal has been authenticated. ACLs assume TLS, SASL, or mTLS is already in place.

How do I audit ACL changes?

Kafka's Log4j authorizer logger captures ACL updates when configured, but there's no native history or query UI. Ship the log output to a SIEM (Splunk, ELK, Datadog) for searchable history.

Do ACLs apply to consumer groups?

Yes. Consumer group operations (join, leave, commit offsets) are a separate resource type. Read access to a topic doesn't include consumer-group access; both need explicit ACLs.

I have more questions.

Drop us a line and we'll get back to you.

Going beyond ACLs?

See how the Conduktor Kafka governance platform layers RBAC, audit trails, and cross-cluster ACL management on top of your existing brokers, without replacing Kafka's native enforcement.

Book a Demo Read the Security Guide →