Skip to content

Commit f9103f6

Browse files
authored
Adding securityContext (#216)
* Adding securityContext * Implementing comments from @brandond * Removing deep merge * Fixing test Signed-off-by: Jiri Tyr <[email protected]>
1 parent a6f66bc commit f9103f6

File tree

3 files changed

+302
-2
lines changed

3 files changed

+302
-2
lines changed

pkg/apis/helm.cattle.io/v1/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ type HelmChartSpec struct {
3939

4040
AuthPassCredentials bool `json:"authPassCredentials,omitempty"`
4141
DockerRegistrySecret *corev1.LocalObjectReference `json:"dockerRegistrySecret,omitempty"`
42+
43+
PodSecurityContext *corev1.PodSecurityContext `json:"podSecurityContext,omitempty"`
44+
SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"`
4245
}
4346

4447
type HelmChartStatus struct {

pkg/controllers/chart/chart.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,22 @@ var (
5656
DefaultJobImage = "rancher/klipper-helm:v0.8.2-build20230815"
5757
DefaultFailurePolicy = FailurePolicyReinstall
5858
defaultBackOffLimit = pointer.Int32(1000)
59+
60+
defaultPodSecurityContext = &corev1.PodSecurityContext{
61+
RunAsNonRoot: pointer.BoolPtr(true),
62+
SeccompProfile: &corev1.SeccompProfile{
63+
Type: "RuntimeDefault",
64+
},
65+
}
66+
defaultSecurityContext = &corev1.SecurityContext{
67+
AllowPrivilegeEscalation: pointer.BoolPtr(false),
68+
Capabilities: &corev1.Capabilities{
69+
Drop: []corev1.Capability{
70+
"ALL",
71+
},
72+
},
73+
ReadOnlyRootFilesystem: pointer.BoolPtr(true),
74+
}
5975
)
6076

6177
type Controller struct {
@@ -377,6 +393,9 @@ func job(chart *v1.HelmChart, apiServerPort string) (*batch.Job, *corev1.Secret,
377393
chartName = chart.Name + "/" + chart.Spec.Chart
378394
}
379395

396+
podSecurityContext := defaultPodSecurityContext.DeepCopy()
397+
securityContext := defaultSecurityContext.DeepCopy()
398+
380399
job := &batch.Job{
381400
TypeMeta: metav1.TypeMeta{
382401
APIVersion: "batch/v1",
@@ -443,9 +462,51 @@ func job(chart *v1.HelmChart, apiServerPort string) (*batch.Job, *corev1.Secret,
443462
Value: fmt.Sprintf("%t", chart.Spec.AuthPassCredentials),
444463
},
445464
},
465+
SecurityContext: securityContext,
466+
VolumeMounts: []corev1.VolumeMount{
467+
{
468+
Name: "klipper-helm",
469+
MountPath: "/home/klipper-helm/.helm",
470+
},
471+
{
472+
Name: "klipper-cache",
473+
MountPath: "/home/klipper-helm/.cache",
474+
},
475+
{
476+
Name: "klipper-config",
477+
MountPath: "/home/klipper-helm/.config",
478+
},
479+
},
446480
},
447481
},
448482
ServiceAccountName: fmt.Sprintf("helm-%s", chart.Name),
483+
SecurityContext: podSecurityContext,
484+
Volumes: []corev1.Volume{
485+
{
486+
Name: "klipper-helm",
487+
VolumeSource: corev1.VolumeSource{
488+
EmptyDir: &corev1.EmptyDirVolumeSource{
489+
Medium: "Memory",
490+
},
491+
},
492+
},
493+
{
494+
Name: "klipper-cache",
495+
VolumeSource: corev1.VolumeSource{
496+
EmptyDir: &corev1.EmptyDirVolumeSource{
497+
Medium: "Memory",
498+
},
499+
},
500+
},
501+
{
502+
Name: "klipper-config",
503+
VolumeSource: corev1.VolumeSource{
504+
EmptyDir: &corev1.EmptyDirVolumeSource{
505+
Medium: "Memory",
506+
},
507+
},
508+
},
509+
},
449510
},
450511
},
451512
},
@@ -507,6 +568,7 @@ func job(chart *v1.HelmChart, apiServerPort string) (*batch.Job, *corev1.Secret,
507568
setAuthSecret(job, chart)
508569
setDockerRegistrySecret(job, chart)
509570
setRepoCAConfigMap(job, chart)
571+
setSecurityContext(job, chart)
510572
valuesSecret := setValuesSecret(job, chart)
511573
contentConfigMap := setContentConfigMap(job, chart)
512574

@@ -831,3 +893,13 @@ func hashObjects(job *batch.Job, objs ...metav1.Object) {
831893
func setBackOffLimit(job *batch.Job, backOffLimit *int32) {
832894
job.Spec.BackoffLimit = backOffLimit
833895
}
896+
897+
func setSecurityContext(job *batch.Job, chart *v1.HelmChart) {
898+
if chart.Spec.PodSecurityContext != nil {
899+
job.Spec.Template.Spec.SecurityContext = chart.Spec.PodSecurityContext
900+
}
901+
902+
if chart.Spec.SecurityContext != nil {
903+
job.Spec.Template.Spec.Containers[0].SecurityContext = chart.Spec.SecurityContext
904+
}
905+
}

test/suite/helm_test.go

Lines changed: 227 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1616
"k8s.io/apimachinery/pkg/labels"
1717
"k8s.io/apimachinery/pkg/util/intstr"
18+
"k8s.io/utils/pointer"
1819
)
1920

2021
var _ = Describe("Helm Tests", Ordered, func() {
@@ -362,7 +363,6 @@ var _ = Describe("Helm Tests", Ordered, func() {
362363
_, err := framework.GetHelmChart(chart.Name, framework.Namespace)
363364
return err != nil && apierrors.IsNotFound(err)
364365
}, 120*time.Second, 5*time.Second).Should(BeTrue())
365-
366366
})
367367
})
368368

@@ -474,7 +474,6 @@ var _ = Describe("Helm Tests", Ordered, func() {
474474
return err != nil && apierrors.IsNotFound(err)
475475
}, 120*time.Second, 5*time.Second).Should(BeTrue())
476476
})
477-
478477
})
479478

480479
Context("When a no backoffLimit is specified", func() {
@@ -528,6 +527,232 @@ var _ = Describe("Helm Tests", Ordered, func() {
528527
return err != nil && apierrors.IsNotFound(err)
529528
}, 120*time.Second, 5*time.Second).Should(BeTrue())
530529
})
530+
})
531+
532+
Context("When a custom podSecurityContext is specified", func() {
533+
var (
534+
err error
535+
chart *v1.HelmChart
536+
job *batchv1.Job
537+
expectedPodSecurityContext = &corev1.PodSecurityContext{
538+
RunAsNonRoot: pointer.BoolPtr(false),
539+
}
540+
)
541+
BeforeEach(func() {
542+
chart = framework.NewHelmChart("traefik-example-custom-podsecuritycontext",
543+
"stable/traefik",
544+
"1.86.1",
545+
"v3",
546+
map[string]intstr.IntOrString{
547+
"rbac.enabled": {
548+
Type: intstr.String,
549+
StrVal: "true",
550+
},
551+
"ssl.enabled": {
552+
Type: intstr.String,
553+
StrVal: "true",
554+
},
555+
})
556+
chart.Spec.PodSecurityContext = &corev1.PodSecurityContext{
557+
RunAsNonRoot: pointer.BoolPtr(false),
558+
}
559+
chart, err = framework.CreateHelmChart(chart, framework.Namespace)
560+
Expect(err).ToNot(HaveOccurred())
561+
562+
labelSelector := labels.SelectorFromSet(labels.Set{
563+
"owner": "helm",
564+
"name": chart.Name,
565+
})
566+
_, err = framework.WaitForRelease(chart, labelSelector, 120*time.Second, 1)
567+
Expect(err).ToNot(HaveOccurred())
568+
569+
chart, err = framework.GetHelmChart(chart.Name, chart.Namespace)
570+
Expect(err).ToNot(HaveOccurred())
571+
job, err = framework.GetJob(chart)
572+
Expect(err).ToNot(HaveOccurred())
573+
})
574+
It("Should have correct pod securityContext", func() {
575+
Expect(*job.Spec.Template.Spec.SecurityContext).To(Equal(*expectedPodSecurityContext))
576+
})
577+
AfterEach(func() {
578+
err = framework.DeleteHelmChart(chart.Name, framework.Namespace)
579+
Expect(err).ToNot(HaveOccurred())
580+
581+
Eventually(func() bool {
582+
_, err := framework.GetHelmChart(chart.Name, framework.Namespace)
583+
return err != nil && apierrors.IsNotFound(err)
584+
}, 120*time.Second, 5*time.Second).Should(BeTrue())
585+
})
586+
})
587+
588+
Context("When a no podSecurityContext is specified", func() {
589+
var (
590+
err error
591+
chart *v1.HelmChart
592+
job *batchv1.Job
593+
defaultPodSecurityContext = &corev1.PodSecurityContext{
594+
RunAsNonRoot: pointer.BoolPtr(true),
595+
SeccompProfile: &corev1.SeccompProfile{
596+
Type: "RuntimeDefault",
597+
},
598+
}
599+
)
600+
BeforeEach(func() {
601+
chart = framework.NewHelmChart("traefik-example-default-podsecuritycontext",
602+
"stable/traefik",
603+
"1.86.1",
604+
"v3",
605+
map[string]intstr.IntOrString{
606+
"rbac.enabled": {
607+
Type: intstr.String,
608+
StrVal: "true",
609+
},
610+
"ssl.enabled": {
611+
Type: intstr.String,
612+
StrVal: "true",
613+
},
614+
})
615+
chart, err = framework.CreateHelmChart(chart, framework.Namespace)
616+
Expect(err).ToNot(HaveOccurred())
617+
618+
labelSelector := labels.SelectorFromSet(labels.Set{
619+
"owner": "helm",
620+
"name": chart.Name,
621+
})
622+
_, err = framework.WaitForRelease(chart, labelSelector, 120*time.Second, 1)
623+
Expect(err).ToNot(HaveOccurred())
624+
625+
chart, err = framework.GetHelmChart(chart.Name, chart.Namespace)
626+
Expect(err).ToNot(HaveOccurred())
627+
job, err = framework.GetJob(chart)
628+
Expect(err).ToNot(HaveOccurred())
629+
})
630+
It("Should have correct pod securityContext", func() {
631+
Expect(*job.Spec.Template.Spec.SecurityContext).To(Equal(*defaultPodSecurityContext))
632+
})
633+
AfterEach(func() {
634+
err = framework.DeleteHelmChart(chart.Name, framework.Namespace)
635+
Expect(err).ToNot(HaveOccurred())
636+
637+
Eventually(func() bool {
638+
_, err := framework.GetHelmChart(chart.Name, framework.Namespace)
639+
return err != nil && apierrors.IsNotFound(err)
640+
}, 120*time.Second, 5*time.Second).Should(BeTrue())
641+
})
642+
})
643+
644+
Context("When a custom securityContext is specified", func() {
645+
var (
646+
err error
647+
chart *v1.HelmChart
648+
job *batchv1.Job
649+
expectedSecurityContext = &corev1.SecurityContext{
650+
AllowPrivilegeEscalation: pointer.BoolPtr(true),
651+
}
652+
)
653+
BeforeEach(func() {
654+
chart = framework.NewHelmChart("traefik-example-custom-securitycontext",
655+
"stable/traefik",
656+
"1.86.1",
657+
"v3",
658+
map[string]intstr.IntOrString{
659+
"rbac.enabled": {
660+
Type: intstr.String,
661+
StrVal: "true",
662+
},
663+
"ssl.enabled": {
664+
Type: intstr.String,
665+
StrVal: "true",
666+
},
667+
})
668+
chart.Spec.SecurityContext = &corev1.SecurityContext{
669+
AllowPrivilegeEscalation: pointer.BoolPtr(true),
670+
}
671+
chart, err = framework.CreateHelmChart(chart, framework.Namespace)
672+
Expect(err).ToNot(HaveOccurred())
673+
674+
labelSelector := labels.SelectorFromSet(labels.Set{
675+
"owner": "helm",
676+
"name": chart.Name,
677+
})
678+
_, err = framework.WaitForRelease(chart, labelSelector, 120*time.Second, 1)
679+
Expect(err).ToNot(HaveOccurred())
680+
681+
chart, err = framework.GetHelmChart(chart.Name, chart.Namespace)
682+
Expect(err).ToNot(HaveOccurred())
683+
job, err = framework.GetJob(chart)
684+
Expect(err).ToNot(HaveOccurred())
685+
})
686+
It("Should have correct container securityContext", func() {
687+
Expect(*job.Spec.Template.Spec.Containers[0].SecurityContext).To(Equal(*expectedSecurityContext))
688+
})
689+
AfterEach(func() {
690+
err = framework.DeleteHelmChart(chart.Name, framework.Namespace)
691+
Expect(err).ToNot(HaveOccurred())
531692

693+
Eventually(func() bool {
694+
_, err := framework.GetHelmChart(chart.Name, framework.Namespace)
695+
return err != nil && apierrors.IsNotFound(err)
696+
}, 120*time.Second, 5*time.Second).Should(BeTrue())
697+
})
698+
})
699+
700+
Context("When a no securityContext is specified", func() {
701+
var (
702+
err error
703+
chart *v1.HelmChart
704+
job *batchv1.Job
705+
defaultSecurityContext = &corev1.SecurityContext{
706+
AllowPrivilegeEscalation: pointer.BoolPtr(false),
707+
Capabilities: &corev1.Capabilities{
708+
Drop: []corev1.Capability{
709+
"ALL",
710+
},
711+
},
712+
ReadOnlyRootFilesystem: pointer.BoolPtr(true),
713+
}
714+
)
715+
BeforeEach(func() {
716+
chart = framework.NewHelmChart("traefik-example-default-securitycontext",
717+
"stable/traefik",
718+
"1.86.1",
719+
"v3",
720+
map[string]intstr.IntOrString{
721+
"rbac.enabled": {
722+
Type: intstr.String,
723+
StrVal: "true",
724+
},
725+
"ssl.enabled": {
726+
Type: intstr.String,
727+
StrVal: "true",
728+
},
729+
})
730+
chart, err = framework.CreateHelmChart(chart, framework.Namespace)
731+
Expect(err).ToNot(HaveOccurred())
732+
733+
labelSelector := labels.SelectorFromSet(labels.Set{
734+
"owner": "helm",
735+
"name": chart.Name,
736+
})
737+
_, err = framework.WaitForRelease(chart, labelSelector, 120*time.Second, 1)
738+
Expect(err).ToNot(HaveOccurred())
739+
740+
chart, err = framework.GetHelmChart(chart.Name, chart.Namespace)
741+
Expect(err).ToNot(HaveOccurred())
742+
job, err = framework.GetJob(chart)
743+
Expect(err).ToNot(HaveOccurred())
744+
})
745+
It("Should have correct container securityContext", func() {
746+
Expect(*job.Spec.Template.Spec.Containers[0].SecurityContext).To(Equal(*defaultSecurityContext))
747+
})
748+
AfterEach(func() {
749+
err = framework.DeleteHelmChart(chart.Name, framework.Namespace)
750+
Expect(err).ToNot(HaveOccurred())
751+
752+
Eventually(func() bool {
753+
_, err := framework.GetHelmChart(chart.Name, framework.Namespace)
754+
return err != nil && apierrors.IsNotFound(err)
755+
}, 120*time.Second, 5*time.Second).Should(BeTrue())
756+
})
532757
})
533758
})

0 commit comments

Comments
 (0)