KubeMQ
ConnectorsKafkaHow-to guides

Translating Kafka ACLs

Turn an Apache Kafka ACL set into KubeMQ authorization rules — the resource mapping, the operations that differ, and a worked example.

KubeMQ does not keep a Kafka ACL store. Access for Kafka clients is written as KubeMQ authorization rules — the same policy every KubeMQ connector uses — and this page is the translation table. The Kafka ACL admin calls are answered honestly rather than faked: kafka-acls --add (CreateAcls) is refused with SECURITY_DISABLED and a message naming the policy mechanism, and DescribeAcls returns an empty list — so a script that provisions ACLs fails loudly instead of appearing to succeed. Authorization is off by default; it is enabled and supplied as described in Security → Authorization.

The rule shape

A policy is a JSON array of rules. For Kafka, every rule sets EventsStore: true — Kafka topics, consumer groups and transactional ids all live there:

policy.json
[
  { "EventsStore": true, "ClientID": "orders-service", "Channel": "orders", "Read": false, "Write": true }
]
  • ClientID is the authenticated principal: the SASL username, the OAUTHBEARER token's sub, or the client certificate's common name (Authentication).
  • Channel is the bare name of a topic, a consumer group, or a transactional id — there is no resource-type prefix, so a topic and a group with the same name share a rule.
  • Both are regular expressions matched against the whole value: orders matches only orders; orders-.* matches every orders- topic; .* matches everything, including the * that stands for "the whole cluster" below.

With authorization on, a request with no authenticated principal is refused — there is no anonymous access.

Translation table

Kafka ACLKubeMQ rule
Topic READRead on the topic
Topic WRITEWrite on the topic
Topic DESCRIBE, DESCRIBE_CONFIGSRead on the topic
Topic CREATE, DELETE, ALTER, ALTER_CONFIGSWrite on the topic
Group READ (consume and commit offsets)Read and Write on the group — see below
Group DESCRIBERead on the group
Group DELETEWrite on the group
TransactionalId WRITEWrite on the transactional id
Cluster IDEMPOTENT_WRITEWrite on channel * (a rule with Channel: ".*" covers it)
Cluster DESCRIBE (list groups, describe cluster)Read on channel *

Where it differs from Kafka

Two operations need more than their Kafka equivalent:

  • Committing a consumer offset needs Write on the group. Apache Kafka asks for Group READ. A consumer translated with only Read consumes but fails its first commit with GROUP_AUTHORIZATION_FAILED.
  • A transaction's offset commit needs Write on the group (AddOffsetsToTxn, TxnOffsetCommit), where Kafka again asks for Read. Exactly-once pipelines — Kafka Streams with exactly_once_v2, consume-transform-produce — hit this first.

And one needs less spelling out: an idempotent producer — the default since Kafka 3.0 — initialises with Write on the cluster channel *. Kafka 2.8+ accepts Topic Write in its place; here, add the cluster rule.

Worked example

A Kafka ACL set for one service that consumes orders, writes orders-enriched transactionally, and runs as orders-service:

Kafka
User:orders-service  Topic:orders           READ
User:orders-service  Topic:orders-enriched  WRITE
User:orders-service  Group:enricher         READ
User:orders-service  TransactionalId:enricher-  WRITE   (prefixed)
KubeMQ policy
[
  { "EventsStore": true, "ClientID": "orders-service", "Channel": "orders",          "Read": true,  "Write": false },
  { "EventsStore": true, "ClientID": "orders-service", "Channel": "orders-enriched", "Read": false, "Write": true },
  { "EventsStore": true, "ClientID": "orders-service", "Channel": "enricher",        "Read": true,  "Write": true },
  { "EventsStore": true, "ClientID": "orders-service", "Channel": "enricher-.*",     "Read": false, "Write": true },
  { "EventsStore": true, "ClientID": "orders-service", "Channel": "\\*",             "Read": false, "Write": true }
]

The group rule carries Write for the offset commits; the last rule is the idempotent-producer cluster grant, written as \\* so it matches only the cluster channel rather than every name.

A denied request answers with the same codes Kafka uses: TOPIC_AUTHORIZATION_FAILED, GROUP_AUTHORIZATION_FAILED, TRANSACTIONAL_ID_AUTHORIZATION_FAILED or CLUSTER_AUTHORIZATION_FAILED. In a request that names several topics, only the denied ones fail.

Was this page helpful?

On this page