From 650cce5d76a625629653ab54199ca3da15380d8e Mon Sep 17 00:00:00 2001 From: David Morgan Date: Tue, 6 Jul 2021 13:56:56 -0400 Subject: [PATCH] Custom security contexts implementation Allow setting custom security contexts for both contour deployments and envoy daemonsets. The default value is unpriviledged and is equivalent to the following: contourSecurityContext: runAsUser: 65534 runAsGroup: 65534 runAsNonRoot: true Fixes #112 Updates #378 --- api/v1alpha1/helpers.go | 8 +++++ internal/objects/daemonset/daemonset.go | 7 +++- internal/objects/daemonset/daemonset_test.go | 34 +++++++++++++++++++ internal/objects/deployment/deployment.go | 7 +++- .../objects/deployment/deployment_test.go | 33 ++++++++++++++++++ 5 files changed, 87 insertions(+), 2 deletions(-) diff --git a/api/v1alpha1/helpers.go b/api/v1alpha1/helpers.go index b99dcb3c..9339e299 100644 --- a/api/v1alpha1/helpers.go +++ b/api/v1alpha1/helpers.go @@ -95,3 +95,11 @@ func (c *Contour) EnvoyTolerationsExist() bool { return false } + +func (c *Contour) ContourSecurityContextExists() bool { + return c.Spec.ContourSecurityContext != nil +} + +func (c *Contour) EnvoySecurityContextExists() bool { + return c.Spec.EnvoySecurityContext != nil +} diff --git a/internal/objects/daemonset/daemonset.go b/internal/objects/daemonset/daemonset.go index 895e0be7..b62347a3 100644 --- a/internal/objects/daemonset/daemonset.go +++ b/internal/objects/daemonset/daemonset.go @@ -341,7 +341,6 @@ func DesiredDaemonSet(contour *operatorv1alpha1.Contour, contourImage, envoyImag DeprecatedServiceAccount: EnvoyContainerName, AutomountServiceAccountToken: pointer.BoolPtr(false), TerminationGracePeriodSeconds: pointer.Int64Ptr(int64(300)), - SecurityContext: &corev1.PodSecurityContext{}, DNSPolicy: corev1.DNSClusterFirst, RestartPolicy: corev1.RestartPolicyAlways, SchedulerName: "default-scheduler", @@ -358,6 +357,12 @@ func DesiredDaemonSet(contour *operatorv1alpha1.Contour, contourImage, envoyImag ds.Spec.Template.Spec.Tolerations = contour.Spec.NodePlacement.Envoy.Tolerations } + if contour.EnvoySecurityContextExists() { + ds.Spec.Template.Spec.SecurityContext = contour.Spec.EnvoySecurityContext + } else { + ds.Spec.Template.Spec.SecurityContext = objutil.NewUnprivilegedPodSecurity() + } + return ds } diff --git a/internal/objects/daemonset/daemonset_test.go b/internal/objects/daemonset/daemonset_test.go index 410a49c7..d73e1a92 100644 --- a/internal/objects/daemonset/daemonset_test.go +++ b/internal/objects/daemonset/daemonset_test.go @@ -18,12 +18,14 @@ import ( "testing" operatorv1alpha1 "github.com/projectcontour/contour-operator/api/v1alpha1" + objutil "github.com/projectcontour/contour-operator/internal/objects" objcontour "github.com/projectcontour/contour-operator/internal/objects/contour" "github.com/projectcontour/contour-operator/internal/operator/config" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/utils/pointer" ) func checkDaemonSetHasEnvVar(t *testing.T, ds *appsv1.DaemonSet, container, name string) { @@ -136,6 +138,16 @@ func checkDaemonSetHasTolerations(t *testing.T, ds *appsv1.DaemonSet, expected [ t.Errorf("deployment has unexpected tolerations %v", expected) } +func checkDaemonSetHasSecurityContext(t *testing.T, ds *appsv1.DaemonSet, expected *corev1.PodSecurityContext) { + t.Helper() + + if apiequality.Semantic.DeepEqual(ds.Spec.Template.Spec.SecurityContext, expected) { + return + } + + t.Errorf("deployment has unexpected security context %v", expected) +} + func TestDesiredDaemonSet(t *testing.T) { name := "ds-test" cfg := objcontour.Config{ @@ -164,6 +176,7 @@ func TestDesiredDaemonSet(t *testing.T) { } checkDaemonSetHasNodeSelector(t, ds, nil) checkDaemonSetHasTolerations(t, ds, nil) + checkDaemonSetHasSecurityContext(t, ds, objutil.NewUnprivilegedPodSecurity()) } func TestNodePlacementDaemonSet(t *testing.T) { @@ -198,3 +211,24 @@ func TestNodePlacementDaemonSet(t *testing.T) { checkDaemonSetHasNodeSelector(t, ds, selectors) checkDaemonSetHasTolerations(t, ds, tolerations) } + +func TestSecurityContextDaemonSet(t *testing.T) { + name := "security-context-test" + sc := &corev1.PodSecurityContext{ + RunAsUser: pointer.Int64(int64(0)), + } + cfg := objcontour.Config{ + Name: name, + Namespace: fmt.Sprintf("%s-ns", name), + SpecNs: "projectcontour", + RemoveNs: false, + NetworkType: operatorv1alpha1.LoadBalancerServicePublishingType, + } + cntr := objcontour.New(cfg) + cntr.Spec.EnvoySecurityContext = sc + + testContourImage := config.DefaultContourImage + testEnvoyImage := config.DefaultEnvoyImage + ds := DesiredDaemonSet(cntr, testContourImage, testEnvoyImage) + checkDaemonSetHasSecurityContext(t, ds, sc) +} diff --git a/internal/objects/deployment/deployment.go b/internal/objects/deployment/deployment.go index 2ee216d7..baa6b866 100644 --- a/internal/objects/deployment/deployment.go +++ b/internal/objects/deployment/deployment.go @@ -304,7 +304,6 @@ func DesiredDeployment(contour *operatorv1alpha1.Contour, image string) *appsv1. ServiceAccountName: objutil.ContourRbacName, RestartPolicy: corev1.RestartPolicyAlways, SchedulerName: "default-scheduler", - SecurityContext: objutil.NewUnprivilegedPodSecurity(), TerminationGracePeriodSeconds: pointer.Int64Ptr(int64(30)), }, }, @@ -319,6 +318,12 @@ func DesiredDeployment(contour *operatorv1alpha1.Contour, image string) *appsv1. deploy.Spec.Template.Spec.Tolerations = contour.Spec.NodePlacement.Contour.Tolerations } + if contour.ContourSecurityContextExists() { + deploy.Spec.Template.Spec.SecurityContext = contour.Spec.ContourSecurityContext + } else { + deploy.Spec.Template.Spec.SecurityContext = objutil.NewUnprivilegedPodSecurity() + } + return deploy } diff --git a/internal/objects/deployment/deployment_test.go b/internal/objects/deployment/deployment_test.go index 336f3aa3..f8a3c99a 100644 --- a/internal/objects/deployment/deployment_test.go +++ b/internal/objects/deployment/deployment_test.go @@ -18,6 +18,7 @@ import ( "testing" operatorv1alpha1 "github.com/projectcontour/contour-operator/api/v1alpha1" + objutil "github.com/projectcontour/contour-operator/internal/objects" objcontour "github.com/projectcontour/contour-operator/internal/objects/contour" objcfg "github.com/projectcontour/contour-operator/internal/objects/sharedconfig" "github.com/projectcontour/contour-operator/internal/operator/config" @@ -25,6 +26,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/utils/pointer" ) func checkDeploymentHasEnvVar(t *testing.T, deploy *appsv1.Deployment, name string) { @@ -107,6 +109,16 @@ func checkDeploymentHasTolerations(t *testing.T, deploy *appsv1.Deployment, expe t.Errorf("deployment has unexpected tolerations %v", expected) } +func checkDeploymentHasSecurityContext(t *testing.T, deploy *appsv1.Deployment, expected *corev1.PodSecurityContext) { + t.Helper() + + if apiequality.Semantic.DeepEqual(deploy.Spec.Template.Spec.SecurityContext, expected) { + return + } + + t.Errorf("deployment has unexpected security context %v", expected) +} + func TestDesiredDeployment(t *testing.T) { name := "deploy-test" cfg := objcontour.Config{ @@ -155,6 +167,7 @@ func TestDesiredDeployment(t *testing.T) { checkContainerHasArg(t, container, arg) checkDeploymentHasNodeSelector(t, deploy, nil) checkDeploymentHasTolerations(t, deploy, nil) + checkDeploymentHasSecurityContext(t, deploy, objutil.NewUnprivilegedPodSecurity()) } func TestNodePlacementDeployment(t *testing.T) { @@ -189,3 +202,23 @@ func TestNodePlacementDeployment(t *testing.T) { checkDeploymentHasNodeSelector(t, deploy, selectors) checkDeploymentHasTolerations(t, deploy, tolerations) } + +func TestSecurityContextDeployment(t *testing.T) { + name := "security-context-test" + sc := &corev1.PodSecurityContext{ + RunAsUser: pointer.Int64(int64(0)), + } + cfg := objcontour.Config{ + Name: name, + Namespace: fmt.Sprintf("%s-ns", name), + SpecNs: "projectcontour", + RemoveNs: false, + NetworkType: operatorv1alpha1.LoadBalancerServicePublishingType, + } + cntr := objcontour.New(cfg) + cntr.Spec.ContourSecurityContext = sc + + testContourImage := config.DefaultContourImage + ds := DesiredDeployment(cntr, testContourImage) + checkDeploymentHasSecurityContext(t, ds, sc) +}