chore: declare pod securityContext explicitly (#1458)

* feat: ensure pod security standard

Signed-off-by: Anatolii Bazko <abazko@redhat.com>
pull/1464/head
Anatolii Bazko 2022-08-01 10:00:42 +03:00 committed by GitHub
parent 8117846f13
commit 152c8211a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 104 additions and 89 deletions

View File

@ -53,17 +53,10 @@ RUN export ARCH="$(uname -m)" && if [[ ${ARCH} == "x86_64" ]]; then export ARCH=
# https://registry.access.redhat.com/ubi8-minimal
FROM registry.access.redhat.com/ubi8-minimal:8.6-854
# install httpd-tools for /usr/bin/htpasswd
RUN microdnf install -y httpd-tools && microdnf -y update && microdnf -y clean all && rm -rf /var/cache/yum && echo "Installed Packages" && rpm -qa | sort -V && echo "End Of Installed Packages" && \
mkdir ~/.ssh && chmod 0766 ~/.ssh
COPY --from=builder /tmp/devworkspace-operator/templates /tmp/devworkspace-operator/templates
COPY --from=builder /tmp/header-rewrite-traefik-plugin /tmp/header-rewrite-traefik-plugin
COPY --from=builder /che-operator/che-operator /manager
WORKDIR /
USER 65532:65532
ENTRYPOINT ["/manager"]
# append Brew metadata here

View File

@ -475,7 +475,7 @@ create-checluster-cr: ## Creates CheCluster Custom Resource V2
# Update networking.domain field with an actual value
if [[ $(PLATFORM) == "kubernetes" ]]; then
# kubectl does not have `whoami` command
CLUSTER_API_URL=$$($(K8S_CLI) whoami --show-server=true) || true;
CLUSTER_API_URL=$$(oc whoami --show-server=true) || true;
CLUSTER_DOMAIN=$$(echo $${CLUSTER_API_URL} | sed -E 's/https:\/\/(.*):.*/\1/g')
yq -riY '.spec.networking.domain = "'$${CLUSTER_DOMAIN}'.nip.io"' $${CHECLUSTER_CR_2_APPLY}
fi
@ -574,8 +574,6 @@ bundle: generate manifests download-kustomize download-operator-sdk ## Generate
yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec."hostIPC") = false' "$${CSV_PATH}"
yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec."hostNetwork") = false' "$${CSV_PATH}"
yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec."hostPID") = false' "$${CSV_PATH}"
yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec.containers[0].securityContext."allowPrivilegeEscalation") = false' "$${CSV_PATH}"
yq -riSY '(.spec.install.spec.deployments[0].spec.template.spec.containers[0].securityContext."runAsNonRoot") = true' "$${CSV_PATH}"
# Fix examples by removing some special characters
FIXED_ALM_EXAMPLES=$$(yq -r '.metadata.annotations["alm-examples"]' $${CSV_PATH} | sed -r 's/"/\\"/g')

View File

@ -76,7 +76,7 @@ metadata:
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
repository: https://github.com/eclipse-che/che-operator
support: Eclipse Foundation
name: eclipse-che-preview-openshift.v7.52.0-627.next
name: eclipse-che-preview-openshift.v7.52.0-639.next
namespace: placeholder
spec:
apiservicedefinitions: {}
@ -1390,7 +1390,7 @@ spec:
maturity: stable
provider:
name: Eclipse Foundation
version: 7.52.0-627.next
version: 7.52.0-639.next
webhookdefinitions:
- admissionReviewVersions:
- v1

View File

@ -21,6 +21,7 @@ resources:
# Kubernetes platform specific patches
patchesStrategicMerge:
- patches/cainjection_in_checlusters.yaml
- patches/manager_pod_security_context.yaml
vars:
- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR

View File

@ -0,0 +1,23 @@
#
# Copyright (c) 2019-2021 Red Hat, Inc.
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# Red Hat, Inc. - initial API and implementation
#
apiVersion: apps/v1
kind: Deployment
metadata:
name: che-operator
namespace: eclipse-che
spec:
template:
spec:
securityContext:
runAsUser: 1724
fsGroup: 1724

View File

@ -125,6 +125,8 @@ spec:
timeoutSeconds: 5
securityContext:
privileged: false
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
capabilities:
drop:

View File

@ -4651,11 +4651,13 @@ spec:
cpu: 100m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsNonRoot: true
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: webhook-tls-certs
@ -4664,6 +4666,9 @@ spec:
hostNetwork: false
hostPID: false
restartPolicy: Always
securityContext:
fsGroup: 1724
runAsUser: 1724
serviceAccountName: che-operator
terminationGracePeriodSeconds: 20
volumes:

View File

@ -134,11 +134,13 @@ spec:
cpu: 100m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsNonRoot: true
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: webhook-tls-certs
@ -147,6 +149,9 @@ spec:
hostNetwork: false
hostPID: false
restartPolicy: Always
securityContext:
fsGroup: 1724
runAsUser: 1724
serviceAccountName: che-operator
terminationGracePeriodSeconds: 20
volumes:

View File

@ -4653,11 +4653,13 @@ spec:
cpu: 100m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsNonRoot: true
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: webhook-tls-certs

View File

@ -134,11 +134,13 @@ spec:
cpu: 100m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsNonRoot: true
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: webhook-tls-certs

View File

@ -134,11 +134,13 @@ spec:
cpu: 100m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsNonRoot: true
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: webhook-tls-certs
@ -147,6 +149,9 @@ spec:
hostNetwork: false
hostPID: false
restartPolicy: Always
securityContext:
fsGroup: 1724
runAsUser: 1724
serviceAccountName: che-operator
terminationGracePeriodSeconds: 20
volumes:

View File

@ -17,6 +17,9 @@ import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/utils/pointer"
chev2 "github.com/eclipse-che/che-operator/api/v2"
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
console "github.com/openshift/api/console/v1"
@ -79,9 +82,9 @@ func CompareResources(actualDeployment *appsv1.Deployment, expected TestExpected
}
func ValidateSecurityContext(actualDeployment *appsv1.Deployment, t *testing.T) {
if actualDeployment.Spec.Template.Spec.Containers[0].SecurityContext.Capabilities.Drop[0] != "ALL" {
t.Error("Deployment doesn't contain 'Capabilities Drop ALL' in a SecurityContext")
}
assert.Equal(t, corev1.Capability("ALL"), actualDeployment.Spec.Template.Spec.Containers[0].SecurityContext.Capabilities.Drop[0])
assert.Equal(t, pointer.BoolPtr(true), actualDeployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsNonRoot)
assert.Equal(t, pointer.BoolPtr(false), actualDeployment.Spec.Template.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation)
}
func compareQuantity(resource string, actualQuantity *resource.Quantity, expected string, t *testing.T) {

View File

@ -17,20 +17,18 @@ import (
"fmt"
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
configv1 "github.com/openshift/api/config/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/pointer"
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
"github.com/eclipse-che/che-operator/pkg/common/constants"
defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults"
"github.com/eclipse-che/che-operator/pkg/common/utils"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/deploy/tls"
configv1 "github.com/openshift/api/config/v1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
)
@ -131,11 +129,6 @@ func (d *DashboardReconciler) getDashboardDeploymentSpec(ctx *chetypes.DeployCon
corev1.ResourceCPU: resource.MustParse(constants.DefaultDashboardCpuLimit),
},
},
SecurityContext: &corev1.SecurityContext{
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
},
},
ReadinessProbe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
@ -182,14 +175,8 @@ func (d *DashboardReconciler) getDashboardDeploymentSpec(ctx *chetypes.DeployCon
},
}
if !infrastructure.IsOpenShift() {
deployment.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{
RunAsUser: pointer.Int64Ptr(constants.DefaultSecurityContextRunAsUser),
FSGroup: pointer.Int64Ptr(constants.DefaultSecurityContextFsGroup),
}
}
deploy.CustomizeDeployment(deployment, ctx.CheCluster.Spec.Components.Dashboard.Deployment, true)
deploy.EnsurePodSecurityStandards(deployment, constants.DefaultSecurityContextRunAsUser, constants.DefaultSecurityContextFsGroup)
deploy.CustomizeDeployment(deployment, ctx.CheCluster.Spec.Components.Dashboard.Deployment)
return deployment, nil
}

View File

@ -97,7 +97,7 @@ func SyncDeploymentSpecToCluster(
}
// CustomizeDeployment customize deployment
func CustomizeDeployment(deployment *appsv1.Deployment, customDeployment *chev2.Deployment, useCustomSecurityContext bool) error {
func CustomizeDeployment(deployment *appsv1.Deployment, customDeployment *chev2.Deployment) error {
if customDeployment == nil || len(customDeployment.Containers) == 0 {
return nil
}
@ -143,15 +143,16 @@ func CustomizeDeployment(deployment *appsv1.Deployment, customDeployment *chev2.
}
if !infrastructure.IsOpenShift() {
if useCustomSecurityContext {
if customDeployment.SecurityContext != nil {
if customDeployment.SecurityContext.FsGroup != nil {
deployment.Spec.Template.Spec.SecurityContext.FSGroup = pointer.Int64Ptr(*customDeployment.SecurityContext.FsGroup)
}
if customDeployment.SecurityContext != nil {
if deployment.Spec.Template.Spec.SecurityContext == nil {
deployment.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{}
}
if customDeployment.SecurityContext.RunAsUser != nil {
deployment.Spec.Template.Spec.SecurityContext.RunAsUser = pointer.Int64Ptr(*customDeployment.SecurityContext.RunAsUser)
}
if customDeployment.SecurityContext.FsGroup != nil {
deployment.Spec.Template.Spec.SecurityContext.FSGroup = pointer.Int64Ptr(*customDeployment.SecurityContext.FsGroup)
}
if customDeployment.SecurityContext.RunAsUser != nil {
deployment.Spec.Template.Spec.SecurityContext.RunAsUser = pointer.Int64Ptr(*customDeployment.SecurityContext.RunAsUser)
}
}
}
@ -159,11 +160,25 @@ func CustomizeDeployment(deployment *appsv1.Deployment, customDeployment *chev2.
return nil
}
func getOrDefaultQuantity(value resource.Quantity, defaultValue resource.Quantity) resource.Quantity {
if !value.IsZero() {
return value
// EnsurePodSecurityStandards sets SecurityContext accordingly
// to standards https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted
func EnsurePodSecurityStandards(deployment *appsv1.Deployment, userId int64, groupId int64) {
for i, _ := range deployment.Spec.Template.Spec.Containers {
if deployment.Spec.Template.Spec.Containers[i].SecurityContext == nil {
deployment.Spec.Template.Spec.Containers[i].SecurityContext = &corev1.SecurityContext{}
}
deployment.Spec.Template.Spec.Containers[i].SecurityContext.RunAsNonRoot = pointer.BoolPtr(true)
deployment.Spec.Template.Spec.Containers[i].SecurityContext.AllowPrivilegeEscalation = pointer.BoolPtr(false)
deployment.Spec.Template.Spec.Containers[i].SecurityContext.Capabilities = &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}}
}
if !infrastructure.IsOpenShift() {
if deployment.Spec.Template.Spec.SecurityContext == nil {
deployment.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{}
}
deployment.Spec.Template.Spec.SecurityContext.RunAsUser = pointer.Int64Ptr(userId)
deployment.Spec.Template.Spec.SecurityContext.FSGroup = pointer.Int64Ptr(groupId)
}
return defaultValue
}
func getContainerByName(name string, containers []chev2.Container) *chev2.Container {

View File

@ -707,7 +707,7 @@ func TestCustomizeDeploymentShouldNotUpdateResources(t *testing.T) {
},
}
err := CustomizeDeployment(deployment, customizationDeployment, false)
err := CustomizeDeployment(deployment, customizationDeployment)
assert.Nil(t, err)
assert.Equal(t, "1", deployment.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu().String())
assert.Equal(t, "100Mi", deployment.Spec.Template.Spec.Containers[0].Resources.Requests.Memory().String())
@ -790,7 +790,7 @@ func TestCustomizeDeploymentImagePullPolicy(t *testing.T) {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
err := CustomizeDeployment(testCase.initDeployment, testCase.customizationDeployment, false)
err := CustomizeDeployment(testCase.initDeployment, testCase.customizationDeployment)
assert.Nil(t, err)
assert.Equal(t, testCase.expectedImagePullPolicy, testCase.initDeployment.Spec.Template.Spec.Containers[0].ImagePullPolicy)
})

View File

@ -203,6 +203,7 @@ func syncDwDeployment(deployContext *chetypes.DeployContext) (bool, error) {
}
}
deploy.EnsurePodSecurityStandards(deployment, constants.DefaultSecurityContextRunAsUser, constants.DefaultSecurityContextFsGroup)
return syncObject(deployContext, deployment, DevWorkspaceNamespace)
}

View File

@ -49,6 +49,8 @@ func (d *DevfileRegistryReconciler) getDevfileRegistryDeploymentSpec(ctx *chetyp
registryImagePullPolicy,
resources,
probePath)
deploy.CustomizeDeployment(deployment, ctx.CheCluster.Spec.Components.DevfileRegistry.Deployment, false)
deploy.EnsurePodSecurityStandards(deployment, constants.DefaultSecurityContextRunAsUser, constants.DefaultSecurityContextFsGroup)
deploy.CustomizeDeployment(deployment, ctx.CheCluster.Spec.Components.DevfileRegistry.Deployment)
return deployment
}

View File

@ -143,7 +143,7 @@ func syncAll(deployContext *chetypes.DeployContext) error {
}
depl := getGatewayDeploymentSpec(deployContext)
if _, err := deploy.Sync(deployContext, &depl, deploy.DefaultDeploymentDiffOpts); err != nil {
if _, err := deploy.Sync(deployContext, depl, deploy.DefaultDeploymentDiffOpts); err != nil {
// Failed to sync (update), let's delete and create instead
if strings.Contains(err.Error(), "field is immutable") {
if _, err := deploy.DeleteNamespacedObject(deployContext, depl.Name, &appsv1.Deployment{}); err != nil {
@ -424,12 +424,12 @@ experimental:
}
}
func getGatewayDeploymentSpec(ctx *chetypes.DeployContext) appsv1.Deployment {
func getGatewayDeploymentSpec(ctx *chetypes.DeployContext) *appsv1.Deployment {
terminationGracePeriodSeconds := int64(10)
deployLabels, labelsSelector := deploy.GetLabelsAndSelector(GatewayServiceName)
deployment := appsv1.Deployment{
deployment := &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: appsv1.SchemeGroupVersion.String(),
Kind: "Deployment",
@ -461,7 +461,8 @@ func getGatewayDeploymentSpec(ctx *chetypes.DeployContext) appsv1.Deployment {
},
}
deploy.CustomizeDeployment(&deployment, ctx.CheCluster.Spec.Networking.Auth.Gateway.Deployment, false)
deploy.EnsurePodSecurityStandards(deployment, constants.DefaultSecurityContextRunAsUser, constants.DefaultSecurityContextFsGroup)
deploy.CustomizeDeployment(deployment, ctx.CheCluster.Spec.Networking.Auth.Gateway.Deployment)
return deployment
}

View File

@ -50,6 +50,7 @@ func (p *PluginRegistryReconciler) getPluginRegistryDeploymentSpec(ctx *chetypes
resources,
probePath)
deploy.CustomizeDeployment(deployment, ctx.CheCluster.Spec.Components.PluginRegistry.Deployment, false)
deploy.EnsurePodSecurityStandards(deployment, constants.DefaultSecurityContextRunAsUser, constants.DefaultSecurityContextFsGroup)
deploy.CustomizeDeployment(deployment, ctx.CheCluster.Spec.Components.PluginRegistry.Deployment)
return deployment
}

View File

@ -15,7 +15,6 @@ import (
"fmt"
"strings"
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
chev2 "github.com/eclipse-che/che-operator/api/v2"
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
"github.com/eclipse-che/che-operator/pkg/common/constants"
@ -143,11 +142,6 @@ func (p *PostgresReconciler) getDeploymentSpec(clusterDeployment *appsv1.Deploym
PeriodSeconds: 10,
TimeoutSeconds: 5,
},
SecurityContext: &corev1.SecurityContext{
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
},
},
Env: []corev1.EnvVar{
{
Name: "POSTGRESQL_DATABASE",
@ -192,15 +186,8 @@ func (p *PostgresReconciler) getDeploymentSpec(clusterDeployment *appsv1.Deploym
},
})
if !infrastructure.IsOpenShift() {
var runAsUser int64 = 26
deployment.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{
RunAsUser: &runAsUser,
FSGroup: &runAsUser,
}
}
deploy.CustomizeDeployment(deployment, ctx.CheCluster.Spec.Components.Database.Deployment, false)
deploy.EnsurePodSecurityStandards(deployment, 26, 26)
deploy.CustomizeDeployment(deployment, ctx.CheCluster.Spec.Components.Database.Deployment)
return deployment, nil
}

View File

@ -125,11 +125,6 @@ func GetSpecRegistryDeployment(
SuccessThreshold: 1,
PeriodSeconds: 10,
},
SecurityContext: &corev1.SecurityContext{
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
},
},
},
},
RestartPolicy: "Always",

View File

@ -12,7 +12,6 @@
package server
import (
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
"github.com/eclipse-che/che-operator/pkg/common/constants"
defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults"
@ -26,7 +25,6 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/pointer"
)
func (s CheServerReconciler) getDeploymentSpec(ctx *chetypes.DeployContext) (*appsv1.Deployment, error) {
@ -192,11 +190,6 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *chetypes.DeployContext) (*ap
corev1.ResourceCPU: resource.MustParse(constants.DefaultServerCpuLimit),
},
},
SecurityContext: &corev1.SecurityContext{
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
},
},
EnvFrom: []corev1.EnvFromSource{
{
ConfigMapRef: &corev1.ConfigMapEnvSource{
@ -299,13 +292,6 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *chetypes.DeployContext) (*ap
}
}
if !infrastructure.IsOpenShift() {
deployment.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{
RunAsUser: pointer.Int64Ptr(constants.DefaultSecurityContextRunAsUser),
FSGroup: pointer.Int64Ptr(constants.DefaultSecurityContextFsGroup),
}
}
if defaults.IsComponentReadinessInitContainersConfigured() {
if !ctx.CheCluster.Spec.Components.Database.ExternalDb {
waitForPostgresInitContainer, err := postgres.GetWaitForPostgresInitContainer(ctx)
@ -316,7 +302,8 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *chetypes.DeployContext) (*ap
}
}
deploy.CustomizeDeployment(deployment, ctx.CheCluster.Spec.Components.CheServer.Deployment, true)
deploy.EnsurePodSecurityStandards(deployment, constants.DefaultSecurityContextRunAsUser, constants.DefaultSecurityContextFsGroup)
deploy.CustomizeDeployment(deployment, ctx.CheCluster.Spec.Components.CheServer.Deployment)
return deployment, nil
}