Skip to content

chore: add draft threat model - #1642

Merged
oscerd merged 2 commits into
mainfrom
docs/threat-model
Sep 8, 2026
Merged

oscerd merged 2 commits into
mainfrom
docs/threat-model

Conversation

@oscerd

@oscerd oscerd commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

What this is

A threat model for Karavan, at docs/threat-model.md.

It is deliberately not an audit, a pentest report, or a findings list. It describes the
implicit contract between Karavan and the people who deploy it: what Karavan assumes, what
it guarantees given those assumptions, what it leaves to the operator, and which misuses
look reasonable but aren't. The audience is two-sided — an operator deciding how to deploy
Karavan, and a triager who has to classify an inbound vulnerability report or a scanner hit
and cite a section to justify the call.

Why

Karavan has no SECURITY.md, and no entry in the ASF security site's
project-coordinates.json.
The Apache Camel security model
scopes itself to apache/camel artifacts and explicitly notes that sibling subprojects
"have their own security surfaces" — Karavan isn't named. So today there is nothing a
triager can cite when a report or an automated scan lands.

The model inherits Camel's role vocabulary (route author / operator / external message
sender) rather than inventing a parallel one. Appendix A back-maps all eleven of that
document's statements to sections here.

Status: reviewed

@mgubaidullin answered waves 1–3 below. Every hypothesis in the first draft is now
confirmed, corrected, or superseded:

57 (documented) · 40 (maintainer) · 0 (inferred) — down from 28 inferred.

Only Q13 and Q14 remain open, and both are about the document rather than about
Karavan (see Still open at the bottom).

The claim the document turns on — confirmed

The trust boundary is HTTP authentication, and nothing after it. A Karavan account is
Camel's "route author", which that model declares fully trusted. Docker mode bind-mounts
/var/run/docker.sock; Kubernetes mode grants verbs: ["*"] plus pods/exec; a project's
docker-compose.yaml reaches MountType.valueOf(...) and /bin/sh -c; and
configuration/build.sh is a user-editable file executed at mode 0755.

Q2 confirmed this. So "an authenticated user achieves RCE" is OUT-OF-MODEL: adversary-not-in-scope, and §4.11a carries it as a suppression entry alongside the
docker.sock mount, the wildcard RBAC, and the application.properties credentials that
every secret scanner flags.

Three answers changed the model rather than confirming it

Q11 corrected the draft. I proposed that the non-invalidating
/platform/internal/sources/{projectId}/{filename} route was an oversight. It isn't — it's
by design. §4.8 property 9 now scopes the single-use guarantee to the whole-project route
only, and a report that the per-file route reuses a session is BY-DESIGN.

Q5 was confirmed but not promised. The negative side-effect inventory (no signal
handlers, no child processes, no host path reads, no extra listeners) is accurate — but
"that is just how it is today and we do not have any plans to change that" is a description,
not an invariant. §4.5 now says so explicitly and asks readers to re-verify on a §4.12
trigger, rather than treating it as a guarantee a future release will keep.

Q1 settled the insecure default. platform.password is an install-time convenience set
via Kubernetes secret or Docker env var and changed afterwards through the UI, so a report
against the shipped default is OUT-OF-MODEL: non-default-build.

Q9 rebound the whole document

"Already fixed" turned out to mean three controls that landed after the 4.22.0 tag:

Control Commit Where it lands
CsrfFilter — synchronizer-token check on X-CSRF-Token, constant-time, logout deliberately not exempt d211d71f §4.8 property 13
PathUtils — project id / file name validation on the /ui/file write paths 5e425249 §4.8 property 14
karavan.deployment.allowed-kinds + pod-spec restrictions, namespace pinned d211d71f §4.5a, §4.9

So the model is now bound to main at 677a293a, not to 4.22.0, and says so in the
header: a report filed against 4.22.0 or earlier is triaged against a model in which those
three do not exist. CSRF moved out of the §4.9 "false friends" table and into §4.8 as a
claimed property.

Two things I want to flag rather than bury:

  • The Kubernetes resource validation is written up as defence in depth, explicitly not a
    boundary.
    It covers startDeployment only — not runBuildProject's pod fragment, not
    configuration/build.sh, and nothing on the Docker path, where a compose file still
    controls bind mounts and the shell command. §4.9 says that plainly so nobody later reads
    it as a sandbox, and §4.11a routes "it can be bypassed via the builder pod" to
    OUT-OF-MODEL rather than to a control bypass.
  • application.properties sets deployment.allowed-kinds, but KubernetesService injects
    karavan.deployment.allowed-kinds.
    The names don't match, so the properties entry
    doesn't bind and the code falls back to its DEFAULT_ALLOWED_DEPLOYMENT_KINDS constant.
    Same value today, so no behaviour change — but editing the properties file to widen the
    list silently does nothing. Not a threat-model matter and not fixed here; worth a separate
    one-line change.

Still open

Q13 — where does this live? Should Karavan get a SECURITY.md pointing at
security@apache.org and at this file, and be added to the ASF security site's
project-coordinates.json so the Camel security page links it as a subproject?

Q14 — who owns revisions, and does the model ship with releases? Proposed: Karavan
committers own it, it updates in the same PR as any change tripping a §4.12 trigger, and it
is tagged per release. This revision already spans two versions — which is exactly the drift
the tagging policy would prevent.

Notes

🤖 Generated with Claude Code

Adds docs/threat-model.md, a first draft of Karavan's threat model
following the ASF Security threat-model-producer rubric.

Karavan has no SECURITY.md and no entry in the ASF security site's
project-coordinates.json. The Apache Camel security model
(camel.apache.org/manual/security-model.html) scopes itself to
apache/camel artifacts and notes that sibling subprojects have their
own security surfaces, so Karavan currently has no document a triager
can cite when classifying a vulnerability report or a scanner finding.

The draft is written from public artifacts only. Every claim carries a
(documented) / (maintainer) / (inferred) provenance tag: 53 documented,
0 maintainer, 28 inferred. The inferred claims are unratified
hypotheses, each routed to a numbered question in section 4.14 for the
maintainers to confirm, correct, or strike.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@oscerd
oscerd force-pushed the docs/threat-model branch from b42af8b to b499cb9 Compare August 28, 2026 13:37
@oscerd
oscerd requested a review from mgubaidullin September 1, 2026 09:16
@oscerd

oscerd commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

@mgubaidullin have a look at the questions so we could complete the model.

@mgubaidullin

mgubaidullin commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Q1: Correct. The default password is a starting point set during installation (via Kubernetes secrets or Docker env vars) and operators are expected to change it through the UI; it is an out-of-scope dev/install convenience, not a supported production posture.

Q2 — Confirm. Yes, any authenticated user is fully trusted.

Q3 — Confirm. Yes, the connected Git repository and image registry are considered fully trusted inputs.

Q4 — Confirm. It is just forward-looking scaffolding for future features.

Q5 - Correct. That is just how it is today and we do not have any plans to change that.

Q6 - Confirmed. Reports against insecure defaults are OUT-OF-MODEL (operator responsibility).

Q7 (Resource Limits): Confirmed. DoS and resource exhaustion are OUT-OF-MODEL.

Q8 (Public Endpoints): Confirmed. Pre-login disclosure of Keycloak/SSO configs is BY-DESIGN.

Q9 - Already fixed

Q10 (VS Code): Confirmed. Malicious workspaces/repos are OUT-OF-MODEL (trusted input).

Q11 (API Sessions): Corrected. The non-invalidating route is BY-DESIGN (not an oversight).

Q12 (Logs): Confirmed. Lack of strict audit trails is BY-DESIGN (it's just telemetry).

@mgubaidullin answered waves 1-3 on #1642. Every hypothesis in the first
draft is now confirmed, corrected, or superseded: 57 documented /
40 maintainer / 0 inferred, down from 28 inferred.

Rulings that changed the model rather than confirming it:

- Q11 corrected the draft. The non-invalidating
  /platform/internal/sources/{projectId}/{filename} route is by design,
  not an oversight; the single-use guarantee is scoped to the
  whole-project route only.
- Q5 confirmed the negative side-effect inventory is accurate but
  declined to commit to it as an invariant, so it is recorded as a
  description of the current release rather than a promise.
- Q1 ruled the default platform.password an install-time convenience
  set via secret or env var and changed through the UI, making reports
  against it OUT-OF-MODEL: non-default-build.

Q9 was answered "already fixed", which rebinds the document. CsrfFilter
(d211d71), PathUtils name validation (5e42524) and the Kubernetes
resource allow-list all landed after the 4.22.0 tag, so the model now
binds to main at 677a293 and says explicitly that a report against
4.22.0 or earlier is triaged against a model without those three. CSRF
moved from a section 4.9 false friend to a claimed property in 4.8.

The Kubernetes resource validation is described as defence in depth and
explicitly not a boundary: it does not cover the builder pod path,
build.sh, or the Docker compose path.

Q13 and Q14, on document ownership and revision policy, remain open.
Appendix B records every ruling so a disputed disposition can be traced
to the answer behind it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@oscerd
oscerd merged commit 0264313 into main Sep 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants