# Isolate workspace content

Enable the Omnia admission policy so that a pod in a workspace namespace can mount only its own `workspace-<namespace>-content` PVC.

The policy sits on top of the storage layout described in [Workspace content layout](/explanation/platform/workspace-content-layout/). It rejects a pod that names a foreign workspace's content PVC or the shared-root PVC. For the paths on the volume and the mount modes, see the [layout reference](/reference/platform/workspace-content-layout/).

## Prerequisites

- Kubernetes 1.30 or later with the `ValidatingAdmissionPolicy` feature. On an older cluster, use a policy controller instead (see [Clusters without ValidatingAdmissionPolicy](#clusters-without-validatingadmissionpolicy)).
- An Omnia release installed with Helm, with workspace content storage configured.

## Enable the policy

1. Set the chart values. `failurePolicy` is `Fail` by default, so a policy error rejects the pod. The policy is off by default.

   ```yaml
   workspaceContent:
     admissionPolicy:
       enabled: true
       failurePolicy: Fail
   ```

2. Optional: also reject any PersistentVolume that points at the share root. Set `shareRootPath` to the NFS export path of your install, and `provisionerUsername` to the identity that creates the per-workspace volumes so that it stays exempt. Both values below are illustrative.

   ```yaml
   workspaceContent:
     admissionPolicy:
       shareRootPath: /data/omnia
       provisionerUsername: system:serviceaccount:kube-system:nfs-provisioner
   ```

   The PV policy is rendered only when `shareRootPath` is set. It rejects creation of a PersistentVolume whose `spec.nfs.path` equals `shareRootPath`.

3. Apply the values. `--reuse-values` does not read the values from steps 1 and 2, so pass them in a file.

   ```bash
   helm upgrade omnia oci://ghcr.io/altairalabs/charts/omnia --devel \
     --namespace omnia-system --reuse-values -f values.yaml
   ```

The policy applies to pod creates and updates in every namespace except the release namespace, where the operator mounts the share root.

## Verify

1. Confirm the policy and its binding exist.

   ```bash
   kubectl get validatingadmissionpolicy,validatingadmissionpolicybinding | grep workspace-content-mount
   ```

2. Try a pod in a workspace namespace that mounts another namespace's claim. `other` is a placeholder.

   ```yaml
   apiVersion: v1
   kind: Pod
   metadata:
     name: mount-probe
   spec:
     containers:
       - name: probe
         image: busybox
         command: ["sleep", "60"]
         volumeMounts:
           - name: content
             mountPath: /content
     volumes:
       - name: content
         persistentVolumeClaim:
           claimName: workspace-other-content
   ```

   ```bash
   kubectl apply -n <workspace-namespace> --dry-run=server -f mount-probe.yaml
   ```

   The request is denied, and the error contains `pod may only mount its own namespace's workspace-content PVC`. Change `claimName` to `workspace-<workspace-namespace>-content` and the same dry run is admitted.

## Clusters without ValidatingAdmissionPolicy

Enforce the same rule with a policy controller. Exclude the Omnia release namespace from either policy, because the operator mounts the share root there.

The rule: reject a pod volume whose PVC claim name contains `workspace`, ends in `-content`, and is not `workspace-<namespace>-content`.

### Gatekeeper

Create a `ConstraintTemplate` whose Rego runs over `input.review.object`, then a Constraint of that kind that matches `Pod` and lists the release namespace in `excludedNamespaces`. The Rego below is the rule in Gatekeeper's syntax; Omnia does not ship or test it.

```rego
package workspacecontent
violation[{"msg": msg}] {
  vol := input.review.object.spec.volumes[_]
  claim := vol.persistentVolumeClaim.claimName
  contains(claim, "workspace")
  endswith(claim, "-content")
  claim != sprintf("workspace-%s-content", [input.review.namespace])
  msg := sprintf("pod may only mount workspace-%s-content", [input.review.namespace])
}
```

### Kyverno

Create a `ClusterPolicy` with a `validate.deny` rule that applies the same condition to `request.object.spec.volumes[].persistentVolumeClaim.claimName`, matching `Pod` and excluding the release namespace.

## Troubleshooting

### A pod in a workspace namespace is rejected after enabling the policy

The pod names a content PVC other than `workspace-<namespace>-content`. Any claim name that contains `workspace` and ends in `-content` is checked, so rename an unrelated claim that matches that pattern.

## Related pages

- [Workspace content layout](/explanation/platform/workspace-content-layout/): why the volume is shaped this way, and how the dashboard reaches it through the operator
- [Workspace content layout reference](/reference/platform/workspace-content-layout/): paths, mount modes and the content API
- [Manage workspaces](/how-to/workspaces/manage-workspaces/)