Document the topics, the consumer account and the credentials store
The README described the cluster as it was before events existed: no declared topics, no consumer, and no way for another namespace to reach a password Strimzi keeps here. The events themselves are specified by their producer, not here — this repo owns the cluster, not the contract. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -41,16 +41,70 @@ kafka-kafka-bootstrap.kafka.svc.cluster.local:9093
|
|||||||
| `kafka-admin` | `kafka-admin` | Super user — unrestricted access |
|
| `kafka-admin` | `kafka-admin` | Super user — unrestricted access |
|
||||||
| `kafka-client` | `kafka-client` | Application user — Read/Write all topics and groups |
|
| `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-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
|
Each application account is scoped to what it actually does. The producer
|
||||||
write-only and prefix-scoped. A producer that can also read every topic is a
|
publishes domain events and consumes nothing, so it is write-only: one that
|
||||||
producer that exfiltrates every other domain's events if it is ever
|
could also read every topic exfiltrates every other domain's events if it is
|
||||||
compromised.
|
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:
|
Strimzi stores credentials in Kubernetes secrets. Each secret contains two keys:
|
||||||
- `password` — the SCRAM password
|
- `password` — the SCRAM password
|
||||||
- `sasl.jaas.config` — the ready-to-use JAAS config string
|
- `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: <kafkauser-name>, 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
|
### TLS
|
||||||
|
|
||||||
Strimzi generates and manages its own internal CA. The cluster CA certificate is stored in:
|
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
|
3. Waits for the Strimzi CRDs to be fully established
|
||||||
4. Applies `kafka.yaml` (KafkaNodePool + Kafka CRs) and waits for `Ready`
|
4. Applies `kafka.yaml` (KafkaNodePool + Kafka CRs) and waits for `Ready`
|
||||||
5. Applies `kafka-users.yaml` (KafkaUser 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.
|
Expected duration: **4–6 minutes** on a single-node k3s cluster.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user