diff --git a/README.md b/README.md index 277dcce..96b44d6 100644 --- a/README.md +++ b/README.md @@ -41,16 +41,70 @@ kafka-kafka-bootstrap.kafka.svc.cluster.local:9093 | `kafka-admin` | `kafka-admin` | Super user — unrestricted access | | `kafka-client` | `kafka-client` | Application user — Read/Write all topics and groups | | `classic-models-api` | `classic-models-api` | Producer for `classic-models.*` — **Write and Describe only**, no Read | +| `classic-models-agent-approvisionnement` | same name | Consumer of `classic-models.catalog` and group prefix `approvisionnement-` — **Read and Describe only**, no Write | -`classic-models-api` publishes domain events and consumes nothing, so it is -write-only and prefix-scoped. A producer that can also read every topic is a -producer that exfiltrates every other domain's events if it is ever -compromised. +Each application account is scoped to what it actually does. The producer +publishes domain events and consumes nothing, so it is write-only: one that +could also read every topic exfiltrates every other domain's events if it is +ever compromised. The consumer is the mirror — it cannot see sales or payments, +and it cannot write, so a compromised workflow can raise no event that another +agent would then act on. + +Duplicate one of these per application. `kafka-client` is the wide-open +template, useful for a smoke test and for nothing that runs unattended. Strimzi stores credentials in Kubernetes secrets. Each secret contains two keys: - `password` — the SCRAM password - `sasl.jaas.config` — the ready-to-use JAAS config string +### Topics + +Declared in `kafka-topics.yaml`, never auto-created — an application account has +no `Create` ACL, so a producer would simply be denied, and an auto-created topic +takes broker defaults nobody chose. + +| Topic | Partitions | Replicas | Retention | +|---|---|---|---| +| `classic-models.catalog` | 3 | 1 | 7 days | +| `classic-models.sales` | 3 | 1 | 7 days | +| `classic-models.payments` | 3 | 1 | 7 days | + +Partition count is the ceiling on consumer parallelism and **cannot be lowered +later**, so it is set for the traffic expected rather than the traffic today. + +The events these carry are specified by their producer, in +`classic-models-api/docs/DOMAIN_EVENTS.md`. This repo owns the cluster, not the +contract. + +### Getting credentials into another namespace + +A Secret cannot be mounted across namespaces, and Strimzi writes a KafkaUser's +password into **this** one. `credentials-access.yaml` therefore declares a +`ClusterSecretStore` named `kafka-credentials` (External Secrets, `kubernetes` +provider) backed by a ServiceAccount that can read Secrets here and nowhere +else. An application namespace then pulls its own password and the cluster CA +with an `ExternalSecret`: + +```yaml +secretStoreRef: + kind: ClusterSecretStore + name: kafka-credentials +data: + - secretKey: password + remoteRef: {key: , property: password} + - secretKey: ca.crt + remoteRef: {key: kafka-cluster-ca-cert, property: ca.crt} +``` + +The password stays generated and rotatable by Strimzi; the copy follows. The +alternative — copying it by hand — drifts the first time the user is +regenerated. + +**The trade, stated:** any namespace allowed to create an `ExternalSecret` +against this store can pull **any** Kafka account's credentials. Acceptable with +one operator; on a shared cluster, narrow the Role with `resourceNames` and run +one store per application. + ### TLS Strimzi generates and manages its own internal CA. The cluster CA certificate is stored in: @@ -125,6 +179,8 @@ The target performs the following steps: 3. Waits for the Strimzi CRDs to be fully established 4. Applies `kafka.yaml` (KafkaNodePool + Kafka CRs) and waits for `Ready` 5. Applies `kafka-users.yaml` (KafkaUser CRs) and waits for `Ready` +6. Applies `kafka-topics.yaml` (the declared topics) and `credentials-access.yaml` + (the cross-namespace credentials store) Expected duration: **4–6 minutes** on a single-node k3s cluster.