User Story
As a platform engineer running OpenShift in an enterprise cluster,
I want to install OpenShell without granting the privileged SCC to sandbox pods and without disabling gateway TLS,
so that OpenShell can pass my organization's admission policy and security review instead of requiring a standing exception.
Problem Statement
The documented OpenShift install path (docs/kubernetes/openshift.mdx) requires two things that enterprise OpenShift clusters generally do not permit:
oc adm policy add-scc-to-user privileged -z openshell-sandbox -n openshell
--set server.disableTls=true
The page carries an explicit warning that the path is experimental and "for evaluation on a private network."
The machinery to avoid both already exists in the tree. The sidecar topology added for #899 puts network enforcement in a dedicated sidecar and leaves the agent container running non-root with capabilities.drop: ["ALL"]. OpenShift SCC UID-range annotations are already auto-detected. A passthrough Route and cert-manager external issuer support already ship in the chart. What is missing is the connection between them: supervisor.topology still defaults to combined, the chart ships no SCC of its own, and the OpenShift documentation still points at privileged plus TLS off.
The result is that OpenShell's own documentation asks for far more privilege than its current architecture actually needs.
Impact / Why This Matters
Today a platform team evaluating OpenShell on OpenShift must grant the privileged SCC to the service account that runs sandbox pods. privileged is the SCC of last resort — it permits host namespaces, host paths, arbitrary capabilities, and privileged containers. Granting it to a workload that runs untrusted agent code is difficult to defend in a security review, and in many organizations the platform team simply cannot approve it regardless of the justification.
The available workarounds are all inadequate:
- Grant
privileged anyway. Requires a standing security exception. In regulated environments this is a multi-week process with an uncertain outcome, and it must be renewed. Several clusters (ROSA, managed OpenShift with enforced admission) will not permit it at all.
- Run OpenShell somewhere else. Defeats the purpose for teams whose agent workloads must run next to existing OpenShift services.
- Hand-roll a custom SCC. Possible today, but every operator must independently reverse-engineer the required capability set from the driver source, and nothing pins that set as a supported contract — a future change to the pod spec silently breaks their SCC.
Running the gateway over plaintext HTTP compounds this. It rules out the documented path for any cluster where unencrypted service traffic is prohibited, and it is unnecessary given that the TLS-enabled configuration is already exercised in the chart's own render coverage.
This is also a prerequisite for anything downstream. An OperatorHub-distributed operator (#1719) cannot pass Red Hat operator certification while its workload pods require privileged, so this gap blocks the Kubernetes operator discussion from producing a shippable OpenShift artifact regardless of which CRD shape wins.
Proposed Design
OpenShift should have a supported, non-experimental install path that requires neither the privileged SCC nor disabled TLS.
Installation workflow. A platform admin installing on OpenShift should be able to enable an OpenShift-aware chart configuration in one place, rather than assembling a set of individual overrides and out-of-band oc adm policy commands. Installing with that configuration should produce a working deployment where:
- Sandbox pods run under a dedicated, least-privilege SCC that OpenShell defines and the chart can install, bound only to the sandbox service account. The SCC should not permit privileged containers, host namespaces, or host paths.
- The gateway pod runs under the cluster's default
restricted-v2 SCC with no overrides, letting OpenShift admission assign UID and fsGroup.
- Gateway TLS is enabled by default on this path, with the passthrough
Route and cert-manager integration that already exist as the documented way to expose it.
Observable behavior. After installing on a cluster where privileged has not been granted to any OpenShell service account, openshell sandbox create succeeds and the sandbox reaches a running state. oc get pod <sandbox> -o jsonpath='{.metadata.annotations.openshift\.io/scc}' reports the OpenShell SCC, not privileged. Sandbox network policy enforcement, L7 inspection, and process supervision behave the same as on upstream Kubernetes.
Supported contract. The set of capabilities the sandbox pod requires should be treated as a documented, tested contract rather than an implementation detail, so that a platform team's own SCC (or a future operator bundle) can rely on it and so that a change to the pod spec that widens it is caught in CI.
Scope boundary. Whether the strict process/binary-aware network policy mode is available under the least-privilege SCC is a design decision this issue does not want to pre-empt: it currently needs additional /proc inspection access that the relaxed mode does not. Either it works under the shipped SCC, or the tradeoff between the two modes is documented so a platform team can choose. What should not happen is privileged remaining the only documented answer.
Acceptance Criteria
Alternatives Considered
Document a hand-written SCC without shipping one. Cheaper, and it would unblock teams who can author their own SCC. Rejected as the primary answer because it leaves the required capability set as an undocumented implementation detail that every operator re-derives and that no test protects. Shipping the SCC makes it a contract.
Make sandbox pods work under stock restricted-v2 with no custom SCC. The ideal outcome, and close to the Platform network mode originally proposed in #899. Not proposed here because the network init container legitimately needs NET_ADMIN/NET_RAW to install nftables rules, and removing that would mean moving egress enforcement out of the pod to NetworkPolicy — a real architectural change with a security tradeoff that #899 already litigated. A least-privilege custom SCC captures most of the benefit without reopening that decision.
Grant anyuid instead of privileged. Narrower than privileged and a common OpenShift escape hatch, but it does not grant the capabilities the network init container needs, so it does not actually work here. It also carries no capability restrictions of its own, making it a weaker contract than a purpose-built SCC.
Leave OpenShift support experimental and address it as part of the operator work in #1719. Rejected because the dependency runs the other way: the operator cannot be certified or distributed on OperatorHub while the workload requires privileged, and #1719 has no agreed shape yet. This gap is independently useful to close and does not depend on that decision.
Agent Investigation
Explored crates/openshell-driver-kubernetes/ and deploy/helm/openshell/ to establish what the sandbox pod spec actually requires.
Sidecar topology already produces a near-restricted pod. With supervisor.topology: sidecar, apply_supervisor_sidecar_topology (driver.rs:2873) rewrites the agent container to runAsUser: <sandbox_uid>, runAsNonRoot: true, allowPrivilegeEscalation: false, capabilities.drop: ["ALL"] — asserted at driver.rs:5737-5746. The elevated work is confined to two other containers:
| Container |
runAsUser |
allowPrivilegeEscalation |
Capabilities |
network-init (init) — driver.rs:2824 |
0 |
false |
drop ALL, add NET_ADMIN, NET_RAW, CHOWN, FOWNER |
agent — driver.rs:2873 |
sandbox UID |
false |
drop ALL |
network sidecar — driver.rs:2742 |
proxy UID (0 in strict mode) |
false |
drop ALL, add SYS_PTRACE, DAC_READ_SEARCH (strict mode only) |
By contrast the default combined topology requests SYS_ADMIN, NET_ADMIN, SYS_PTRACE, SYSLOG on the agent container itself (driver.rs:3572) — that is the path that forces privileged, and it is still the chart default (values.yaml:51).
Nothing in the sandbox pod spec needs host access. No hostPath, hostNetwork, hostPID, or privileged: true appears in the pod spec; driver.rs:5537 explicitly asserts the supervisor volume is emptyDir, not hostPath. This is what makes a narrow SCC feasible rather than just narrower-than-privileged.
OpenShift UID handling is already implemented. The driver reads openshift.io/sa.scc.uid-range and openshift.io/sa.scc.supplemental-groups (config.rs:419-425) and resolves the sandbox UID/GID from them when not explicitly configured (config.rs:603-614, driver.rs:1395).
TLS-on is already reachable. openshiftRoute.* (values.yaml:523) renders a passthrough Route, and ci/values-openshift-route-cert-manager.yaml already exercises server.disableTls: false with cert-manager and a Route in render coverage. The plaintext requirement in the docs appears to be a documentation default rather than a technical constraint.
No SCC ships today. deploy/helm/openshell/templates/ contains role.yaml, clusterrole.yaml, and their bindings, but no SecurityContextConstraints resource and no oc-equivalent binding.
Sidecar mode is tested but downgraded in CI. ci/values-sidecar.yaml sets processBinaryAwareNetworkPolicy: false, noting the strict path "is hardened" while dev/e2e uses the relaxed mode. That is the reason for the scope boundary noted in the design above.
Related: #899 (closed as completed — delivered the sidecar topology this builds on), #2751 (combined-topology CAP_SETPCAP gap), #2697 (sandbox_uid/sandbox_gid as Helm values, accepted), #2707 (low numeric UID/GID, validated), #2466 (cert-manager external issuer + passthrough Route, merged), #2091 and #2094 (OpenShift docs expansion), #1719 (Kubernetes Operator — downstream consumer of this).
Checklist
User Story
As a platform engineer running OpenShift in an enterprise cluster,
I want to install OpenShell without granting the
privilegedSCC to sandbox pods and without disabling gateway TLS,so that OpenShell can pass my organization's admission policy and security review instead of requiring a standing exception.
Problem Statement
The documented OpenShift install path (
docs/kubernetes/openshift.mdx) requires two things that enterprise OpenShift clusters generally do not permit:oc adm policy add-scc-to-user privileged -z openshell-sandbox -n openshell--set server.disableTls=trueThe page carries an explicit warning that the path is experimental and "for evaluation on a private network."
The machinery to avoid both already exists in the tree. The sidecar topology added for #899 puts network enforcement in a dedicated sidecar and leaves the agent container running non-root with
capabilities.drop: ["ALL"]. OpenShift SCC UID-range annotations are already auto-detected. A passthroughRouteand cert-manager external issuer support already ship in the chart. What is missing is the connection between them:supervisor.topologystill defaults tocombined, the chart ships no SCC of its own, and the OpenShift documentation still points atprivilegedplus TLS off.The result is that OpenShell's own documentation asks for far more privilege than its current architecture actually needs.
Impact / Why This Matters
Today a platform team evaluating OpenShell on OpenShift must grant the
privilegedSCC to the service account that runs sandbox pods.privilegedis the SCC of last resort — it permits host namespaces, host paths, arbitrary capabilities, and privileged containers. Granting it to a workload that runs untrusted agent code is difficult to defend in a security review, and in many organizations the platform team simply cannot approve it regardless of the justification.The available workarounds are all inadequate:
privilegedanyway. Requires a standing security exception. In regulated environments this is a multi-week process with an uncertain outcome, and it must be renewed. Several clusters (ROSA, managed OpenShift with enforced admission) will not permit it at all.Running the gateway over plaintext HTTP compounds this. It rules out the documented path for any cluster where unencrypted service traffic is prohibited, and it is unnecessary given that the TLS-enabled configuration is already exercised in the chart's own render coverage.
This is also a prerequisite for anything downstream. An OperatorHub-distributed operator (#1719) cannot pass Red Hat operator certification while its workload pods require
privileged, so this gap blocks the Kubernetes operator discussion from producing a shippable OpenShift artifact regardless of which CRD shape wins.Proposed Design
OpenShift should have a supported, non-experimental install path that requires neither the
privilegedSCC nor disabled TLS.Installation workflow. A platform admin installing on OpenShift should be able to enable an OpenShift-aware chart configuration in one place, rather than assembling a set of individual overrides and out-of-band
oc adm policycommands. Installing with that configuration should produce a working deployment where:restricted-v2SCC with no overrides, letting OpenShift admission assign UID and fsGroup.Routeand cert-manager integration that already exist as the documented way to expose it.Observable behavior. After installing on a cluster where
privilegedhas not been granted to any OpenShell service account,openshell sandbox createsucceeds and the sandbox reaches a running state.oc get pod <sandbox> -o jsonpath='{.metadata.annotations.openshift\.io/scc}'reports the OpenShell SCC, notprivileged. Sandbox network policy enforcement, L7 inspection, and process supervision behave the same as on upstream Kubernetes.Supported contract. The set of capabilities the sandbox pod requires should be treated as a documented, tested contract rather than an implementation detail, so that a platform team's own SCC (or a future operator bundle) can rely on it and so that a change to the pod spec that widens it is caught in CI.
Scope boundary. Whether the strict process/binary-aware network policy mode is available under the least-privilege SCC is a design decision this issue does not want to pre-empt: it currently needs additional
/procinspection access that the relaxed mode does not. Either it works under the shipped SCC, or the tradeoff between the two modes is documented so a platform team can choose. What should not happen isprivilegedremaining the only documented answer.Acceptance Criteria
privilegedSCC to any OpenShell service account.restricted-v2withoutpodSecurityContext.fsGroup=null/securityContext.runAsUser=nulloverrides.openshift.io/sccannotation.docs/kubernetes/openshift.mdxdocuments this path and drops theprivileged+disableTls=trueinstructions.Alternatives Considered
Document a hand-written SCC without shipping one. Cheaper, and it would unblock teams who can author their own SCC. Rejected as the primary answer because it leaves the required capability set as an undocumented implementation detail that every operator re-derives and that no test protects. Shipping the SCC makes it a contract.
Make sandbox pods work under stock
restricted-v2with no custom SCC. The ideal outcome, and close to thePlatformnetwork mode originally proposed in #899. Not proposed here because the network init container legitimately needsNET_ADMIN/NET_RAWto install nftables rules, and removing that would mean moving egress enforcement out of the pod to NetworkPolicy — a real architectural change with a security tradeoff that #899 already litigated. A least-privilege custom SCC captures most of the benefit without reopening that decision.Grant
anyuidinstead ofprivileged. Narrower thanprivilegedand a common OpenShift escape hatch, but it does not grant the capabilities the network init container needs, so it does not actually work here. It also carries no capability restrictions of its own, making it a weaker contract than a purpose-built SCC.Leave OpenShift support experimental and address it as part of the operator work in #1719. Rejected because the dependency runs the other way: the operator cannot be certified or distributed on OperatorHub while the workload requires
privileged, and #1719 has no agreed shape yet. This gap is independently useful to close and does not depend on that decision.Agent Investigation
Explored
crates/openshell-driver-kubernetes/anddeploy/helm/openshell/to establish what the sandbox pod spec actually requires.Sidecar topology already produces a near-restricted pod. With
supervisor.topology: sidecar,apply_supervisor_sidecar_topology(driver.rs:2873) rewrites the agent container torunAsUser: <sandbox_uid>,runAsNonRoot: true,allowPrivilegeEscalation: false,capabilities.drop: ["ALL"]— asserted atdriver.rs:5737-5746. The elevated work is confined to two other containers:network-init(init) —driver.rs:2824NET_ADMIN,NET_RAW,CHOWN,FOWNERagent—driver.rs:2873driver.rs:2742SYS_PTRACE,DAC_READ_SEARCH(strict mode only)By contrast the default
combinedtopology requestsSYS_ADMIN,NET_ADMIN,SYS_PTRACE,SYSLOGon the agent container itself (driver.rs:3572) — that is the path that forcesprivileged, and it is still the chart default (values.yaml:51).Nothing in the sandbox pod spec needs host access. No
hostPath,hostNetwork,hostPID, orprivileged: trueappears in the pod spec;driver.rs:5537explicitly asserts the supervisor volume isemptyDir, nothostPath. This is what makes a narrow SCC feasible rather than just narrower-than-privileged.OpenShift UID handling is already implemented. The driver reads
openshift.io/sa.scc.uid-rangeandopenshift.io/sa.scc.supplemental-groups(config.rs:419-425) and resolves the sandbox UID/GID from them when not explicitly configured (config.rs:603-614,driver.rs:1395).TLS-on is already reachable.
openshiftRoute.*(values.yaml:523) renders a passthroughRoute, andci/values-openshift-route-cert-manager.yamlalready exercisesserver.disableTls: falsewith cert-manager and a Route in render coverage. The plaintext requirement in the docs appears to be a documentation default rather than a technical constraint.No SCC ships today.
deploy/helm/openshell/templates/containsrole.yaml,clusterrole.yaml, and their bindings, but no SecurityContextConstraints resource and nooc-equivalent binding.Sidecar mode is tested but downgraded in CI.
ci/values-sidecar.yamlsetsprocessBinaryAwareNetworkPolicy: false, noting the strict path "is hardened" while dev/e2e uses the relaxed mode. That is the reason for the scope boundary noted in the design above.Related: #899 (closed as completed — delivered the sidecar topology this builds on), #2751 (combined-topology
CAP_SETPCAPgap), #2697 (sandbox_uid/sandbox_gidas Helm values, accepted), #2707 (low numeric UID/GID, validated), #2466 (cert-manager external issuer + passthrough Route, merged), #2091 and #2094 (OpenShift docs expansion), #1719 (Kubernetes Operator — downstream consumer of this).Checklist