diff --git a/helm/coder/tests/chart_test.go b/helm/coder/tests/chart_test.go index a11d631a2f247..17678a85e0dec 100644 --- a/helm/coder/tests/chart_test.go +++ b/helm/coder/tests/chart_test.go @@ -125,6 +125,10 @@ var testCases = []testCase{ name: "partial_resources", expectedError: "", }, + { + name: "pod_securitycontext", + expectedError: "", + }, } type testCase struct { diff --git a/helm/coder/tests/testdata/pod_securitycontext.golden b/helm/coder/tests/testdata/pod_securitycontext.golden new file mode 100644 index 0000000000000..17f6272f3637b --- /dev/null +++ b/helm/coder/tests/testdata/pod_securitycontext.golden @@ -0,0 +1,208 @@ +--- +# Source: coder/templates/coder.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: {} + labels: + app.kubernetes.io/instance: release-name + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: coder + app.kubernetes.io/part-of: coder + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: coder-0.1.0 + name: coder + namespace: default +--- +# Source: coder/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: coder-workspace-perms + namespace: default +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: [""] + resources: ["persistentvolumeclaims"] + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +--- +# Source: coder/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: "coder" + namespace: default +subjects: + - kind: ServiceAccount + name: "coder" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: coder-workspace-perms +--- +# Source: coder/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: coder + namespace: default + labels: + helm.sh/chart: coder-0.1.0 + app.kubernetes.io/name: coder + app.kubernetes.io/instance: release-name + app.kubernetes.io/part-of: coder + app.kubernetes.io/version: "0.1.0" + app.kubernetes.io/managed-by: Helm + annotations: + {} +spec: + type: LoadBalancer + sessionAffinity: None + ports: + - name: "http" + port: 80 + targetPort: "http" + protocol: TCP + nodePort: + externalTrafficPolicy: "Cluster" + selector: + app.kubernetes.io/name: coder + app.kubernetes.io/instance: release-name +--- +# Source: coder/templates/coder.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: {} + labels: + app.kubernetes.io/instance: release-name + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: coder + app.kubernetes.io/part-of: coder + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: coder-0.1.0 + name: coder + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: release-name + app.kubernetes.io/name: coder + template: + metadata: + annotations: {} + labels: + app.kubernetes.io/instance: release-name + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: coder + app.kubernetes.io/part-of: coder + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: coder-0.1.0 + spec: + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchExpressions: + - key: app.kubernetes.io/instance + operator: In + values: + - coder + topologyKey: kubernetes.io/hostname + weight: 1 + containers: + - args: + - server + command: + - /opt/coder + env: + - name: CODER_HTTP_ADDRESS + value: 0.0.0.0:8080 + - name: CODER_PROMETHEUS_ADDRESS + value: 0.0.0.0:2112 + - name: CODER_ACCESS_URL + value: http://coder.default.svc.cluster.local + - name: KUBE_POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: CODER_DERP_SERVER_RELAY_URL + value: http://$(KUBE_POD_IP):8080 + image: ghcr.io/coder/coder:latest + imagePullPolicy: IfNotPresent + lifecycle: {} + livenessProbe: + httpGet: + path: /healthz + port: http + scheme: HTTP + initialDelaySeconds: 0 + name: coder + ports: + - containerPort: 8080 + name: http + protocol: TCP + readinessProbe: + httpGet: + path: /healthz + port: http + scheme: HTTP + initialDelaySeconds: 0 + resources: + limits: + cpu: 2000m + memory: 4096Mi + requests: + cpu: 2000m + memory: 4096Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: null + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault + volumeMounts: [] + restartPolicy: Always + securityContext: + fsgroup: 1000 + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + serviceAccountName: coder + terminationGracePeriodSeconds: 60 + volumes: [] diff --git a/helm/coder/tests/testdata/pod_securitycontext.yaml b/helm/coder/tests/testdata/pod_securitycontext.yaml new file mode 100644 index 0000000000000..ba0a2ba37f952 --- /dev/null +++ b/helm/coder/tests/testdata/pod_securitycontext.yaml @@ -0,0 +1,8 @@ +coder: + image: + tag: latest + podSecurityContext: + fsgroup: 1000 + runAsUser: 1000 + runAsGroup: 1000 + runAsNonRoot: true diff --git a/helm/coder/tests/testdata/pod_securitycontext_coder.golden b/helm/coder/tests/testdata/pod_securitycontext_coder.golden new file mode 100644 index 0000000000000..c8d1ced840fc3 --- /dev/null +++ b/helm/coder/tests/testdata/pod_securitycontext_coder.golden @@ -0,0 +1,208 @@ +--- +# Source: coder/templates/coder.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: {} + labels: + app.kubernetes.io/instance: release-name + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: coder + app.kubernetes.io/part-of: coder + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: coder-0.1.0 + name: coder + namespace: coder +--- +# Source: coder/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: coder-workspace-perms + namespace: coder +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: [""] + resources: ["persistentvolumeclaims"] + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +--- +# Source: coder/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: "coder" + namespace: coder +subjects: + - kind: ServiceAccount + name: "coder" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: coder-workspace-perms +--- +# Source: coder/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: coder + namespace: coder + labels: + helm.sh/chart: coder-0.1.0 + app.kubernetes.io/name: coder + app.kubernetes.io/instance: release-name + app.kubernetes.io/part-of: coder + app.kubernetes.io/version: "0.1.0" + app.kubernetes.io/managed-by: Helm + annotations: + {} +spec: + type: LoadBalancer + sessionAffinity: None + ports: + - name: "http" + port: 80 + targetPort: "http" + protocol: TCP + nodePort: + externalTrafficPolicy: "Cluster" + selector: + app.kubernetes.io/name: coder + app.kubernetes.io/instance: release-name +--- +# Source: coder/templates/coder.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: {} + labels: + app.kubernetes.io/instance: release-name + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: coder + app.kubernetes.io/part-of: coder + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: coder-0.1.0 + name: coder + namespace: coder +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: release-name + app.kubernetes.io/name: coder + template: + metadata: + annotations: {} + labels: + app.kubernetes.io/instance: release-name + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: coder + app.kubernetes.io/part-of: coder + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: coder-0.1.0 + spec: + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchExpressions: + - key: app.kubernetes.io/instance + operator: In + values: + - coder + topologyKey: kubernetes.io/hostname + weight: 1 + containers: + - args: + - server + command: + - /opt/coder + env: + - name: CODER_HTTP_ADDRESS + value: 0.0.0.0:8080 + - name: CODER_PROMETHEUS_ADDRESS + value: 0.0.0.0:2112 + - name: CODER_ACCESS_URL + value: http://coder.coder.svc.cluster.local + - name: KUBE_POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: CODER_DERP_SERVER_RELAY_URL + value: http://$(KUBE_POD_IP):8080 + image: ghcr.io/coder/coder:latest + imagePullPolicy: IfNotPresent + lifecycle: {} + livenessProbe: + httpGet: + path: /healthz + port: http + scheme: HTTP + initialDelaySeconds: 0 + name: coder + ports: + - containerPort: 8080 + name: http + protocol: TCP + readinessProbe: + httpGet: + path: /healthz + port: http + scheme: HTTP + initialDelaySeconds: 0 + resources: + limits: + cpu: 2000m + memory: 4096Mi + requests: + cpu: 2000m + memory: 4096Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: null + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault + volumeMounts: [] + restartPolicy: Always + securityContext: + fsgroup: 1000 + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + serviceAccountName: coder + terminationGracePeriodSeconds: 60 + volumes: [] diff --git a/helm/coder/values.yaml b/helm/coder/values.yaml index fa6cb2c3622f8..fcc8f7746b0f1 100644 --- a/helm/coder/values.yaml +++ b/helm/coder/values.yaml @@ -142,6 +142,38 @@ coder: # root. It is recommended to leave this setting disabled in production. allowPrivilegeEscalation: false + # coder.podSecurityContext -- Pod-level security context settings that apply + # to all containers in the pod. This is useful for setting volume ownership + # (fsGroup) when mounting secrets like TLS certificates. These settings are + # applied at the pod level, while coder.securityContext applies at the + # container level. Container-level settings take precedence over pod-level + # settings for overlapping fields. This is opt-in and not set by default. + # Common use case: Set fsGroup to ensure mounted secret volumes have correct + # group ownership for the coder user to read certificate files. + podSecurityContext: {} + # Example configuration for certificate mounting: + # podSecurityContext: + # # Sets group ownership of mounted volumes (e.g., for certificate secrets) + # fsGroup: 1000 + # # Additional pod-level security settings (optional) + # runAsUser: 1000 + # runAsGroup: 1000 + # runAsNonRoot: true + # supplementalGroups: [4000] + # seccompProfile: + # type: RuntimeDefault + # # Note: Avoid conflicts with container-level securityContext settings + # # Container-level settings take precedence over pod-level settings + # + # IMPORTANT: OpenShift Compatibility + # On OpenShift, Security Context Constraints (SCCs) may restrict or override + # these values. If you encounter pod creation failures: + # 1. Check your namespace's assigned SCC with: oc describe scc + # 2. Ensure runAsUser/fsGroup values are within allowed UID/GID ranges + # 3. Consider using 'anyuid' SCC for more flexibility, or + # 4. Omit runAsUser/runAsGroup and only set fsGroup for volume ownership + # 5. OpenShift may automatically assign compatible values if left unset + # coder.volumes -- A list of extra volumes to add to the Coder pod. volumes: [] # - name: "my-volume" @@ -159,6 +191,10 @@ coder: # Helm deployment and should be of type "kubernetes.io/tls". The secrets # will be automatically mounted into the pod if specified, and the correct # "CODER_TLS_*" environment variables will be set for you. + + # Note: If you encounter permission issues reading mounted certificates, + # consider setting coder.podSecurityContext.fsGroup to match your container + # user (typically 1000) to ensure proper file ownership. secretNames: [] # coder.replicaCount -- The number of Kubernetes deployment replicas. This diff --git a/helm/libcoder/templates/_coder.yaml b/helm/libcoder/templates/_coder.yaml index b836bdf1df77f..6001df90d6580 100644 --- a/helm/libcoder/templates/_coder.yaml +++ b/helm/libcoder/templates/_coder.yaml @@ -26,6 +26,10 @@ spec: {{- toYaml .Values.coder.podAnnotations | nindent 8 }} spec: serviceAccountName: {{ .Values.coder.serviceAccount.name | quote }} + {{- with .Values.coder.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} restartPolicy: Always {{- with .Values.coder.image.pullSecrets }} imagePullSecrets:
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: