Description
The sandbox ServiceAccount is fixed gateway-wide. [openshell.drivers.kubernetes] service_account_name is a single value that serves two purposes at once:
- the driver stamps it onto every sandbox pod as
serviceAccountName —
|
if !params.service_account_name.is_empty() { |
- bootstrap auth requires the presented principal to match it exactly —
|
if sa_name != expected_service_account { |
So every sandbox on a gateway necessarily shares one Kubernetes identity. This asks for that to be configurable as a set, in the same spirit as the namespace-level validation added in #2656.
Context
The ServiceAccount is the unit of cloud IAM federation. On EKS the IAM trust policy conditions on the token subject:
StringLike <oidc>:sub = "system:serviceaccount:<namespace>:<service-account>"
GCP Workload Identity and Azure Workload Identity bind the same way. So "one SA per gateway" means "one cloud identity per gateway": sandboxes doing different work, with different blast radii, cannot be granted different cloud permissions. Granting the union to the shared SA gives every sandbox the most-privileged set.
Notably the SA is the only pod-level field with no per-sandbox override. platform_config on the SandboxTemplate already carries runtime_class_name, node_selector, tolerations, annotations and host_users, including a documented per-sandbox-overrides-cluster-default precedent:
|
// Per-sandbox platform_config.host_users overrides the cluster-wide default. |
Workarounds today, both unattractive:
- Share one SA and grant it the union of permissions — every sandbox gets every permission.
- Run a gateway per identity — preserves isolation, but duplicates the gateway, its database and its forwarder topology for what is a single config value.
Proposed Direction
Two complementary parts; either is useful, together they close the case:
- Accept a configured set of sandbox service accounts in bootstrap auth. A single value stays the default and keeps current behaviour.
- Allow the pod's ServiceAccount to be selected per sandbox — e.g.
platform_config.service_account_name, constrained to the set from (1), defaulting to the existing single value.
They are separable. (2) without (1) cannot authenticate, so (1) is the prerequisite. (1) alone is already useful for deployments where something other than the gateway sets the pod's ServiceAccount — a mutating admission policy, or an external controller that owns the sandbox pods — which is the situation we hit: our pods are given a different SA out-of-band, and bootstrap then fails with
K8s TokenReview principal is not the configured sandbox service account
Whichever way (1) is implemented, it should be additive rather than a widening of service_account_name, because of consumer (1) in the Description: turning that field into a list or a delimited string would render invalid pods. The pod default and the accepted-for-auth set need to be distinct settings.
This overlaps #2023, which proposes making bootstrap config explicit and independent of the selected compute driver, and whose first Definition-of-Done item is defining the ownership model for the bootstrap namespace and service-account settings. That seems like the natural place to decide where an accepted set lives; happy to follow that issue's outcome rather than pre-empt it.
Security properties are unchanged: tokens are still verified through TokenReview, still required to be pod-bound (pod_name / pod_uid extras), and IssueSandboxToken still requires a K8sServiceAccount-sourced sandbox principal. This widens which identities an operator may enrol, not how they are verified — the accepted set stays operator-configured and closed.
Definition of Done
Environment
Observed on gateway 0.0.85; code references above are against main at 8d67250.
Description
The sandbox ServiceAccount is fixed gateway-wide.
[openshell.drivers.kubernetes] service_account_nameis a single value that serves two purposes at once:serviceAccountName—OpenShell/crates/openshell-driver-kubernetes/src/driver.rs
Line 3517 in 8d67250
OpenShell/crates/openshell-server/src/auth/k8s_sa.rs
Line 386 in 8d67250
So every sandbox on a gateway necessarily shares one Kubernetes identity. This asks for that to be configurable as a set, in the same spirit as the namespace-level validation added in #2656.
Context
The ServiceAccount is the unit of cloud IAM federation. On EKS the IAM trust policy conditions on the token subject:
GCP Workload Identity and Azure Workload Identity bind the same way. So "one SA per gateway" means "one cloud identity per gateway": sandboxes doing different work, with different blast radii, cannot be granted different cloud permissions. Granting the union to the shared SA gives every sandbox the most-privileged set.
Notably the SA is the only pod-level field with no per-sandbox override.
platform_configon the SandboxTemplate already carriesruntime_class_name,node_selector,tolerations,annotationsandhost_users, including a documented per-sandbox-overrides-cluster-default precedent:OpenShell/crates/openshell-driver-kubernetes/src/driver.rs
Line 3503 in 8d67250
Workarounds today, both unattractive:
Proposed Direction
Two complementary parts; either is useful, together they close the case:
platform_config.service_account_name, constrained to the set from (1), defaulting to the existing single value.They are separable. (2) without (1) cannot authenticate, so (1) is the prerequisite. (1) alone is already useful for deployments where something other than the gateway sets the pod's ServiceAccount — a mutating admission policy, or an external controller that owns the sandbox pods — which is the situation we hit: our pods are given a different SA out-of-band, and bootstrap then fails with
Whichever way (1) is implemented, it should be additive rather than a widening of
service_account_name, because of consumer (1) in the Description: turning that field into a list or a delimited string would render invalid pods. The pod default and the accepted-for-auth set need to be distinct settings.This overlaps #2023, which proposes making bootstrap config explicit and independent of the selected compute driver, and whose first Definition-of-Done item is defining the ownership model for the bootstrap namespace and service-account settings. That seems like the natural place to decide where an accepted set lives; happy to follow that issue's outcome rather than pre-empt it.
Security properties are unchanged: tokens are still verified through
TokenReview, still required to be pod-bound (pod_name/pod_uidextras), andIssueSandboxTokenstill requires aK8sServiceAccount-sourced sandbox principal. This widens which identities an operator may enrol, not how they are verified — the accepted set stays operator-configured and closed.Definition of Done
Environment
Observed on gateway 0.0.85; code references above are against
mainat 8d67250.