MINOR: Add draft threat model + SECURITY.md + AGENTS.md for security-model discoverability#22431
MINOR: Add draft threat model + SECURITY.md + AGENTS.md for security-model discoverability#22431potiuk wants to merge 2 commits into
Conversation
…l discoverability Adds a draft (v0) threat model plus SECURITY.md and AGENTS.md so an automated scan agent can discover the model via AGENTS.md -> SECURITY.md -> THREAT_MODEL.md. The model is a proposal for the PMC to review; most claims are (inferred) and route to open questions in its section 14. Generated-by: Claude Code (Claude Opus 4.8)
clolov
left a comment
There was a problem hiding this comment.
Thanks for opening this PR. I have tried providing answers to the questions I have answers to and pulled in others who I think are better-suited at answering the ones I don't.
| ## §14 Open questions for the maintainers | ||
|
|
||
| **Wave 1 — the default-posture rulings (decide VALID-vs-misconfig; §5a/§8/§9):** | ||
| 1. Is running a broker with the **default PLAINTEXT listener and no authorizer** a *supported* posture (relying |
There was a problem hiding this comment.
My opinion is that a "PLAINTEXT listener and no authorizer" is valid only for development.
| 1. Is running a broker with the **default PLAINTEXT listener and no authorizer** a *supported* posture (relying | ||
| on network controls), so an "unauthenticated broker" report against defaults is `BY-DESIGN` — or should it | ||
| be `VALID`? *Proposed:* operator must secure before exposing; open default is dev-only. | ||
| 2. With the StandardAuthorizer, what is the default of **`allow.everyone.if.no.acl.found`**, and is "no ACL ⇒ |
There was a problem hiding this comment.
| **Wave 2 — auth/authz mechanics (§8):** | ||
| 4. Which **SASL mechanisms** are recommended/discouraged by default, and does the broker enforce TLS for | ||
| credential-exposing mechanisms (PLAIN)? *Proposed:* SCRAM/GSSAPI/OAUTHBEARER recommended; PLAIN requires TLS. | ||
| 5. Are **delegation tokens** and idempotent/transactional state gated by ACLs the same as normal operations? |
There was a problem hiding this comment.
Idempotent producers and transactions are gated by ACLs (source: https://kafka.apache.org/43/security/authorization-and-acls/#operations-and-resources-on-protocols)
Delegation tokens are a mix of both ACLs and additional checks specifically for tokens i.e. something authenticated with a token cannot create another token. For the purposes of this the answer is yes.
| operator-trusted configs is out of model, but an unauthenticated REST API is the real exposure. | ||
|
|
||
| **Wave 2 — auth/authz mechanics (§8):** | ||
| 4. Which **SASL mechanisms** are recommended/discouraged by default, and does the broker enforce TLS for |
There was a problem hiding this comment.
SCRAM/GSSAPI/OAUTHBEARER are recommended.
As far as I am aware nothing enforces TLS for PLAIN (sources: https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/security/auth/SecurityProtocol.java#L28, https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/security/auth/SecurityProtocol.java#L32). Everything over PLAINTEXT should be development-only.
For OAUTHBEARER we support client_credentials and client_assertion (source: https://cwiki.apache.org/confluence/display/KAFKA/KIP-1258%3A+Add+Support+for+OAuth+Client+Assertion+to+client_credentials+Grant+Type)
| **Wave 3 — DoS, peers, §11a (§7/§8/§11a):** | ||
| 6. What **request-size / quota / throttling** guarantees bound RPC DoS, and where is the resource line? | ||
| *Proposed:* `socket.request.max.bytes` + quotas bound it; beyond that, operator config. | ||
| 7. Confirm **cluster peers / the KRaft quorum / ZooKeeper** are trusted (out of §7). *Proposed:* yes. |
There was a problem hiding this comment.
Let's trust cluster peers and KRaft quorum for now. Let's remove references to Apache ZooKeeper. Technically versions 3.9.x still have ZooKeeper, but it isn't on trunk.
| 6. What **request-size / quota / throttling** guarantees bound RPC DoS, and where is the resource line? | ||
| *Proposed:* `socket.request.max.bytes` + quotas bound it; beyond that, operator config. | ||
| 7. Confirm **cluster peers / the KRaft quorum / ZooKeeper** are trusted (out of §7). *Proposed:* yes. | ||
| 8. What do scanners most often (re)report that the PMC considers a **non-finding**? (Seeds §11a.) |
| 8. What do scanners most often (re)report that the PMC considers a **non-finding**? (Seeds §11a.) | ||
|
|
||
| **Meta:** | ||
| 9. Confirm this model lives as root `THREAT_MODEL.md` referenced from a new `SECURITY.md`, covering the broker |
There was a problem hiding this comment.
Ideally the THREAT_MODEL.md makes it in as a new page under docs/security, but I am happy to move it as a subsequent step if that formatting will somehow break things. I think it is fair to treat Streams as client library for now (@mjsax maybe you can weigh in here?). Given that we expose REST APIs from Connect I have a feeling we need to go in a bit more depth (@mimaison thoughts?).
| be `VALID`? *Proposed:* operator must secure before exposing; open default is dev-only. | ||
| 2. With the StandardAuthorizer, what is the default of **`allow.everyone.if.no.acl.found`**, and is "no ACL ⇒ | ||
| deny" the intended secured behavior? *Proposed:* deny by default under StandardAuthorizer. | ||
| 3. Does the **Connect REST API** require authentication by default, and is connector-config URL handling |
There was a problem hiding this comment.
@mimaison you may be the best-suited person to answer this question? If not maybe you know who might be?
| *Proposed:* yes. | ||
|
|
||
| **Wave 3 — DoS, peers, §11a (§7/§8/§11a):** | ||
| 6. What **request-size / quota / throttling** guarantees bound RPC DoS, and where is the resource line? |
There was a problem hiding this comment.
The configurations which protect against a DoS and their default values are:
socket.request.max.bytes at 100 MiB
queued.max.requests at 500
connection.failed.authentication.delay.ms at 100 ms
We further have the following unset by default ones:
queued.max.request.bytes
max.connections{.per.ip}
max.connection.creation.rate
By default quotas are unset. They can be set on a produce/consume level, on a generic request level, or on the number of mutations processable by the controller.
| | Metadata control plane | KRaft quorum (`raft`, `metadata`) / ZooKeeper (legacy) | network | **Yes (peer-trust)** | | ||
| | Coordinators | group / transaction / share coordinators | — | **Yes** | | ||
| | Storage + tiered storage | log segments; remote-storage plugins | filesystem; remote store | **Yes** | | ||
| | Kafka Connect | REST control plane + connector plugins | network egress; plugin code | **Yes (addendum C)** | |
| an untrusted REST caller (if the REST API is unauthenticated) is the real finding. | ||
| - **Findings in `tools`, `shell`, `trogdor`, `tests`, `docker`, samples** — out of scope (§3). | ||
| - **Streams application-level issues** — out of the broker model (§3). | ||
| - **Idempotent-producer / replication internals** not reachable from an unauthorized client — out of surface. |
There was a problem hiding this comment.
What is the reason that an idempotent producer is grouped with the replication internals? Is the idea here that Kafka has some internal state (i.e. for idempotent producer or for replication) which lives on brokers and is not exposed?
| | Kafka Connect | REST control plane + connector plugins | network egress; plugin code | **Yes (addendum C)** | | ||
| | Kafka Streams | client library (runs in the app) | — | Light → §3 | | ||
| | Clients library | parses broker responses | — | **Yes (client-side)** | | ||
| | tools / shell / trogdor / tests / docker | — | — | No → §3 | |
There was a problem hiding this comment.
I don't know whether you need an exhaustive list or just an list of examples, but let's also exclude committer-tools
There was a problem hiding this comment.
And bin. bin contains the .sh files which we use to start a broker, for example, but if we are assuming that this is out of scope due to it being a responsibility of the operator I don't think we need to look into it either
This is a draft proposal for the Kafka PMC to review — please correct,
reject, or discuss as needed. Nothing here is a requirement; the
maintainers are the decision-makers, and this describes Kafka as the
PMC says it is.
This PR adds
THREAT_MODEL.md+SECURITY.md+AGENTS.md, wiringAGENTS.md -> SECURITY.md -> THREAT_MODEL.md.Framing: Kafka is a configurable platform — it provides mechanisms
(SASL/mTLS auth, an ACL authorizer, TLS, quotas) and the operator
chooses which listeners use them; a broker can run wide open
(PLAINTEXT, no authorizer) or fully locked down. The untrusted network
client is the adversary; the operator and trusted cluster peers /
metadata quorum are out of model.
Draft-first, mostly inferred (~16 documented / 0 maintainer / ~58
inferred); every
*(inferred)*claim routes to a numbered §14question. The wave-1 rulings decide
VALID-vs-misconfiguration:supported posture (network-trust), so an "unauthenticated broker"
report against defaults is by-design — or should it be
VALID?allow.everyone.if.no.acl.found"no ACL ⇒ deny"?how should connector-config URL handling (SSRF) be treated?
Scope note: this covers the broker + Connect; Kafka Streams is treated
as a client library (in-app trust), and tools/shell/trogdor/tests are
out of the runtime model.
Context: the ASF Security team is preparing the project for an automated
agentic security scan we're piloting. Drafted via the
threat-model-producer
rubric. If you'd rather author it yourselves, close this PR and we'll
regroup.
Reviewers: Christo Lolov lolovc@amazon.com