The Kafka agent is an authorizer of Kafka's Authorizer API, named in authorizer.class.name: brokers and controllers ask it about every request, and it answers from the kafka data service's signed policies. The distribution ships it as one Java 8-compatible JAR, agents/kafka/grantforge-agent-kafka-<version>.jar, with its dependencies shaded inside.
The repository's Testcontainers integration tests run the JAR inside Kafka 3.7.2, 3.9.2, 4.1.2 and 4.3.1, in KRaft nodes that are broker and controller at once. This does not certify every other release. See Apache Kafka for the service plugin.
How it works
- Every request is checked. For each resource a request touches, Kafka asks for an ACL operation; the agent answers from the policies, with Kafka's resource types as the resource levels and its operations as the access types. A refusal reaches the client as Kafka's own error, such as
TOPIC_AUTHORIZATION_FAILED. - Idempotent producers need only a topic. Kafka lets a producer start without
idempotent_writeon the cluster when it may write to some topic, and asks the authorizer whether it may. The agent answers from the policies themselves: an allow ofwriteon any topic counts, and a denial counts only in a policy that covers every topic. - The policies show as ACLs.
kafka-acls --listand the Admin API'sdescribeAclsshow the policies that apply now: a value without wildcards as a literal ACL, a value such asorders*as a prefixed one and*as Kafka's wildcard; users asUser:principals, groups asGroup:, roles asRole:and thepublicgroup asUser:*. What ACLs cannot express is left out: other wildcards, excluded values, items with conditions and the items of a policy that has exceptions to them. Creating or deleting ACLs is refused: the ACLs of this cluster are GrantForge policies; change them in GrantForge. - Decisions are kept. A broker asks the same questions on every produce and fetch; the agent keeps the decisions of the current snapshot, by default 100,000 of them for 10 seconds. A new snapshot starts afresh, so a policy change applies as soon as the agent has it.
- Repeated accesses are folded. Identical allowed accesses within a window, by default 10 seconds, are reported as one access event that says how many there were and when the last one happened; the console shows N identical accesses. Accesses are identical when they share the user, client address, resource, access type, request type and deciding policy. Refusals are reported one by one at once. Kafka chooses what is worth reporting: it does not ask to report, for example, the topics it leaves out of a listing.
- Super users are not checked. The principals of
super.usersmay do everything, as with Kafka's own authorizer, and are not audited.
Deployment
-
Add a
kafkadata service and configure its policies. Open Data permissions → Agents, select the service and issue an agent token. It is shown only once; store it in a file only Kafka's account can read. -
Copy the JAR into
libsof every broker and controller, such as/opt/kafka/libs. -
Add the settings to the
server.propertiesof each, replacing the example principals, address, paths and instance name:authorizer.class.name=org.devlive.grantforge.kafka.agent.GrantForgeKafkaAuthorizer super.users=User:kafka grantforge.server.url=https://grantforge.example.com grantforge.token.file=/etc/kafka/grantforge.token grantforge.instance=broker-1 grantforge.cache.dir=/var/lib/kafka/grantforge-cachesuper.usersmust name the principals brokers and controllers use among themselves, such as that of the inter-broker listener, or policies must allow them what they do, such ascluster_actionon the cluster. Connections to aPLAINTEXTlistener areUser:ANONYMOUS; name it only when no client can reach such a listener. The cache directory must be writable by Kafka's account. -
Restart the brokers and controllers one at a time. Listeners other than the controllers' open once the agent has its first snapshot, at most after
grantforge.start.timeout.ms; until then, and whenever there is no snapshot, requests are refused. -
Move existing ACLs first: ACLs Kafka's own authorizer stored no longer apply once the agent replaces it. List them with
kafka-acls --listbeforehand and create matching policies.
| Setting | Purpose |
|---|---|
grantforge.server.url, grantforge.token.file, grantforge.instance, grantforge.cache.dir |
Required: the GrantForge server, the agent token's file, a name for this broker or controller and a writable directory for the signed snapshot and spooled events |
grantforge.signing.key.file |
Optional file pinning the snapshot signing key; copy the Base64-encoded X.509 Ed25519 public key from the Agents page. If omitted, the key is fetched from the server on first contact |
grantforge.connect.timeout.ms, grantforge.read.timeout.ms |
GrantForge connection and read timeouts, default 5000 / 8000 milliseconds |
grantforge.refresh.interval.ms |
How often the agent asks for new policies, default 30000, at least 1000 milliseconds |
grantforge.audit.batch.size, grantforge.audit.queue.capacity, grantforge.audit.flush.interval.ms, grantforge.audit.spool.limit.bytes |
Access events: batch size (default 500), queue size (default 10000), sending interval (default 5000 milliseconds) and spool limit (default 64 MiB) |
grantforge.audit.fold.window.ms |
How long identical allowed accesses are folded into one event, default 10000, up to 3600000 milliseconds; 0 reports each |
grantforge.decision.cache.size, grantforge.decision.cache.ttl.ms |
The most decisions kept, default 100000, up to 10000000, 0 keeps none; and for how long, default 10000, 1..3600000 milliseconds |
grantforge.start.timeout.ms |
How long listeners wait for the first snapshot, default 30000, 0..600000 milliseconds |
The agent reads its settings from the configuration Kafka hands its authorizer. A node that is broker and controller at once creates an authorizer for each role; both share one agent.
Users, groups and roles
Policies name the principal Kafka gives the connection: the SASL user name, the short name of a Kerberos principal by sasl.kerberos.principal.to.local.rules, or a TLS certificate's name by ssl.principal.mapping.rules. Kafka has no groups or roles; those of the policies are the ones GrantForge gives the user.
Decisions and limitations
- Without a usable snapshot, everything but the super users' requests is refused. A service that is not in use leaves Kafka unchecked, as it is without an authorizer.
- A token bound to a service whose type is not
kafkarefuses everything. - A denial of an access type also denies what it implies: denying
writeon a topic deniesdescribe, so the user does not see it. Kafka's own ACLs differ here. - Kafka's question whether a user may act on some resource of a type is no access and is not reported.
- Decisions are kept for
grantforge.decision.cache.ttl.ms, so a policy's validity period starts or ends up to that much late. - The agent works in brokers and controllers. Kafka Connect's REST API, schema registries and other services around Kafka are not covered.
