diff --git a/pkg/render/apiserver.go b/pkg/render/apiserver.go index 54a030dab4..f9363c661c 100644 --- a/pkg/render/apiserver.go +++ b/pkg/render/apiserver.go @@ -34,9 +34,9 @@ import ( "github.com/tigera/operator/pkg/ptr" rmeta "github.com/tigera/operator/pkg/render/common/meta" "github.com/tigera/operator/pkg/render/common/podaffinity" - "github.com/tigera/operator/pkg/render/common/podsecuritycontext" "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" + "github.com/tigera/operator/pkg/render/common/securitycontext" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -943,7 +943,8 @@ func (c *apiServerComponent) queryServerContainer() corev1.Container { InitialDelaySeconds: 90, PeriodSeconds: 10, }, - SecurityContext: podsecuritycontext.NewBaseContext(), + // UID 1001 is used in the queryserver Dockerfile. + SecurityContext: securitycontext.NewBaseContext(1001, 0), VolumeMounts: volumeMounts, } return container diff --git a/pkg/render/apiserver_test.go b/pkg/render/apiserver_test.go index 5437ce20fd..131b5f2a35 100644 --- a/pkg/render/apiserver_test.go +++ b/pkg/render/apiserver_test.go @@ -194,7 +194,7 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { Expect(d.Spec.Template.Spec.Tolerations).To(ConsistOf(rmeta.TolerateMaster)) Expect(d.Spec.Template.Spec.ImagePullSecrets).To(BeEmpty()) - Expect(len(d.Spec.Template.Spec.Containers)).To(Equal(2)) + Expect(d.Spec.Template.Spec.Containers).To(HaveLen(2)) Expect(d.Spec.Template.Spec.Containers[0].Name).To(Equal("tigera-apiserver")) Expect(d.Spec.Template.Spec.Containers[0].Image).To(Equal( fmt.Sprintf("testregistry.com/%s:%s", components.ComponentAPIServer.Image, components.ComponentAPIServer.Version), @@ -267,6 +267,12 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { Expect(d.Spec.Template.Spec.Containers[1].LivenessProbe.InitialDelaySeconds).To(BeEquivalentTo(90)) Expect(d.Spec.Template.Spec.Containers[1].LivenessProbe.PeriodSeconds).To(BeEquivalentTo(10)) + Expect(*d.Spec.Template.Spec.Containers[1].SecurityContext.AllowPrivilegeEscalation).To(BeFalse()) + Expect(*d.Spec.Template.Spec.Containers[1].SecurityContext.Privileged).To(BeFalse()) + Expect(*d.Spec.Template.Spec.Containers[1].SecurityContext.RunAsGroup).To(BeEquivalentTo(0)) + Expect(*d.Spec.Template.Spec.Containers[1].SecurityContext.RunAsNonRoot).To(BeTrue()) + Expect(*d.Spec.Template.Spec.Containers[1].SecurityContext.RunAsUser).To(BeEquivalentTo(1001)) + Expect(len(d.Spec.Template.Spec.Volumes)).To(Equal(3)) Expect(d.Spec.Template.Spec.Volumes[0].Name).To(Equal("tigera-apiserver-certs")) diff --git a/pkg/render/aws-securitygroup-setup.go b/pkg/render/aws-securitygroup-setup.go index 7024b9ae21..ba9948c1ac 100644 --- a/pkg/render/aws-securitygroup-setup.go +++ b/pkg/render/aws-securitygroup-setup.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Tigera, Inc. All rights reserved. +// Copyright (c) 2019,2022 Tigera, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ import ( "github.com/tigera/operator/pkg/common" "github.com/tigera/operator/pkg/components" rmeta "github.com/tigera/operator/pkg/render/common/meta" - "github.com/tigera/operator/pkg/render/common/podsecuritycontext" + "github.com/tigera/operator/pkg/render/common/securitycontext" ) func AWSSecurityGroupSetup(cfg *AWSSGSetupConfiguration) (Component, error) { @@ -110,7 +110,8 @@ func (c *awsSGSetupComponent) setupJob() *batchv1.Job { Value: "/etc/kubernetes/kubeconfig", }, }, - SecurityContext: podsecuritycontext.NewBaseContext(), + // UID 1001 is used in the operator Dockerfile. + SecurityContext: securitycontext.NewBaseContext(1001, 0), }}, }, }, diff --git a/pkg/render/aws-securitygroup-setup_test.go b/pkg/render/aws-securitygroup-setup_test.go new file mode 100644 index 0000000000..5789b42362 --- /dev/null +++ b/pkg/render/aws-securitygroup-setup_test.go @@ -0,0 +1,85 @@ +// Copyright (c) 2022 Tigera, Inc. All rights reserved. + +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package render + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + + operatorv1 "github.com/tigera/operator/api/v1" + rtest "github.com/tigera/operator/pkg/render/common/test" +) + +var _ = Describe("AWS SecurityGroup Setup rendering tests", func() { + var cfg *AWSSGSetupConfiguration + + BeforeEach(func() { + cfg = &AWSSGSetupConfiguration{ + PullSecrets: []corev1.LocalObjectReference{}, + Installation: &operatorv1.InstallationSpec{}, + } + }) + + It("should render AWS SecurityGroup Setup resources", func() { + component, err := AWSSecurityGroupSetup(cfg) + Expect(err).NotTo(HaveOccurred()) + Expect(component.ResolveImages(nil)).NotTo(HaveOccurred()) + + toCreate, toDelete := component.Objects() + + expectedResources := []struct { + name string + ns string + group string + version string + kind string + }{ + {"tigera-aws-security-group-setup", "tigera-operator", "", "v1", "ServiceAccount"}, + {"tigera-aws-security-group-setup", "kube-system", "rbac.authorization.k8s.io", "v1", "Role"}, + {"tigera-aws-security-group-setup", "kube-system", "rbac.authorization.k8s.io", "v1", "RoleBinding"}, + {"aws-security-group-setup-1", "tigera-operator", "batch", "v1", "Job"}, + } + + Expect(len(toCreate)).To(Equal(len(expectedResources))) + + for i, expectedRes := range expectedResources { + obj := toCreate[i] + rtest.ExpectResource(obj, expectedRes.name, expectedRes.ns, expectedRes.group, expectedRes.version, expectedRes.kind) + } + + Expect(toDelete).To(BeNil()) + }) + + It("should render Setup Job specs correctly", func() { + component, err := AWSSecurityGroupSetup(cfg) + Expect(err).NotTo(HaveOccurred()) + Expect(component.ResolveImages(nil)).NotTo(HaveOccurred()) + toCreate, _ := component.Objects() + + job, ok := rtest.GetResource(toCreate, "aws-security-group-setup-1", "tigera-operator", "batch", "v1", "Job").(*batchv1.Job) + Expect(ok).To(BeTrue()) + + Expect(job.Spec.Template.Spec.Containers).To(HaveLen(1)) + + Expect(*job.Spec.Template.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation).To(BeFalse()) + Expect(*job.Spec.Template.Spec.Containers[0].SecurityContext.Privileged).To(BeFalse()) + Expect(*job.Spec.Template.Spec.Containers[0].SecurityContext.RunAsGroup).To(BeEquivalentTo(0)) + Expect(*job.Spec.Template.Spec.Containers[0].SecurityContext.RunAsNonRoot).To(BeTrue()) + Expect(*job.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser).To(BeEquivalentTo(1001)) + }) +}) diff --git a/pkg/render/common/podsecuritycontext/pod_security_context.go b/pkg/render/common/podsecuritycontext/pod_security_context.go deleted file mode 100644 index 902474af68..0000000000 --- a/pkg/render/common/podsecuritycontext/pod_security_context.go +++ /dev/null @@ -1,15 +0,0 @@ -package podsecuritycontext - -import ( - "github.com/tigera/operator/pkg/ptr" - corev1 "k8s.io/api/core/v1" -) - -// NewBaseContext returns the non root non privileged security context that most of the containers running should -// be using. -func NewBaseContext() *corev1.SecurityContext { - return &corev1.SecurityContext{ - RunAsNonRoot: ptr.BoolToPtr(true), - AllowPrivilegeEscalation: ptr.BoolToPtr(false), - } -} diff --git a/pkg/render/common/securitycontext/security_context.go b/pkg/render/common/securitycontext/security_context.go new file mode 100644 index 0000000000..7846e7e86e --- /dev/null +++ b/pkg/render/common/securitycontext/security_context.go @@ -0,0 +1,42 @@ +// Copyright (c) 2021-2022 Tigera, Inc. All rights reserved. + +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package securitycontext + +import ( + corev1 "k8s.io/api/core/v1" + + "github.com/tigera/operator/pkg/ptr" +) + +var ( + // It is recommended to choose UID and GID that don't collide with existing system users and groups. + // Non-system UID and GID range is normally from 1000 to 60000 (Debian derived systems define this + // in /etc/login.defs). On a normal Linux host, it is unlikely to have more than 10k non-system users. + // 10001 is chosen based on this assumption. + RunAsUserID int64 = 10001 + RunAsGroupID int64 = 10001 +) + +// NewBaseContext returns the non root non privileged security context that most of the containers running should +// be using. +func NewBaseContext(uid, gid int64) *corev1.SecurityContext { + return &corev1.SecurityContext{ + AllowPrivilegeEscalation: ptr.BoolToPtr(false), + Privileged: ptr.BoolToPtr(false), + RunAsGroup: &gid, + RunAsNonRoot: ptr.BoolToPtr(true), + RunAsUser: &uid, + } +} diff --git a/pkg/render/dex.go b/pkg/render/dex.go index 26e62a51fa..cf8cf14157 100644 --- a/pkg/render/dex.go +++ b/pkg/render/dex.go @@ -24,8 +24,8 @@ import ( "github.com/tigera/operator/pkg/components" rmeta "github.com/tigera/operator/pkg/render/common/meta" "github.com/tigera/operator/pkg/render/common/podaffinity" - "github.com/tigera/operator/pkg/render/common/podsecuritycontext" "github.com/tigera/operator/pkg/render/common/secret" + "github.com/tigera/operator/pkg/render/common/securitycontext" "github.com/tigera/operator/pkg/tls/certificatemanagement" "gopkg.in/yaml.v2" @@ -216,11 +216,12 @@ func (c *dexComponent) deployment() client.Object { InitContainers: initContainers, Containers: []corev1.Container{ { - Name: DexObjectName, - Image: c.image, - Env: c.cfg.DexConfig.RequiredEnv(""), - LivenessProbe: c.probe(), - SecurityContext: podsecuritycontext.NewBaseContext(), + Name: DexObjectName, + Image: c.image, + Env: c.cfg.DexConfig.RequiredEnv(""), + LivenessProbe: c.probe(), + // UID and GID 1001:1001 are used in dex Dockerfile. + SecurityContext: securitycontext.NewBaseContext(1001, 1001), Command: []string{"/usr/local/bin/dex", "serve", "/etc/dex/baseCfg/config.yaml"}, diff --git a/pkg/render/dex_test.go b/pkg/render/dex_test.go index 767e655956..bbc8f97dca 100644 --- a/pkg/render/dex_test.go +++ b/pkg/render/dex_test.go @@ -146,6 +146,15 @@ var _ = Describe("dex rendering tests", func() { rtest.ExpectResource(resources[i], expectedRes.name, expectedRes.ns, expectedRes.group, expectedRes.version, expectedRes.kind) } Expect(len(resources)).To(Equal(len(expectedResources))) + + d := rtest.GetResource(resources, "tigera-dex", "tigera-dex", "apps", "v1", "Deployment").(*appsv1.Deployment) + + Expect(d.Spec.Template.Spec.Containers).To(HaveLen(1)) + Expect(*d.Spec.Template.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation).To(BeFalse()) + Expect(*d.Spec.Template.Spec.Containers[0].SecurityContext.Privileged).To(BeFalse()) + Expect(*d.Spec.Template.Spec.Containers[0].SecurityContext.RunAsGroup).To(BeEquivalentTo(1001)) + Expect(*d.Spec.Template.Spec.Containers[0].SecurityContext.RunAsNonRoot).To(BeTrue()) + Expect(*d.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser).To(BeEquivalentTo(1001)) }) DescribeTable("should render the cluster name properly in the validator", func(clusterDomain string) { diff --git a/pkg/render/guardian.go b/pkg/render/guardian.go index 8f89890e59..3cfad08dd2 100644 --- a/pkg/render/guardian.go +++ b/pkg/render/guardian.go @@ -27,8 +27,8 @@ import ( operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/components" rmeta "github.com/tigera/operator/pkg/render/common/meta" - "github.com/tigera/operator/pkg/render/common/podsecuritycontext" "github.com/tigera/operator/pkg/render/common/secret" + "github.com/tigera/operator/pkg/render/common/securitycontext" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -268,7 +268,8 @@ func (c *GuardianComponent) container() []corev1.Container { InitialDelaySeconds: 10, PeriodSeconds: 5, }, - SecurityContext: podsecuritycontext.NewBaseContext(), + // UID 1001 is used in the guardian Dockerfile. + SecurityContext: securitycontext.NewBaseContext(1001, 0), }, } } diff --git a/pkg/render/guardian_test.go b/pkg/render/guardian_test.go index 748deb775f..77fa29dee2 100644 --- a/pkg/render/guardian_test.go +++ b/pkg/render/guardian_test.go @@ -113,7 +113,14 @@ var _ = Describe("Rendering tests", func() { } deployment := rtest.GetResource(resources, render.GuardianDeploymentName, render.GuardianNamespace, "apps", "v1", "Deployment").(*appsv1.Deployment) + Expect(deployment.Spec.Template.Spec.Containers).To(HaveLen(1)) Expect(deployment.Spec.Template.Spec.Containers[0].Image).Should(Equal("my-reg/tigera/guardian:" + components.ComponentGuardian.Version)) + + Expect(*deployment.Spec.Template.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation).To(BeFalse()) + Expect(*deployment.Spec.Template.Spec.Containers[0].SecurityContext.Privileged).To(BeFalse()) + Expect(*deployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsGroup).To(BeEquivalentTo(0)) + Expect(*deployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsNonRoot).To(BeTrue()) + Expect(*deployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser).To(BeEquivalentTo(1001)) }) It("should render controlPlaneTolerations", func() { diff --git a/pkg/render/intrusion_detection.go b/pkg/render/intrusion_detection.go index a7cfb48a21..76f9ac4e59 100644 --- a/pkg/render/intrusion_detection.go +++ b/pkg/render/intrusion_detection.go @@ -34,11 +34,13 @@ import ( operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/components" "github.com/tigera/operator/pkg/dns" + "github.com/tigera/operator/pkg/ptr" relasticsearch "github.com/tigera/operator/pkg/render/common/elasticsearch" rkibana "github.com/tigera/operator/pkg/render/common/kibana" rmeta "github.com/tigera/operator/pkg/render/common/meta" "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" + "github.com/tigera/operator/pkg/render/common/securitycontext" "github.com/tigera/operator/pkg/tls/certificatemanagement" "github.com/tigera/operator/pkg/url" ) @@ -543,7 +545,7 @@ func (c *intrusionDetectionComponent) intrusionDetectionControllerContainer() co }, } - privileged := false + sc := securitycontext.NewBaseContext(securitycontext.RunAsUserID, securitycontext.RunAsGroupID) // If syslog forwarding is enabled then set the necessary ENV var and volume mount to // write logs for Fluentd. @@ -558,7 +560,7 @@ func (c *intrusionDetectionComponent) intrusionDetectionControllerContainer() co // On OpenShift, if we need the volume mount to hostpath volume for syslog forwarding, // then ID controller needs privileged access to write event logs to that volume if c.cfg.Openshift { - privileged = true + sc.Privileged = ptr.BoolToPtr(true) } } @@ -578,10 +580,8 @@ func (c *intrusionDetectionComponent) intrusionDetectionControllerContainer() co }, InitialDelaySeconds: 5, }, - SecurityContext: &corev1.SecurityContext{ - Privileged: &privileged, - }, - VolumeMounts: volumeMounts, + SecurityContext: sc, + VolumeMounts: volumeMounts, } } diff --git a/pkg/render/intrusion_detection_test.go b/pkg/render/intrusion_detection_test.go index fb11d379b5..6a833fee66 100644 --- a/pkg/render/intrusion_detection_test.go +++ b/pkg/render/intrusion_detection_test.go @@ -143,17 +143,26 @@ var _ = Describe("Intrusion Detection rendering tests", func() { // Should mount ManagerTLSSecret for non-managed clusters idc := rtest.GetResource(resources, "intrusion-detection-controller", render.IntrusionDetectionNamespace, "apps", "v1", "Deployment").(*appsv1.Deployment) idji := rtest.GetResource(resources, "intrusion-detection-es-job-installer", render.IntrusionDetectionNamespace, "batch", "v1", "Job").(*batchv1.Job) + Expect(idc.Spec.Template.Spec.Containers).To(HaveLen(1)) Expect(idc.Spec.Template.Spec.Containers[0].Env).Should(ContainElements( corev1.EnvVar{Name: "ELASTIC_INDEX_SUFFIX", Value: "clusterTestName"}, )) + Expect(idji.Spec.Template.Spec.Containers).To(HaveLen(1)) Expect(idji.Spec.Template.Spec.Containers[0].Env).Should(ContainElements( corev1.EnvVar{Name: "ELASTIC_INDEX_SUFFIX", Value: "clusterTestName"}, )) Expect(idc.Spec.Template.Spec.Containers[0].VolumeMounts[0].Name).To(Equal(certificatemanagement.TrustedCertConfigMapName)) Expect(idc.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath).To(Equal(certificatemanagement.TrustedCertVolumeMountPath)) + Expect(idc.Spec.Template.Spec.Volumes[0].Name).To(Equal(certificatemanagement.TrustedCertConfigMapName)) Expect(idc.Spec.Template.Spec.Volumes[0].ConfigMap.Name).To(Equal(certificatemanagement.TrustedCertConfigMapName)) + Expect(*idc.Spec.Template.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation).To(BeFalse()) + Expect(*idc.Spec.Template.Spec.Containers[0].SecurityContext.Privileged).To(BeFalse()) + Expect(*idc.Spec.Template.Spec.Containers[0].SecurityContext.RunAsGroup).To(BeEquivalentTo(10001)) + Expect(*idc.Spec.Template.Spec.Containers[0].SecurityContext.RunAsNonRoot).To(BeTrue()) + Expect(*idc.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser).To(BeEquivalentTo(10001)) + clusterRole := rtest.GetResource(resources, "intrusion-detection-controller", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) Expect(clusterRole.Rules).To(ContainElement(rbacv1.PolicyRule{ diff --git a/pkg/render/logstorage.go b/pkg/render/logstorage.go index 2d582cd789..3af574473f 100644 --- a/pkg/render/logstorage.go +++ b/pkg/render/logstorage.go @@ -49,6 +49,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podaffinity" "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" + "github.com/tigera/operator/pkg/render/common/securitycontext" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -608,15 +609,15 @@ func (es elasticsearchComponent) podTemplate() corev1.PodTemplateSpec { // Init container that logs the SELinux context of the `/usr/share/elasticsearch` folder. // This init container is added as a workaround for a bug where Elasticsearch fails to starts when - // under some scenarios Kuberentes starts the main container before all the init containers have + // under some scenarios Kubernetes starts the main container before all the init containers have // completed, SELinux is also enabled on the node, and Kubernetes/kubelet is using the Docker runtime. // // When SELinux is enabled, SELinux policy only allows a container to read a file/folder when their // SELinux labels match or when the mounts are configured to be shared among multiple containers (the // latter isn't used by Kubernetes). These SELinux labels are managed by the container runtime. - // This assignement of SELinux labels and relabelling of files/folders happen when the container is started. + // This assignment of SELinux labels and relabelling of files/folders happen when the container is started. // The container runtime also assigns the same SELinux labels to containers created within the same sandbox. - // The exception to this SELinux label assignement being when a container is privileged, the Docker runtime + // The exception to this SELinux label assignment being when a container is privileged, the Docker runtime // mounts the container's files/folders with a different SELinux label than the one used for the sandbox. // // In Kubernetes, pods are created within the same sandbox. This ensures that files/folders can be shared by @@ -1287,6 +1288,11 @@ func (es elasticsearchComponent) kibanaCR() *kbv1.Kibana { }, VolumeMounts: volumeMounts, }}, + SecurityContext: &corev1.PodSecurityContext{ + RunAsGroup: &securitycontext.RunAsGroupID, + RunAsNonRoot: ptr.BoolToPtr(true), + RunAsUser: &securitycontext.RunAsUserID, + }, Volumes: volumes, }, }, diff --git a/pkg/render/logstorage/esmetrics/elasticsearch_metrics.go b/pkg/render/logstorage/esmetrics/elasticsearch_metrics.go index 9b53153393..0db79471bc 100644 --- a/pkg/render/logstorage/esmetrics/elasticsearch_metrics.go +++ b/pkg/render/logstorage/esmetrics/elasticsearch_metrics.go @@ -30,6 +30,7 @@ import ( relasticsearch "github.com/tigera/operator/pkg/render/common/elasticsearch" rmeta "github.com/tigera/operator/pkg/render/common/meta" "github.com/tigera/operator/pkg/render/common/secret" + "github.com/tigera/operator/pkg/render/common/securitycontext" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -158,9 +159,10 @@ func (e elasticsearchMetrics) metricsDeployment() *appsv1.Deployment { Containers: []corev1.Container{ relasticsearch.ContainerDecorate( corev1.Container{ - Name: ElasticsearchMetricsName, - Image: e.esMetricsImage, - Command: []string{"/bin/elasticsearch_exporter"}, + Name: ElasticsearchMetricsName, + Image: e.esMetricsImage, + SecurityContext: securitycontext.NewBaseContext(securitycontext.RunAsUserID, securitycontext.RunAsGroupID), + Command: []string{"/bin/elasticsearch_exporter"}, Args: []string{"--es.uri=https://$(ELASTIC_USERNAME):$(ELASTIC_PASSWORD)@$(ELASTIC_HOST):$(ELASTIC_PORT)", "--es.all", "--es.indices", "--es.indices_settings", "--es.shards", "--es.cluster_settings", "--es.timeout=30s", "--es.ca=$(ELASTIC_CA)", "--web.listen-address=:9081", diff --git a/pkg/render/logstorage/esmetrics/elasticsearch_metrics_test.go b/pkg/render/logstorage/esmetrics/elasticsearch_metrics_test.go index 18ea79d64f..4d57247bb1 100644 --- a/pkg/render/logstorage/esmetrics/elasticsearch_metrics_test.go +++ b/pkg/render/logstorage/esmetrics/elasticsearch_metrics_test.go @@ -211,10 +211,17 @@ var _ = Describe("Elasticsearch metrics", func() { Expect(service.Spec).To(Equal(expectedService.Spec)) Expect(deploy.Annotations).To(Equal(expectedDeploy.Annotations)) Expect(deploy.Spec.Template.Spec.Volumes).To(Equal(expectedDeploy.Spec.Template.Spec.Volumes)) + Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1)) Expect(deploy.Spec.Template.Spec.Containers[0].VolumeMounts).To(Equal(expectedDeploy.Spec.Template.Spec.Containers[0].VolumeMounts)) Expect(deploy.Spec.Template.Spec.Containers[0].Args).To(Equal(expectedDeploy.Spec.Template.Spec.Containers[0].Args)) Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(Equal(expectedDeploy.Spec.Template.Spec.Containers[0].Env)) Expect(deploy.Spec.Template.Spec.Containers[0].Command).To(Equal(expectedDeploy.Spec.Template.Spec.Containers[0].Command)) + + Expect(*deploy.Spec.Template.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation).To(BeFalse()) + Expect(*deploy.Spec.Template.Spec.Containers[0].SecurityContext.Privileged).To(BeFalse()) + Expect(*deploy.Spec.Template.Spec.Containers[0].SecurityContext.RunAsGroup).To(BeEquivalentTo(10001)) + Expect(*deploy.Spec.Template.Spec.Containers[0].SecurityContext.RunAsNonRoot).To(BeTrue()) + Expect(*deploy.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser).To(BeEquivalentTo(10001)) }) It("should apply controlPlaneNodeSelector correctly", func() { diff --git a/pkg/render/logstorage_test.go b/pkg/render/logstorage_test.go index cca0c34b16..1a8dd021b1 100644 --- a/pkg/render/logstorage_test.go +++ b/pkg/render/logstorage_test.go @@ -222,6 +222,13 @@ var _ = Describe("Elasticsearch rendering tests", func() { "--enable-webhook=false", "--manage-webhook-certs=false", })) + + kb := rtest.GetResource(createResources, "tigera-secure", "tigera-kibana", "kibana.k8s.elastic.co", "v1", "Kibana") + Expect(kb).NotTo(BeNil()) + kibana := kb.(*kbv1.Kibana) + Expect(*kibana.Spec.PodTemplate.Spec.SecurityContext.RunAsGroup).To(BeEquivalentTo(10001)) + Expect(*kibana.Spec.PodTemplate.Spec.SecurityContext.RunAsNonRoot).To(BeTrue()) + Expect(*kibana.Spec.PodTemplate.Spec.SecurityContext.RunAsUser).To(BeEquivalentTo(10001)) }) It("should render an elasticsearchComponent and delete the Elasticsearch and Kibana ExternalService", func() { diff --git a/pkg/render/manager.go b/pkg/render/manager.go index db093c5021..64f74c4c0b 100644 --- a/pkg/render/manager.go +++ b/pkg/render/manager.go @@ -32,9 +32,9 @@ import ( rkibana "github.com/tigera/operator/pkg/render/common/kibana" rmeta "github.com/tigera/operator/pkg/render/common/meta" "github.com/tigera/operator/pkg/render/common/podaffinity" - "github.com/tigera/operator/pkg/render/common/podsecuritycontext" "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" + "github.com/tigera/operator/pkg/render/common/securitycontext" "github.com/tigera/operator/pkg/tls/certificatemanagement" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -348,11 +348,12 @@ func (c *managerComponent) managerEnvVars() []corev1.EnvVar { // managerContainer returns the manager container. func (c *managerComponent) managerContainer() corev1.Container { tm := corev1.Container{ - Name: "tigera-manager", - Image: c.managerImage, - Env: c.managerEnvVars(), - LivenessProbe: c.managerProbe(), - SecurityContext: podsecuritycontext.NewBaseContext(), + Name: "tigera-manager", + Image: c.managerImage, + Env: c.managerEnvVars(), + LivenessProbe: c.managerProbe(), + // UID 999 is used in the manager Dockerfile. + SecurityContext: securitycontext.NewBaseContext(999, 0), VolumeMounts: c.managerVolumeMounts(), } @@ -424,12 +425,13 @@ func (c *managerComponent) managerProxyContainer() corev1.Container { } return corev1.Container{ - Name: VoltronName, - Image: c.proxyImage, - Env: env, - VolumeMounts: c.volumeMountsForProxyManager(), - LivenessProbe: c.managerProxyProbe(), - SecurityContext: podsecuritycontext.NewBaseContext(), + Name: VoltronName, + Image: c.proxyImage, + Env: env, + VolumeMounts: c.volumeMountsForProxyManager(), + LivenessProbe: c.managerProxyProbe(), + // UID 1001 is used in the voltron Dockerfile. + SecurityContext: securitycontext.NewBaseContext(1001, 0), } } @@ -464,10 +466,11 @@ func (c *managerComponent) managerEsProxyContainer() corev1.Container { } return corev1.Container{ - Name: "tigera-es-proxy", - Image: c.esProxyImage, - LivenessProbe: c.managerEsProxyProbe(), - SecurityContext: podsecuritycontext.NewBaseContext(), + Name: "tigera-es-proxy", + Image: c.esProxyImage, + LivenessProbe: c.managerEsProxyProbe(), + // UID 1001 is used in the es-proxy Dockerfile. + SecurityContext: securitycontext.NewBaseContext(1001, 0), Env: env, VolumeMounts: volumeMounts, } diff --git a/pkg/render/manager_test.go b/pkg/render/manager_test.go index cc66666c0d..e6fbd63436 100644 --- a/pkg/render/manager_test.go +++ b/pkg/render/manager_test.go @@ -85,7 +85,15 @@ var _ = Describe("Tigera Secure Manager rendering tests", func() { Expect(resources).To(HaveLen(expectedResourcesNumber)) deployment := rtest.GetResource(resources, "tigera-manager", render.ManagerNamespace, "apps", "v1", "Deployment").(*appsv1.Deployment) - Expect(len(deployment.Spec.Template.Spec.Containers)).Should(Equal(3)) + + // deployment + Expect(deployment.Spec.Template.Spec.Volumes).To(HaveLen(2)) + Expect(deployment.Spec.Template.Spec.Volumes[0].Name).To(Equal(render.ManagerTLSSecretName)) + Expect(deployment.Spec.Template.Spec.Volumes[0].Secret.SecretName).To(Equal(render.ManagerTLSSecretName)) + Expect(deployment.Spec.Template.Spec.Volumes[1].Name).To(Equal(certificatemanagement.TrustedCertConfigMapName)) + Expect(deployment.Spec.Template.Spec.Volumes[1].VolumeSource.ConfigMap.Name).To(Equal(certificatemanagement.TrustedCertConfigMapName)) + + Expect(deployment.Spec.Template.Spec.Containers).To(HaveLen(3)) manager := deployment.Spec.Template.Spec.Containers[0] esProxy := deployment.Spec.Template.Spec.Containers[1] voltron := deployment.Spec.Template.Spec.Containers[2] @@ -94,33 +102,47 @@ var _ = Describe("Tigera Secure Manager rendering tests", func() { Expect(esProxy.Image).Should(Equal(components.TigeraRegistry + "tigera/es-proxy:" + components.ComponentEsProxy.Version)) Expect(voltron.Image).Should(Equal(components.TigeraRegistry + "tigera/voltron:" + components.ComponentManagerProxy.Version)) - Expect(voltron.Env).Should(ContainElements([]corev1.EnvVar{ - {Name: "VOLTRON_QUERYSERVER_ENDPOINT", Value: "https://tigera-api.tigera-system.svc:8080"}, - {Name: "VOLTRON_QUERYSERVER_BASE_PATH", Value: "/api/v1/namespaces/tigera-system/services/https:tigera-api:8080/proxy/"}, - {Name: "VOLTRON_QUERYSERVER_CA_BUNDLE_PATH", Value: "/etc/pki/tls/certs/tigera-ca-bundle.crt"}, - }, - )) + // manager container + Expect(*manager.SecurityContext.AllowPrivilegeEscalation).To(BeFalse()) + Expect(*manager.SecurityContext.Privileged).To(BeFalse()) + Expect(*manager.SecurityContext.RunAsGroup).To(BeEquivalentTo(0)) + Expect(*manager.SecurityContext.RunAsNonRoot).To(BeTrue()) + Expect(*manager.SecurityContext.RunAsUser).To(BeEquivalentTo(999)) + // es-proxy container Expect(esProxy.Env).Should(ContainElements( corev1.EnvVar{Name: "ELASTIC_INDEX_SUFFIX", Value: "clusterTestName"}, )) - Expect(len(esProxy.VolumeMounts)).To(Equal(1)) + + Expect(esProxy.VolumeMounts).To(HaveLen(1)) Expect(esProxy.VolumeMounts[0].Name).To(Equal(certificatemanagement.TrustedCertConfigMapName)) Expect(esProxy.VolumeMounts[0].MountPath).To(Equal(certificatemanagement.TrustedCertVolumeMountPath)) - Expect(len(voltron.VolumeMounts)).To(Equal(2)) + Expect(*esProxy.SecurityContext.AllowPrivilegeEscalation).To(BeFalse()) + Expect(*esProxy.SecurityContext.Privileged).To(BeFalse()) + Expect(*esProxy.SecurityContext.RunAsGroup).To(BeEquivalentTo(0)) + Expect(*esProxy.SecurityContext.RunAsNonRoot).To(BeTrue()) + Expect(*esProxy.SecurityContext.RunAsUser).To(BeEquivalentTo(1001)) + + // voltron container + Expect(voltron.Env).To(ContainElements([]corev1.EnvVar{ + {Name: "VOLTRON_ENABLE_COMPLIANCE", Value: "true"}, + {Name: "VOLTRON_QUERYSERVER_ENDPOINT", Value: "https://tigera-api.tigera-system.svc:8080"}, + {Name: "VOLTRON_QUERYSERVER_BASE_PATH", Value: "/api/v1/namespaces/tigera-system/services/https:tigera-api:8080/proxy/"}, + {Name: "VOLTRON_QUERYSERVER_CA_BUNDLE_PATH", Value: "/etc/pki/tls/certs/tigera-ca-bundle.crt"}, + })) + + Expect(voltron.VolumeMounts).To(HaveLen(2)) Expect(voltron.VolumeMounts[0].Name).To(Equal(render.ManagerTLSSecretName)) Expect(voltron.VolumeMounts[0].MountPath).To(Equal("/manager-tls")) Expect(voltron.VolumeMounts[1].Name).To(Equal(certificatemanagement.TrustedCertConfigMapName)) Expect(voltron.VolumeMounts[1].MountPath).To(Equal(certificatemanagement.TrustedCertVolumeMountPath)) - Expect(voltron.Env).To(ContainElement(corev1.EnvVar{Name: "VOLTRON_ENABLE_COMPLIANCE", Value: "true"})) - - Expect(len(deployment.Spec.Template.Spec.Volumes)).To(Equal(2)) - Expect(deployment.Spec.Template.Spec.Volumes[0].Name).To(Equal(render.ManagerTLSSecretName)) - Expect(deployment.Spec.Template.Spec.Volumes[0].Secret.SecretName).To(Equal(render.ManagerTLSSecretName)) - Expect(deployment.Spec.Template.Spec.Volumes[1].Name).To(Equal(certificatemanagement.TrustedCertConfigMapName)) - Expect(deployment.Spec.Template.Spec.Volumes[1].VolumeSource.ConfigMap.Name).To(Equal(certificatemanagement.TrustedCertConfigMapName)) + Expect(*voltron.SecurityContext.AllowPrivilegeEscalation).To(BeFalse()) + Expect(*voltron.SecurityContext.Privileged).To(BeFalse()) + Expect(*voltron.SecurityContext.RunAsGroup).To(BeEquivalentTo(0)) + Expect(*voltron.SecurityContext.RunAsNonRoot).To(BeTrue()) + Expect(*voltron.SecurityContext.RunAsUser).To(BeEquivalentTo(1001)) }) It("should not proxy compliance if the feature is not active", func() { diff --git a/pkg/render/monitor/monitor.go b/pkg/render/monitor/monitor.go index 7d94fa962c..f1a7f74290 100644 --- a/pkg/render/monitor/monitor.go +++ b/pkg/render/monitor/monitor.go @@ -28,6 +28,7 @@ import ( "github.com/tigera/operator/pkg/render/common/configmap" rmeta "github.com/tigera/operator/pkg/render/common/meta" "github.com/tigera/operator/pkg/render/common/secret" + "github.com/tigera/operator/pkg/render/common/securitycontext" "github.com/tigera/operator/pkg/render/logstorage/esmetrics" "github.com/tigera/operator/pkg/tls/certificatemanagement" @@ -236,6 +237,11 @@ func (mc *monitorComponent) alertmanager() *monitoringv1.Alertmanager { Version: components.ComponentCoreOSAlertmanager.Version, Tolerations: mc.cfg.Installation.ControlPlaneTolerations, NodeSelector: mc.cfg.Installation.ControlPlaneNodeSelector, + SecurityContext: &corev1.PodSecurityContext{ + RunAsGroup: &securitycontext.RunAsGroupID, + RunAsNonRoot: ptr.BoolToPtr(true), + RunAsUser: &securitycontext.RunAsUserID, + }, }, } } @@ -388,6 +394,11 @@ func (mc *monitorComponent) prometheus() *monitoringv1.Prometheus { }, }, }, + SecurityContext: &corev1.PodSecurityContext{ + RunAsGroup: &securitycontext.RunAsGroupID, + RunAsNonRoot: ptr.BoolToPtr(true), + RunAsUser: &securitycontext.RunAsUserID, + }, }, } } diff --git a/pkg/render/monitor/monitor_test.go b/pkg/render/monitor/monitor_test.go index 6d25f625c6..0828d1820b 100644 --- a/pkg/render/monitor/monitor_test.go +++ b/pkg/render/monitor/monitor_test.go @@ -141,6 +141,9 @@ var _ = Describe("monitor rendering tests", func() { Expect(*alertmanagerObj.Spec.Image).To(Equal(fmt.Sprintf("%s%s:%s", components.TigeraRegistry, alertmanagerCom.Image, alertmanagerCom.Version))) Expect(*alertmanagerObj.Spec.Replicas).To(Equal(int32(3))) Expect(alertmanagerObj.Spec.Version).To(Equal(components.ComponentCoreOSAlertmanager.Version)) + Expect(*alertmanagerObj.Spec.SecurityContext.RunAsGroup).To(BeEquivalentTo(10001)) + Expect(*alertmanagerObj.Spec.SecurityContext.RunAsNonRoot).To(BeTrue()) + Expect(*alertmanagerObj.Spec.SecurityContext.RunAsUser).To(BeEquivalentTo(10001)) // Alertmanager Service serviceObj, ok := rtest.GetResource(toCreate, "calico-node-alertmanager", common.TigeraPrometheusNamespace, "", "v1", "Service").(*corev1.Service) @@ -176,6 +179,9 @@ var _ = Describe("monitor rendering tests", func() { Expect(prometheusObj.Spec.Alerting.Alertmanagers[0].Namespace).To(Equal("tigera-prometheus")) Expect(prometheusObj.Spec.Alerting.Alertmanagers[0].Port).To(Equal(intstr.FromString("web"))) Expect(prometheusObj.Spec.Alerting.Alertmanagers[0].Scheme).To(Equal("HTTP")) + Expect(*prometheusObj.Spec.SecurityContext.RunAsGroup).To(BeEquivalentTo(10001)) + Expect(*prometheusObj.Spec.SecurityContext.RunAsNonRoot).To(BeTrue()) + Expect(*prometheusObj.Spec.SecurityContext.RunAsUser).To(BeEquivalentTo(10001)) // Prometheus ServiceAccount _, ok = rtest.GetResource(toCreate, "prometheus", common.TigeraPrometheusNamespace, "", "v1", "ServiceAccount").(*corev1.ServiceAccount) diff --git a/pkg/render/packet_capture_api.go b/pkg/render/packet_capture_api.go index 25334c42bf..fed76d11f2 100644 --- a/pkg/render/packet_capture_api.go +++ b/pkg/render/packet_capture_api.go @@ -28,8 +28,8 @@ import ( "github.com/tigera/operator/pkg/render/common/authentication" "github.com/tigera/operator/pkg/render/common/configmap" rmeta "github.com/tigera/operator/pkg/render/common/meta" - "github.com/tigera/operator/pkg/render/common/podsecuritycontext" "github.com/tigera/operator/pkg/render/common/secret" + "github.com/tigera/operator/pkg/render/common/securitycontext" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -257,11 +257,12 @@ func (pc *packetCaptureApiComponent) container() corev1.Container { } return corev1.Container{ - Name: PacketCaptureContainerName, - Image: pc.image, - LivenessProbe: pc.healthProbe(), - ReadinessProbe: pc.healthProbe(), - SecurityContext: podsecuritycontext.NewBaseContext(), + Name: PacketCaptureContainerName, + Image: pc.image, + LivenessProbe: pc.healthProbe(), + ReadinessProbe: pc.healthProbe(), + // UID 1001 is used in the packetcapture Dockerfile. + SecurityContext: securitycontext.NewBaseContext(1001, 0), Env: env, VolumeMounts: volumeMounts, } diff --git a/pkg/render/packetcapture_api_test.go b/pkg/render/packetcapture_api_test.go index 937feaac41..e8b87e1599 100644 --- a/pkg/render/packetcapture_api_test.go +++ b/pkg/render/packetcapture_api_test.go @@ -187,8 +187,11 @@ var _ = Describe("Rendering tests for PacketCapture API component", func() { Name: render.PacketCaptureContainerName, Image: fmt.Sprintf("%s%s:%s", components.TigeraRegistry, components.ComponentPacketCapture.Image, components.ComponentPacketCapture.Version), SecurityContext: &corev1.SecurityContext{ - RunAsNonRoot: ptr.BoolToPtr(true), AllowPrivilegeEscalation: ptr.BoolToPtr(false), + Privileged: ptr.BoolToPtr(false), + RunAsGroup: ptr.Int64ToPtr(0), + RunAsNonRoot: ptr.BoolToPtr(true), + RunAsUser: ptr.Int64ToPtr(1001), }, ReadinessProbe: &corev1.Probe{ Handler: corev1.Handler{