Skip to content
Open
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
22 changes: 21 additions & 1 deletion apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2019-2025 Red Hat, Inc.
// Copyright (c) 2019-2026 Red Hat, Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Expand Down Expand Up @@ -146,6 +146,23 @@ type RoutingConfig struct {
TLSCertificateConfigmapRef *ConfigmapReference `json:"tlsCertificateConfigmapRef,omitempty"`
}

// OverrideConfig defines configuration options for controlling which fields are denied
// in `container-overrides` and `pod-overrides` DevWorkspace attributes.
// Entries support value restrictions: "fieldName" denies the field entirely,
// "fieldName=value" denies only the specific value (other values are allowed).
type OverrideConfig struct {
// DeniedContainerOverrideFields defines a list of container-level fields that are denied
// in `container-overrides` attributes. The following fields are always
// restricted regardless of this setting: `name`, `image`, `command`, `args`, `ports`, `env`.
// +kubebuilder:validation:Optional
DeniedContainerOverrideFields []string `json:"deniedContainerOverrideFields,omitempty"`
// DeniedPodOverrideFields defines a list of pod-level fields that are denied
// in `pod-overrides` attributes. The following fields are always restricted
// regardless of this setting: `containers`, `initContainers`.
// +kubebuilder:validation:Optional
DeniedPodOverrideFields []string `json:"deniedPodOverrideFields,omitempty"`
}

type WorkspaceConfig struct {
// ProjectCloneConfig defines configuration related to the project clone init container
// that is used to clone git projects into the DevWorkspace.
Expand Down Expand Up @@ -264,6 +281,9 @@ type WorkspaceConfig struct {
// InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods.
// Typical uses cases include injecting organization tools/configs, initializing persistent home, etc.
InitContainers []corev1.Container `json:"initContainers,omitempty"`
// Overrides defines configuration options for `container-overrides` and
// `pod-overrides` DevWorkspace attributes.
Overrides *OverrideConfig `json:"overrides,omitempty"`
}

type WebhookConfig struct {
Expand Down
30 changes: 30 additions & 0 deletions apis/controller/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions controllers/workspace/devworkspace_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"time"

"github.com/devfile/devworkspace-operator/pkg/library/initcontainers"
"github.com/devfile/devworkspace-operator/pkg/library/overrides"
"github.com/devfile/devworkspace-operator/pkg/library/ssh"

dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
Expand Down Expand Up @@ -339,6 +340,7 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request
workspace.Config.Workspace.DefaultContainerResources,
workspace.Config.Workspace.ContainerResourceCaps,
workspace.Config.Workspace.PostStartTimeout,
overrides.GetDeniedContainerOverrideFields(workspace),
postStartDebugTrapSleepDuration,
)
if err != nil {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions deploy/deployment/kubernetes/combined.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions deploy/deployment/openshift/combined.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

80 changes: 80 additions & 0 deletions docs/dwo-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,83 @@ config:
### Execution Order

Custom init containers are injected after the project-clone init container in the order they are defined in the configuration. The `init-persistent-home` container runs in this sequence along with other custom init containers.

## Restricting override fields

The DevWorkspace Operator allows cluster administrators to restrict which fields
can be set via `pod-overrides` and `container-overrides` attributes.
Fields are restricted using a deny list -- by default, all fields are allowed unless explicitly denied.

The deny list supports two formats:

- `"fieldName"` -- denies the field entirely, regardless of value
- `"fieldName=value"` -- denies only a specific value for the field

For nested fields such as securityContext or volumes, use dot notation: `securityContext.privileged=true`.

On Kubernetes, the operator ships with default denied fields that align
with the Pod Security Standards baseline profile.
On OpenShift, no fields are denied by default since Security Context Constraints (SCC)
already enforce security policies at the admission level.

**Important:** Configuring `deniedContainerOverrideFields` or `deniedPodOverrideFields`
**replaces** the platform defaults entirely. Admins who want to extend the default
deny list must re-include the default entries alongside any additional restrictions.

**Limitation for plain boolean fields in pod overrides:**
Some `PodSpec` fields such as `hostNetwork`, `hostPID`, and `hostIPC` are plain `bool`
types in the Kubernetes API (not `*bool` pointers). Because Go zero-initializes
unset `bool` fields to `false`, the operator cannot distinguish between a field that
was explicitly set to `false` and one that was simply omitted. As a result, using the
bare field name format (e.g. `"hostNetwork"`) to deny these fields entirely will reject
**all** pod overrides, including those that never mention the field. To avoid this,
use the value-specific format instead (e.g. `"hostNetwork=true"`). The default denied
fields already follow this pattern. This limitation does not affect `*bool` pointer
fields (e.g. `automountServiceAccountToken`, `shareProcessNamespace`, `hostUsers`)
or non-boolean fields.

For example, on Kubernetes, to add `volumeMounts` and `lifecycle` restrictions
while keeping the default denied container fields:

```yaml
apiVersion: controller.devfile.io/v1alpha1
kind: DevWorkspaceOperatorConfig
metadata:
name: devworkspace-operator-config
config:
workspace:
overrides:
deniedContainerOverrideFields:
Comment thread
tolusha marked this conversation as resolved.
# Default Kubernetes denied fields (must be re-listed to retain them)
- "securityContext.privileged=true"
- "securityContext.runAsNonRoot=false"
- "securityContext.runAsUser=0"
- "securityContext.allowPrivilegeEscalation=true"
- "securityContext.procMount=Unmasked"
- "securityContext.capabilities.add"
# Additional restrictions
- "volumeMounts"
- "lifecycle"
```

Similarly, to extend the default denied pod override fields on Kubernetes:

```yaml
apiVersion: controller.devfile.io/v1alpha1
kind: DevWorkspaceOperatorConfig
metadata:
name: devworkspace-operator-config
config:
workspace:
overrides:
deniedPodOverrideFields:
# Default Kubernetes denied fields (must be re-listed to retain them)
- "hostNetwork=true"
- "hostPID=true"
- "hostIPC=true"
- "securityContext.runAsNonRoot=false"
- "securityContext.runAsUser=0"
- "volumes.hostPath"
# Additional restrictions
- "hostUsers=false"
```
3 changes: 2 additions & 1 deletion pkg/config/common_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2019-2025 Red Hat, Inc.
// Copyright (c) 2019-2026 Red Hat, Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Expand Down Expand Up @@ -56,6 +56,7 @@ func setupForTest(t *testing.T) {
infrastructure.InitializeForTesting(infrastructure.Kubernetes)
setDefaultPodSecurityContext()
setDefaultContainerSecurityContext()
setDefaultOverrideConfig()
configNamespace = testNamespace
originalDefaultConfig := defaultConfig.DeepCopy()
t.Cleanup(func() {
Expand Down
Loading
Loading