From eb46e7ab3e4d2da62f4ef5e233405d5ffdaa5661 Mon Sep 17 00:00:00 2001 From: DrummyFloyd Date: Sat, 25 Jul 2026 05:33:35 +0200 Subject: [PATCH 1/2] docs: warn what the volumes and securityContext override rights grant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The allow-list gates the field, never its content: a delegated `volumes` right lets a user mount any Secret sitting in the workspace namespace, and `securityContext` includes `privileged: true`. Pod Security Admission does not backstop the first — `restricted` permits `secret` and `projected` volumes. Says so plainly on the accepted-limitations page, gives the mental model (delegate only to people you would trust with kubectl on the namespace), points at the alternatives that keep the admin in control, and names the cluster-side tools for partial trust. Cross-linked from the governance concept page, where the fields are chosen. --- docs/accepted-limitations.md | 72 ++++++++++++++++++++++++++++++++++-- docs/concepts/governance.md | 16 ++++++++ 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/docs/accepted-limitations.md b/docs/accepted-limitations.md index a278c84..993f350 100644 --- a/docs/accepted-limitations.md +++ b/docs/accepted-limitations.md @@ -75,11 +75,75 @@ usual `[Reason] message` format: ::: -:::warning For admins +### Before you delegate these rights (admins) -Delegating `securityContext` delegates the **whole struct** — -`privileged: true` included. Only grant it on templates whose audience -you would trust with `kubectl` on the target namespace. +:::danger These rights gate the *field*, not its *content* + +`allowedFields` decides **whether** a user may set `volumes` or +`securityContext`. It does not — and deliberately will not — inspect +**what** they put in it. Read literally, that means: + +**`volumes`** accepts any Kubernetes volume source. A user who has the +right can attach: + +```yaml +volumes: + - name: anything + secret: + secretName: +``` + +and read that Secret from their desktop. The workspace namespace +normally holds at least the registry pull secret and the per-workspace +SSH key. The same reachability comes through `projected:` (which can +also re-introduce a ServiceAccount token WaaS otherwise disables) and +through `csi`, `cephfs`, `rbd` or `iscsi`, all of which carry their own +`secretRef`. + +**`securityContext`** covers the whole struct, `privileged: true` +included. Only the namespace's Pod Security Admission level stops that. + +::: + +**Why WaaS does not validate the content.** Pod Security Admission is +not the safety net here: the `restricted` level explicitly **permits** +`secret` and `projected` volumes, because mounting a Secret from your +own namespace is ordinary Kubernetes. Re-implementing that judgement +inside WaaS would mean running a second policy engine over a volume +schema that grows with every Kubernetes release — and it would put the +decision in the wrong place. **Constraining how a delegated pod-spec +field may be used is cluster administration**, and Kubernetes already +ships the tools for it. + +**What to do instead.** Treat these two rights as equivalent to handing +out namespace-level access: + +1. **Delegate them only to people you would trust with `kubectl` on the + target namespace.** That is the honest mental model — everything + above follows from it. +2. **Prefer the alternatives when they fit.** A shared filesystem is + better served by an admin-provisioned PersistentVolumeClaim, or by a + volume declared **in the template**, than by a user-authored inline + volume. Both keep you in control of *which* storage is reachable. + Same for secrets: inject them from the template, never through a + user override. +3. **If you must delegate with partial trust, pair it with an admission + policy.** A `ValidatingAdmissionPolicy` (in-tree since Kubernetes + 1.30, no extra component), Kyverno or Gatekeeper, scoped to your + workspace namespaces, is where you express rules like "no `secret` + volumes in tenant-created pods". + +:::info The default policy does not grant these + +The bootstrap `WorkspacePolicy` shipped by the Helm chart grants +`env`, `resources` and `schedule` — **not** `volumes`, and never the +security contexts. The reference GitOps policy set does the same. +Granting them is an explicit, auditable change on your side. + +Earlier chart versions did include `volumes` in that bootstrap policy. +If you relied on it, add it back deliberately via +`defaultPolicy.overrides.allowedFields` or a higher-priority policy — +after reading the warning above. ::: diff --git a/docs/concepts/governance.md b/docs/concepts/governance.md index 0de4576..c306b6e 100644 --- a/docs/concepts/governance.md +++ b/docs/concepts/governance.md @@ -140,6 +140,22 @@ with the template's own list. Absent block = no policy restriction; empty list = all overrides forbidden. Platform admins bypass both lists; a template's `owner` bypasses the template list only. +:::warning `volumes` and `securityContext` are not like the others + +Most fields on that list are bounded by their own type — `resources` is +capped by your limits, `schedule` is a pair of crons. These two are not: +they are pod-spec-shaped, and the allow-list gates the field without +inspecting its content. Granting `volumes` lets a user mount any Secret +present in the workspace namespace; granting `securityContext` includes +`privileged: true`. + +Read [what delegating these rights +grants](../accepted-limitations#before-you-delegate-these-rights-admins) +before adding either to a policy. The bootstrap default policy grants +neither. + +::: + ## Everyday admin procedures **Approve an image**: add a `WorkspaceImage` with the exact ref (Git or From 8a4c0927f118316900b9d245cce0e7903ab39c8a Mon Sep 17 00:00:00 2001 From: DrummyFloyd Date: Sat, 25 Jul 2026 05:48:15 +0200 Subject: [PATCH 2/2] docs: fix the volumes default-grant contradiction, extend to podSecurityContext bootstrap-governance still listed `volumes` as granted by default; chart 0.3.0 drops it, so its table and warning bullet are updated (an upgrade does not rewrite an existing policy CR). Also: `podSecurityContext` joins the two other unbounded rights, cephfs/rbd/iscsi scoped to non-`restricted` clusters, VAP redated to GA 1.30 / beta 1.28, and the "never references Secrets" note narrowed to env. Field-vs-content warning backported to v0.2.0, without the default-policy paragraph that would invert there. --- docs/accepted-limitations.md | 34 ++++++----- docs/admin/bootstrap-governance.md | 13 ++-- docs/concepts/governance.md | 18 +++--- docs/concepts/templates-and-protocols.md | 10 +-- .../version-v0.2.0/accepted-limitations.md | 61 +++++++++++++++++-- .../version-v0.2.0/concepts/governance.md | 16 +++++ .../concepts/templates-and-protocols.md | 6 +- 7 files changed, 120 insertions(+), 38 deletions(-) diff --git a/docs/accepted-limitations.md b/docs/accepted-limitations.md index 993f350..0d4ee3f 100644 --- a/docs/accepted-limitations.md +++ b/docs/accepted-limitations.md @@ -79,9 +79,10 @@ usual `[Reason] message` format: :::danger These rights gate the *field*, not its *content* -`allowedFields` decides **whether** a user may set `volumes` or -`securityContext`. It does not — and deliberately will not — inspect -**what** they put in it. Read literally, that means: +`allowedFields` decides **whether** a user may set `volumes`, +`securityContext` or `podSecurityContext`. It does not — and +deliberately will not — inspect **what** they put in it. Read literally, +that means: **`volumes`** accepts any Kubernetes volume source. A user who has the right can attach: @@ -95,13 +96,15 @@ volumes: and read that Secret from their desktop. The workspace namespace normally holds at least the registry pull secret and the per-workspace -SSH key. The same reachability comes through `projected:` (which can -also re-introduce a ServiceAccount token WaaS otherwise disables) and -through `csi`, `cephfs`, `rbd` or `iscsi`, all of which carry their own -`secretRef`. +SSH key. The same reachability comes through `projected:`, which can +also re-introduce a ServiceAccount token WaaS otherwise disables. On +clusters not enforcing Pod Security Admission `restricted`, `cephfs`, +`rbd` and `iscsi` are reachable too, each with its own `secretRef`. **`securityContext`** covers the whole struct, `privileged: true` -included. Only the namespace's Pod Security Admission level stops that. +included. **`podSecurityContext`** is the pod-level twin — `runAsUser: 0`, +`fsGroup`, `supplementalGroups`, `sysctls`. Only the namespace's Pod +Security Admission level stops either. ::: @@ -115,7 +118,7 @@ decision in the wrong place. **Constraining how a delegated pod-spec field may be used is cluster administration**, and Kubernetes already ships the tools for it. -**What to do instead.** Treat these two rights as equivalent to handing +**What to do instead.** Treat all three rights as equivalent to handing out namespace-level access: 1. **Delegate them only to people you would trust with `kubectl` on the @@ -128,8 +131,8 @@ out namespace-level access: Same for secrets: inject them from the template, never through a user override. 3. **If you must delegate with partial trust, pair it with an admission - policy.** A `ValidatingAdmissionPolicy` (in-tree since Kubernetes - 1.30, no extra component), Kyverno or Gatekeeper, scoped to your + policy.** A `ValidatingAdmissionPolicy` (GA in Kubernetes 1.30, beta + since 1.28, no extra component), Kyverno or Gatekeeper, scoped to your workspace namespaces, is where you express rules like "no `secret` volumes in tenant-created pods". @@ -140,10 +143,11 @@ The bootstrap `WorkspacePolicy` shipped by the Helm chart grants security contexts. The reference GitOps policy set does the same. Granting them is an explicit, auditable change on your side. -Earlier chart versions did include `volumes` in that bootstrap policy. -If you relied on it, add it back deliberately via -`defaultPolicy.overrides.allowedFields` or a higher-priority policy — -after reading the warning above. +**Chart 0.2.x and earlier did grant `volumes`** in that bootstrap +policy; 0.3.0 drops it. Upgrading does not rewrite a policy you already +have, so check `defaultPolicy.overrides.allowedFields` on an existing +install. If you relied on it, add it back deliberately — after reading +the warning above. ::: diff --git a/docs/admin/bootstrap-governance.md b/docs/admin/bootstrap-governance.md index 497a4a2..66529d7 100644 --- a/docs/admin/bootstrap-governance.md +++ b/docs/admin/bootstrap-governance.md @@ -37,7 +37,7 @@ may: | Idle suspend | paused after **2 h** without a session (compute freed, home kept) | | Max lifetime | deleted after **336 h / 14 days** — **home volume included** (TTL contract) | | Clipboard | both directions allowed | -| Template overrides | `env`, `resources`, `schedule`, `volumes` — still intersected with each template's own allow-list | +| Template overrides | `env`, `resources`, `schedule` — still intersected with each template's own allow-list | | Remote workspaces | **off** | Two things worth a second look before going to production: @@ -45,10 +45,13 @@ Two things worth a second look before going to production: - **`maxLifetime: 336h` destroys data**: a policy TTL deletes the home volume along with the workspace — that is the documented contract of a TTL. If your users keep long-lived desktops, raise or remove it. -- **`volumes` is in the default override list**: on any template that - *also* delegates `volumes`, a user can mount arbitrary volume - sources (including `hostPath`). The template list is the effective - gate — just be aware the policy side is open by default. +- **`volumes` is no longer in the default override list**: chart 0.2.x + and earlier granted it, so an upgraded install may still carry it in + `defaultPolicy.overrides.allowedFields`. Check yours. Granting + `volumes` — or either security context — on a template *and* a policy + lets a user mount arbitrary volume sources, including any Secret in + the workspace namespace; see [what delegating these rights + grants](../accepted-limitations#before-you-delegate-these-rights-admins). ## The admin policy — off by default, and why you probably want it diff --git a/docs/concepts/governance.md b/docs/concepts/governance.md index c306b6e..c3f083f 100644 --- a/docs/concepts/governance.md +++ b/docs/concepts/governance.md @@ -140,19 +140,21 @@ with the template's own list. Absent block = no policy restriction; empty list = all overrides forbidden. Platform admins bypass both lists; a template's `owner` bypasses the template list only. -:::warning `volumes` and `securityContext` are not like the others +:::warning `volumes` and the security contexts are not like the others Most fields on that list are bounded by their own type — `resources` is -capped by your limits, `schedule` is a pair of crons. These two are not: -they are pod-spec-shaped, and the allow-list gates the field without -inspecting its content. Granting `volumes` lets a user mount any Secret -present in the workspace namespace; granting `securityContext` includes -`privileged: true`. +capped by your limits, `schedule` is a pair of crons. `volumes`, +`securityContext` and `podSecurityContext` are not: they are +pod-spec-shaped, and the allow-list gates the field without inspecting +its content. Granting `volumes` lets a user mount any Secret present in +the workspace namespace; `securityContext` includes `privileged: true`, +and `podSecurityContext` includes `runAsUser: 0` and `sysctls`. Read [what delegating these rights grants](../accepted-limitations#before-you-delegate-these-rights-admins) -before adding either to a policy. The bootstrap default policy grants -neither. +before adding any of them to a policy. The bootstrap default policy +grants none of them — but an install upgraded from chart 0.2.x may +still carry `volumes`. ::: diff --git a/docs/concepts/templates-and-protocols.md b/docs/concepts/templates-and-protocols.md index 9cb5064..6e5e0fa 100644 --- a/docs/concepts/templates-and-protocols.md +++ b/docs/concepts/templates-and-protocols.md @@ -169,10 +169,12 @@ by the operator when the workload is rendered). Wiring a Secret into a desktop is a **template** decision: the admin who authors the template references it through env `valueFrom` or [`credentialsSecretRef`](#credentials), both resolved against the -workspace's own namespace. The template — authored by an admin, -typically through GitOps — is therefore the only channel that can point -a desktop at a cluster Secret; a workspace creator tunes values, never -references Secrets. +workspace's own namespace. Through `env`, therefore, a workspace creator +tunes values and never references Secrets. + +This holds for `env` only. A delegated `volumes` override reaches +Secrets directly, by mounting them — see [what delegating that right +grants](../accepted-limitations#before-you-delegate-these-rights-admins). ::: :::warning diff --git a/versioned_docs/version-v0.2.0/accepted-limitations.md b/versioned_docs/version-v0.2.0/accepted-limitations.md index a278c84..c30cabd 100644 --- a/versioned_docs/version-v0.2.0/accepted-limitations.md +++ b/versioned_docs/version-v0.2.0/accepted-limitations.md @@ -75,14 +75,67 @@ usual `[Reason] message` format: ::: -:::warning For admins +### Before you delegate these rights (admins) -Delegating `securityContext` delegates the **whole struct** — -`privileged: true` included. Only grant it on templates whose audience -you would trust with `kubectl` on the target namespace. +:::danger These rights gate the *field*, not its *content* + +`allowedFields` decides **whether** a user may set `volumes`, +`securityContext` or `podSecurityContext`. It does not — and +deliberately will not — inspect **what** they put in it. Read literally, +that means: + +**`volumes`** accepts any Kubernetes volume source. A user who has the +right can attach: + +```yaml +volumes: + - name: anything + secret: + secretName: +``` + +and read that Secret from their desktop. The workspace namespace +normally holds at least the registry pull secret and the per-workspace +SSH key. The same reachability comes through `projected:`, which can +also re-introduce a ServiceAccount token WaaS otherwise disables. On +clusters not enforcing Pod Security Admission `restricted`, `cephfs`, +`rbd` and `iscsi` are reachable too, each with its own `secretRef`. + +**`securityContext`** covers the whole struct, `privileged: true` +included. **`podSecurityContext`** is the pod-level twin — `runAsUser: 0`, +`fsGroup`, `supplementalGroups`, `sysctls`. Only the namespace's Pod +Security Admission level stops either. ::: +**Why WaaS does not validate the content.** Pod Security Admission is +not the safety net here: the `restricted` level explicitly **permits** +`secret` and `projected` volumes, because mounting a Secret from your +own namespace is ordinary Kubernetes. Re-implementing that judgement +inside WaaS would mean running a second policy engine over a volume +schema that grows with every Kubernetes release — and it would put the +decision in the wrong place. **Constraining how a delegated pod-spec +field may be used is cluster administration**, and Kubernetes already +ships the tools for it. + +**What to do instead.** Treat all three rights as equivalent to handing +out namespace-level access: + +1. **Delegate them only to people you would trust with `kubectl` on the + target namespace.** That is the honest mental model — everything + above follows from it. +2. **Prefer the alternatives when they fit.** A shared filesystem is + better served by an admin-provisioned PersistentVolumeClaim, or by a + volume declared **in the template**, than by a user-authored inline + volume. Both keep you in control of *which* storage is reachable. + Same for secrets: inject them from the template, never through a + user override. +3. **If you must delegate with partial trust, pair it with an admission + policy.** A `ValidatingAdmissionPolicy` (GA in Kubernetes 1.30, beta + since 1.28, no extra component), Kyverno or Gatekeeper, scoped to your + workspace namespaces, is where you express rules like "no `secret` + volumes in tenant-created pods". + ## Choosing the target namespace at creation A template (or policy) can delegate the `placement` right, and the diff --git a/versioned_docs/version-v0.2.0/concepts/governance.md b/versioned_docs/version-v0.2.0/concepts/governance.md index 0de4576..ce9d0d6 100644 --- a/versioned_docs/version-v0.2.0/concepts/governance.md +++ b/versioned_docs/version-v0.2.0/concepts/governance.md @@ -140,6 +140,22 @@ with the template's own list. Absent block = no policy restriction; empty list = all overrides forbidden. Platform admins bypass both lists; a template's `owner` bypasses the template list only. +:::warning `volumes` and the security contexts are not like the others + +Most fields on that list are bounded by their own type — `resources` is +capped by your limits, `schedule` is a pair of crons. `volumes`, +`securityContext` and `podSecurityContext` are not: they are +pod-spec-shaped, and the allow-list gates the field without inspecting +its content. Granting `volumes` lets a user mount any Secret present in +the workspace namespace; `securityContext` includes `privileged: true`, +and `podSecurityContext` includes `runAsUser: 0` and `sysctls`. + +Read [what delegating these rights +grants](../accepted-limitations#before-you-delegate-these-rights-admins) +before adding any of them to a policy. + +::: + ## Everyday admin procedures **Approve an image**: add a `WorkspaceImage` with the exact ref (Git or diff --git a/versioned_docs/version-v0.2.0/concepts/templates-and-protocols.md b/versioned_docs/version-v0.2.0/concepts/templates-and-protocols.md index 6728955..4620ef2 100644 --- a/versioned_docs/version-v0.2.0/concepts/templates-and-protocols.md +++ b/versioned_docs/version-v0.2.0/concepts/templates-and-protocols.md @@ -160,8 +160,10 @@ applied override is audited (field and env var _names_, never values). :::warning Allow-listing `volumes` lets users mount arbitrary volume sources — -including `hostPath`. Only enable it on templates aimed at trusted -groups. +including `hostPath`, and including any Secret in the workspace +namespace. Only enable it on templates aimed at trusted groups; see +[what delegating that right +grants](../accepted-limitations#before-you-delegate-these-rights-admins). ::: In the portal, users who hold override rights see an **Advanced