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
8 changes: 8 additions & 0 deletions architecture/sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,3 +532,11 @@ engine with a gateway policy revision.
reports its normalized exit code before shutdown. The gateway persists the
code on sandbox status, records `MainProcessExited`, and makes the sandbox
terminal `Error`; runtime restart policies must not replace the process.

### Kubernetes volume ownership

The Kubernetes driver applies the sandbox group with `OnRootMismatch`. Initial
volume preparation remains enabled; repeated CSI republish does not recursively
replace application-managed groups after the volume root is prepared. This keeps
long-running workloads with distinct internal users usable without granting those
users one another's groups.
10 changes: 10 additions & 0 deletions crates/openshell-driver-kubernetes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,13 @@ openshell sandbox create \
}' \
-- claude
```

## Persistent volume ownership

Sandbox pods pair `fsGroup` with `fsGroupChangePolicy: OnRootMismatch`.
Kubelet prepares a volume whose root lacks the sandbox group or required mode,
then leaves an initialized volume unchanged on subsequent mounts and CSI
republish. Workloads may therefore use distinct identities within their workspace
without periodic recursive group changes revoking access during execution.
Applications remain responsible for their internal file permissions; changing the
volume root's group or required mode can cause kubelet to prepare it again.
18 changes: 18 additions & 0 deletions crates/openshell-driver-kubernetes/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3246,6 +3246,11 @@ fn apply_supervisor_sidecar_topology(
.or_insert_with(|| serde_json::json!({}));
if let Some(sc) = pod_security_context.as_object_mut() {
sc.insert("fsGroup".to_string(), serde_json::json!(params.sandbox_gid));
// CSI republish must not rewrite ownership chosen by a running workload.
sc.insert(
"fsGroupChangePolicy".to_string(),
serde_json::json!("OnRootMismatch"),
);
}

spec.insert("shareProcessNamespace".to_string(), serde_json::json!(true));
Expand Down Expand Up @@ -3462,6 +3467,10 @@ fn apply_workspace_persistence(
.or_insert_with(|| serde_json::json!({}));
if let Some(pod_sc_obj) = pod_sc.as_object_mut() {
pod_sc_obj.insert("fsGroup".to_string(), serde_json::json!(sandbox_gid));
pod_sc_obj.insert(
"fsGroupChangePolicy".to_string(),
serde_json::json!("OnRootMismatch"),
);
}

// 1. Add workspace volume mount to the agent container
Expand Down Expand Up @@ -6520,6 +6529,10 @@ mod tests {

assert_eq!(pod_template["spec"]["shareProcessNamespace"], true);
assert_eq!(pod_template["spec"]["securityContext"]["fsGroup"], 1500);
assert_eq!(
pod_template["spec"]["securityContext"]["fsGroupChangePolicy"],
"OnRootMismatch"
);
let containers = pod_template["spec"]["containers"].as_array().unwrap();
assert_eq!(containers.len(), 2);

Expand Down Expand Up @@ -7447,6 +7460,11 @@ mod tests {
});

apply_workspace_persistence(&mut pod_template, "img:latest", "Always", 1000);
assert_eq!(pod_template["spec"]["securityContext"]["fsGroup"], 1000);
assert_eq!(
pod_template["spec"]["securityContext"]["fsGroupChangePolicy"],
"OnRootMismatch"
);

let cmd = pod_template["spec"]["initContainers"][0]["command"]
.as_array()
Expand Down
Loading