Skip to content

feat(openshift): supported install path without the privileged SCC or disabled TLS #2812

Description

@flg77

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:

  1. oc adm policy add-scc-to-user privileged -z openshell-sandbox -n openshell
  2. --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

  • OpenShell installs on an OpenShift cluster without granting the privileged SCC to any OpenShell service account.
  • The chart can create a least-privilege SCC and bind it to the sandbox service account, and this is the documented OpenShift path.
  • The gateway installs and runs with TLS enabled on the documented OpenShift path.
  • The gateway pod is admitted under restricted-v2 without podSecurityContext.fsGroup=null / securityContext.runAsUser=null overrides.
  • Sandbox pods are admitted under the OpenShell SCC, verifiable via the openshift.io/scc annotation.
  • Sandbox creation, network policy enforcement, and L7 inspection behave equivalently to upstream Kubernetes on this path.
  • The capability set required by sandbox pods is documented and covered by a test that fails if the pod spec requires more than the shipped SCC allows.
  • docs/kubernetes/openshift.mdx documents this path and drops the privileged + disableTls=true instructions.
  • The experimental warning on the OpenShift page is removed or narrowed to what remains unsupported.

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

  • I've reviewed existing issues and the architecture docs
  • This is a design proposal, not a "please build this" request

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:triage-neededOpened without agent diagnostics and needs triage

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions