Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 72 additions & 4 deletions docs/accepted-limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,79 @@ 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: <any Secret sitting in the workspace namespace>
```

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".

:::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.

**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.

:::

Expand Down
13 changes: 8 additions & 5 deletions docs/admin/bootstrap-governance.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,21 @@ 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:

- **`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

Expand Down
18 changes: 18 additions & 0 deletions docs/concepts/governance.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,24 @@ 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. The bootstrap default policy
grants none of them — but an install upgraded from chart 0.2.x may
still carry `volumes`.

:::

## Everyday admin procedures

**Approve an image**: add a `WorkspaceImage` with the exact ref (Git or
Expand Down
10 changes: 6 additions & 4 deletions docs/concepts/templates-and-protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
61 changes: 57 additions & 4 deletions versioned_docs/version-v0.2.0/accepted-limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: <any Secret sitting in the workspace namespace>
```

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
Expand Down
16 changes: 16 additions & 0 deletions versioned_docs/version-v0.2.0/concepts/governance.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down