Skip to content

Kafka ACL

Kafka
Chad Harris·August 29, 2026·6 min read

A Kafka ACL is an access control rule that allows or denies a principal an operation on a resource from a host. The general format reads: Principal P is allowed or denied Operation O from Host H on any Resource R matching ResourcePattern RP. ACLs are managed with kafka-acls.sh and enforced by the broker’s authorizer. Where ACLs sit in the wider operational picture is mapped in the complete Kafka guide.

Our position, stated in our security-architecture guide and earned in support threads: native ACLs become difficult to manage at scale, and the practical failure mode is permission creep, where ACLs accumulate through broad grants and principals that never get revoked. The syntax below is the easy part. The real problem this page is about is keeping the rule set legible after three years and forty teams.

Syntax and quick reference

The authorizer is enabled in server.properties. On KRaft clusters the configuration goes on every node, brokers and controllers:

one ACL entry User:analytics Read * Topic:orders.* PREFIXED Principal Operation Host Resource Pattern type Any matching DENY beats every ALLOW; no match at all is a deny by default
Screenshot to come

kafka-acls.sh --list output on a real cluster, and the same rules in an ACL management screen.

authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer

kafka-acls.sh takes an action (--add, --remove, --list), a connection (--bootstrap-server, or --bootstrap-controller for controller-hosted ACLs), a principal (--allow-principal or --deny-principal, in PrincipalType:name format, and the default type string User is case sensitive), a resource (--topic, --group, --cluster, --transactional-id), and one or more --operation flags.

The valid operations are: Read, Write, Create, Delete, Alter, Describe, ClusterAction, DescribeConfigs, AlterConfigs, IdempotentWrite, CreateTokens, DescribeTokens, All.

Resource patterns come in three types via --resource-pattern-type, default literal. A literal pattern names one resource exactly. The wildcard resource --topic '*' matches all topics. A prefixed pattern matches every resource under a name prefix, which is the mechanism that makes ACLs manageable at scale.

Two defaults worth knowing before writing any rule. A resource with no matching ACL is denied to everyone except super users, unless allow.everyone.if.no.acl.found=true is set. And super users bypass the authorizer entirely, configured with a semicolon delimiter because SSL distinguished names contain commas: super.users=User:Bob;User:Alice.

Copy-paste commands for common scenarios

Allow a service to produce to one topic. The --producer convenience flag expands to Write, Describe and Create on the topic:

bin/kafka-acls.sh --bootstrap-server localhost:9092 \
  --add --allow-principal User:orders-svc \
  --producer --topic orders

Allow a service to consume from a topic with a consumer group. The --consumer flag expands to Read and Describe on the topic plus Read on the group, and missing the group ACL is the classic cause of a GROUP_AUTHORIZATION_FAILED error after the topic ACL looks right:

bin/kafka-acls.sh --bootstrap-server localhost:9092 \
  --add --allow-principal User:billing-svc \
  --consumer --topic orders --group billing-consumers

Grant a team everything under its own prefix:

bin/kafka-acls.sh --bootstrap-server localhost:9092 \
  --add --allow-principal User:teamA \
  --producer --resource-pattern-type prefixed --topic acme.teamA.

List every rule that affects one topic, across literal, wildcard and prefixed patterns:

bin/kafka-acls.sh --bootstrap-server localhost:9092 \
  --list --topic orders --resource-pattern-type match

Transactional producers additionally need Write on the transactional id, and idempotent producers without a transactional id need IdempotentWrite on the cluster. ACL-protected clusters missing IdempotentWrite are a recurring production surprise because newer clients enable idempotence by default, a breaking change Derek documented in our Kafka 3.2.0 idempotent producer write-up.

The requests we field are rarely a plain syntax question. They arrive shaped like one from a platform team running managed connectors, relayed by Derek, our co-founder: scope connector and consumer-group actions to the application owners, driven from the corporate identity provider, without exposing which connectors are self-hosted and which are managed. Native ACLs alone do not express that. It takes ACLs for the broker surface plus role-based control in the tooling layer above it.

Production best practices

Precedence is deny first. When ALLOW and DENY rules both match a request, DENY wins. The practical pattern is broad allows narrowed by specific denies, for example allowing all users to read a topic while denying one bad actor from one host.

Storage moved with KRaft. ZooKeeper-based clusters stored ACLs in ZooKeeper under the AclAuthorizer. KRaft clusters store them in the cluster metadata log under StandardAuthorizer, which removes ZooKeeper as a second system to secure and back up. Backing up ACLs on KRaft means backing up cluster metadata, and exporting rules with kafka-acls.sh --list remains the portable audit trail either way.

Prefer prefixed patterns over rule sprawl. Native ACLs have no groups, no roles and no expiry. Every user-topic-operation combination is an explicit entry, so per-literal-topic grants grow into thousands of rules that are hard to audit. Prefixed patterns plus a topic naming convention keep the rule count proportional to teams rather than topics. The naming conventions that make this work are covered in multi-tenant architecture, and role-level grouping above raw ACLs in RBAC roles.

Audit the decisions, not just the rules. A complete audit trail captures authentication events, authorization allows and denies, ACL changes and administrative operations, so that “who could read this topic” and “who did” are both answerable. On audit logging, the number that settles arguments: a busy cluster at 10,000 requests per second generates gigabytes of DEBUG authorizer log per hour, so the compliance goal is legitimate but DEBUG-in-production is not the mechanism that meets it. Capture authorization decisions through purpose-built audit tooling instead of broker log levels.

Automation, GitOps and principal mapping

ACLs managed by hand drift. The durable pattern is declaring them as code and letting a pipeline reconcile the cluster: Terraform providers, Ansible modules, or on Kubernetes the Strimzi operator, where a KafkaUser custom resource carries its ACL rules (KafkaUserAuthorizationSimple with AclRule entries) and the operator applies them. Whichever tool, the review-and-merge step is the point, an ACL change gets the same scrutiny as a code change. The pipeline patterns are the same ones covered in deployment automation.

The principals those rules bind to come from the authentication layer. SCRAM and PLAIN yield User:<username> directly. Mutual TLS yields the certificate distinguished name, which is shortened to a stable principal with ssl.principal.mapping.rules before ACLs are written against it, as covered on Kafka authentication. Where native ACLs run out of expressiveness, external authorizers such as Open Policy Agent and Apache Ranger evaluate policy as code through the same pluggable authorizer.class.name interface.

Treat the broker config changes like code, and ACLs are broker-adjacent config: version them, review them, apply them through CI, and after any rollback diff the running state against the baseline. This is not a novel discipline. Reddit was managing Kafka provisioning and broker lifecycle through Terraform from 2017, before its Kubernetes migration. If your ACL changes still happen by hand on a jump box, that is the first thing to fix on this page.

FAQ

What is Kafka ACL?

A Kafka ACL is an access control rule that allows or denies a principal an operation on a resource from a host, in the form: Principal P is allowed or denied Operation O from Host H on Resource R matching pattern RP. ACLs are managed with kafka-acls.sh and enforced by the broker’s authorizer.

Related reading