Monedula
monedula-gitops docs menu

Write advanced ACL rules

Use KafkaAccessPolicy for prefixed patterns, shared consumer groups, host restrictions and rules that do not belong to a single topic.

How-to guides 6 of 18

3 min read

Updated 5 October 2026

Markdown

On this page

A KafkaAccessPolicy holds raw Kafka ACL rules for access that a topic’s access block cannot express: prefixed patterns, shared consumer groups, transactional IDs, the cluster resource, Deny rules and host restrictions.

Before you start

  • A connecting principal that may manage ACLs, which takes Describe and Alter on the Cluster resource, or a super-user.
  • On Confluent Cloud, write principals with the service account’s numeric ID, such as User:9207877 (Supported platforms).

Steps

1. Write the policy

This policy lets User:svc-billing write with the transactional ID billing-tx and read with the consumer group billing-cg. Save it as manifests/policy.yaml:

apiVersion: gitops.monedula.dev/v1alpha1
kind: KafkaAccessPolicy
metadata:
  name: billing-access
spec:
  clusterRef:
    name: shared
  rules:
    - principal: "User:svc-billing"
      permission: Allow
      host: "*"
      resource:
        type: transactionalId
        name: billing-tx
        patternType: literal
      operations:
        - Write
        - Describe
    - principal: "User:svc-billing"
      permission: Allow
      host: "*"
      resource:
        type: group
        name: billing-cg
        patternType: literal
      operations:
        - Read

Each rule grants its operations on one resource pattern to one principal. resource.type is topic, group, cluster, transactionalId or delegationToken; resource.patternType is literal (default) or prefixed; permission is Allow (default) or Deny; and host (default *) is matched against the client IP as the broker sees it. operations lists at least one of Read, Write, Create, Delete, Alter, Describe, ClusterAction, DescribeConfigs, AlterConfigs, IdempotentWrite and All.

Grant shared consumer groups in a policy, as this one does for billing-cg, instead of in a topic’s access block.

2. Add a prefixed, host-restricted rule

A prefixed rule covers every resource whose name starts with the prefix. Add this rule to spec.rules to let User:svc-billing read, write and describe every topic whose name starts with billing., but only from the host 10.0.0.15:

- principal: User:svc-billing
  host: "10.0.0.15"
  resource:
    type: topic
    name: billing.
    patternType: prefixed
  operations: [Read, Write, Describe]

A Deny rule must not contradict an Allow. The same principal, host, resource and operation granted Allow by one resource, a topic’s access block included, and Deny by another is a conflict. The CLI rejects the manifests when it loads them and exits 2, naming the conflicting rule. With the operator, the admission webhook, when enabled, rejects the conflicting policy, and the reconciler always marks every resource party to the conflict with the ACLConflict condition and leaves the contested rule out of what it applies.

3. Apply the policy

monedula-gitops apply -f ./manifests --cluster-config-file ./cluster.yaml
kubectl apply -f manifests/

Add --dry-run to the CLI command to preview the ACLs it would create. With the operator, a namespace that a tenancy policy restricts to topic prefixes may only declare topic and group rules that match them.

4. Change or remove rules

When you narrow a rule that stays declared, for example drop an operation, the ACLs it no longer needs become PruneDisabled candidates until you opt in to pruning with apply --prune or spec.prune: true on the policy. When you remove a rule and no other manifest declares that principal on that resource pattern, its ACLs leave the managed scope and stay in Kafka, unreported. Changing a rule’s resource.name or patternType leaves the old pattern behind the same way. To revoke that access, delete the leftover ACLs outside monedula-gitops, for example with Kafka’s kafka-acls tool.

Check it worked

monedula-gitops verify -f ./manifests --cluster-config-file ./cluster.yaml
kubectl get kafkaaccesspolicy billing-access -o jsonpath='{.status.conditions}'

verify exits 0 when the live ACLs match the policy. With the operator, the Ready and AccessPolicySynced conditions have the status True. For the policy from step 1, the broker now has three ACLs for User:svc-billing: Write and Describe on the transactional ID billing-tx, and Read on the group billing-cg.