Skip to content

Commit

Permalink
Adding securityContext (#216)
Browse files Browse the repository at this point in the history
* Adding securityContext
* Implementing comments from @brandond
* Removing deep merge
* Fixing test

Signed-off-by: Jiri Tyr <[email protected]>
  • Loading branch information
jtyr authored Dec 19, 2023
1 parent a6f66bc commit f9103f6
Show file tree
Hide file tree
Showing 3 changed files with 302 additions and 2 deletions.
3 changes: 3 additions & 0 deletions pkg/apis/helm.cattle.io/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ type HelmChartSpec struct {

AuthPassCredentials bool `json:"authPassCredentials,omitempty"`
DockerRegistrySecret *corev1.LocalObjectReference `json:"dockerRegistrySecret,omitempty"`

PodSecurityContext *corev1.PodSecurityContext `json:"podSecurityContext,omitempty"`
SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"`
}

type HelmChartStatus struct {
Expand Down
72 changes: 72 additions & 0 deletions pkg/controllers/chart/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ var (
DefaultJobImage = "rancher/klipper-helm:v0.8.2-build20230815"
DefaultFailurePolicy = FailurePolicyReinstall
defaultBackOffLimit = pointer.Int32(1000)

defaultPodSecurityContext = &corev1.PodSecurityContext{
RunAsNonRoot: pointer.BoolPtr(true),
SeccompProfile: &corev1.SeccompProfile{
Type: "RuntimeDefault",
},
}
defaultSecurityContext = &corev1.SecurityContext{
AllowPrivilegeEscalation: pointer.BoolPtr(false),
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{
"ALL",
},
},
ReadOnlyRootFilesystem: pointer.BoolPtr(true),
}
)

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

podSecurityContext := defaultPodSecurityContext.DeepCopy()
securityContext := defaultSecurityContext.DeepCopy()

job := &batch.Job{
TypeMeta: metav1.TypeMeta{
APIVersion: "batch/v1",
Expand Down Expand Up @@ -443,9 +462,51 @@ func job(chart *v1.HelmChart, apiServerPort string) (*batch.Job, *corev1.Secret,
Value: fmt.Sprintf("%t", chart.Spec.AuthPassCredentials),
},
},
SecurityContext: securityContext,
VolumeMounts: []corev1.VolumeMount{
{
Name: "klipper-helm",
MountPath: "/home/klipper-helm/.helm",
},
{
Name: "klipper-cache",
MountPath: "/home/klipper-helm/.cache",
},
{
Name: "klipper-config",
MountPath: "/home/klipper-helm/.config",
},
},
},
},
ServiceAccountName: fmt.Sprintf("helm-%s", chart.Name),
SecurityContext: podSecurityContext,
Volumes: []corev1.Volume{
{
Name: "klipper-helm",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{
Medium: "Memory",
},
},
},
{
Name: "klipper-cache",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{
Medium: "Memory",
},
},
},
{
Name: "klipper-config",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{
Medium: "Memory",
},
},
},
},
},
},
},
Expand Down Expand Up @@ -507,6 +568,7 @@ func job(chart *v1.HelmChart, apiServerPort string) (*batch.Job, *corev1.Secret,
setAuthSecret(job, chart)
setDockerRegistrySecret(job, chart)
setRepoCAConfigMap(job, chart)
setSecurityContext(job, chart)
valuesSecret := setValuesSecret(job, chart)
contentConfigMap := setContentConfigMap(job, chart)

Expand Down Expand Up @@ -831,3 +893,13 @@ func hashObjects(job *batch.Job, objs ...metav1.Object) {
func setBackOffLimit(job *batch.Job, backOffLimit *int32) {
job.Spec.BackoffLimit = backOffLimit
}

func setSecurityContext(job *batch.Job, chart *v1.HelmChart) {
if chart.Spec.PodSecurityContext != nil {
job.Spec.Template.Spec.SecurityContext = chart.Spec.PodSecurityContext
}

if chart.Spec.SecurityContext != nil {
job.Spec.Template.Spec.Containers[0].SecurityContext = chart.Spec.SecurityContext
}
}
229 changes: 227 additions & 2 deletions test/suite/helm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/pointer"
)

var _ = Describe("Helm Tests", Ordered, func() {
Expand Down Expand Up @@ -362,7 +363,6 @@ var _ = Describe("Helm Tests", Ordered, func() {
_, err := framework.GetHelmChart(chart.Name, framework.Namespace)
return err != nil && apierrors.IsNotFound(err)
}, 120*time.Second, 5*time.Second).Should(BeTrue())

})
})

Expand Down Expand Up @@ -474,7 +474,6 @@ var _ = Describe("Helm Tests", Ordered, func() {
return err != nil && apierrors.IsNotFound(err)
}, 120*time.Second, 5*time.Second).Should(BeTrue())
})

})

Context("When a no backoffLimit is specified", func() {
Expand Down Expand Up @@ -528,6 +527,232 @@ var _ = Describe("Helm Tests", Ordered, func() {
return err != nil && apierrors.IsNotFound(err)
}, 120*time.Second, 5*time.Second).Should(BeTrue())
})
})

Context("When a custom podSecurityContext is specified", func() {
var (
err error
chart *v1.HelmChart
job *batchv1.Job
expectedPodSecurityContext = &corev1.PodSecurityContext{
RunAsNonRoot: pointer.BoolPtr(false),
}
)
BeforeEach(func() {
chart = framework.NewHelmChart("traefik-example-custom-podsecuritycontext",
"stable/traefik",
"1.86.1",
"v3",
map[string]intstr.IntOrString{
"rbac.enabled": {
Type: intstr.String,
StrVal: "true",
},
"ssl.enabled": {
Type: intstr.String,
StrVal: "true",
},
})
chart.Spec.PodSecurityContext = &corev1.PodSecurityContext{
RunAsNonRoot: pointer.BoolPtr(false),
}
chart, err = framework.CreateHelmChart(chart, framework.Namespace)
Expect(err).ToNot(HaveOccurred())

labelSelector := labels.SelectorFromSet(labels.Set{
"owner": "helm",
"name": chart.Name,
})
_, err = framework.WaitForRelease(chart, labelSelector, 120*time.Second, 1)
Expect(err).ToNot(HaveOccurred())

chart, err = framework.GetHelmChart(chart.Name, chart.Namespace)
Expect(err).ToNot(HaveOccurred())
job, err = framework.GetJob(chart)
Expect(err).ToNot(HaveOccurred())
})
It("Should have correct pod securityContext", func() {
Expect(*job.Spec.Template.Spec.SecurityContext).To(Equal(*expectedPodSecurityContext))
})
AfterEach(func() {
err = framework.DeleteHelmChart(chart.Name, framework.Namespace)
Expect(err).ToNot(HaveOccurred())

Eventually(func() bool {
_, err := framework.GetHelmChart(chart.Name, framework.Namespace)
return err != nil && apierrors.IsNotFound(err)
}, 120*time.Second, 5*time.Second).Should(BeTrue())
})
})

Context("When a no podSecurityContext is specified", func() {
var (
err error
chart *v1.HelmChart
job *batchv1.Job
defaultPodSecurityContext = &corev1.PodSecurityContext{
RunAsNonRoot: pointer.BoolPtr(true),
SeccompProfile: &corev1.SeccompProfile{
Type: "RuntimeDefault",
},
}
)
BeforeEach(func() {
chart = framework.NewHelmChart("traefik-example-default-podsecuritycontext",
"stable/traefik",
"1.86.1",
"v3",
map[string]intstr.IntOrString{
"rbac.enabled": {
Type: intstr.String,
StrVal: "true",
},
"ssl.enabled": {
Type: intstr.String,
StrVal: "true",
},
})
chart, err = framework.CreateHelmChart(chart, framework.Namespace)
Expect(err).ToNot(HaveOccurred())

labelSelector := labels.SelectorFromSet(labels.Set{
"owner": "helm",
"name": chart.Name,
})
_, err = framework.WaitForRelease(chart, labelSelector, 120*time.Second, 1)
Expect(err).ToNot(HaveOccurred())

chart, err = framework.GetHelmChart(chart.Name, chart.Namespace)
Expect(err).ToNot(HaveOccurred())
job, err = framework.GetJob(chart)
Expect(err).ToNot(HaveOccurred())
})
It("Should have correct pod securityContext", func() {
Expect(*job.Spec.Template.Spec.SecurityContext).To(Equal(*defaultPodSecurityContext))
})
AfterEach(func() {
err = framework.DeleteHelmChart(chart.Name, framework.Namespace)
Expect(err).ToNot(HaveOccurred())

Eventually(func() bool {
_, err := framework.GetHelmChart(chart.Name, framework.Namespace)
return err != nil && apierrors.IsNotFound(err)
}, 120*time.Second, 5*time.Second).Should(BeTrue())
})
})

Context("When a custom securityContext is specified", func() {
var (
err error
chart *v1.HelmChart
job *batchv1.Job
expectedSecurityContext = &corev1.SecurityContext{
AllowPrivilegeEscalation: pointer.BoolPtr(true),
}
)
BeforeEach(func() {
chart = framework.NewHelmChart("traefik-example-custom-securitycontext",
"stable/traefik",
"1.86.1",
"v3",
map[string]intstr.IntOrString{
"rbac.enabled": {
Type: intstr.String,
StrVal: "true",
},
"ssl.enabled": {
Type: intstr.String,
StrVal: "true",
},
})
chart.Spec.SecurityContext = &corev1.SecurityContext{
AllowPrivilegeEscalation: pointer.BoolPtr(true),
}
chart, err = framework.CreateHelmChart(chart, framework.Namespace)
Expect(err).ToNot(HaveOccurred())

labelSelector := labels.SelectorFromSet(labels.Set{
"owner": "helm",
"name": chart.Name,
})
_, err = framework.WaitForRelease(chart, labelSelector, 120*time.Second, 1)
Expect(err).ToNot(HaveOccurred())

chart, err = framework.GetHelmChart(chart.Name, chart.Namespace)
Expect(err).ToNot(HaveOccurred())
job, err = framework.GetJob(chart)
Expect(err).ToNot(HaveOccurred())
})
It("Should have correct container securityContext", func() {
Expect(*job.Spec.Template.Spec.Containers[0].SecurityContext).To(Equal(*expectedSecurityContext))
})
AfterEach(func() {
err = framework.DeleteHelmChart(chart.Name, framework.Namespace)
Expect(err).ToNot(HaveOccurred())

Eventually(func() bool {
_, err := framework.GetHelmChart(chart.Name, framework.Namespace)
return err != nil && apierrors.IsNotFound(err)
}, 120*time.Second, 5*time.Second).Should(BeTrue())
})
})

Context("When a no securityContext is specified", func() {
var (
err error
chart *v1.HelmChart
job *batchv1.Job
defaultSecurityContext = &corev1.SecurityContext{
AllowPrivilegeEscalation: pointer.BoolPtr(false),
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{
"ALL",
},
},
ReadOnlyRootFilesystem: pointer.BoolPtr(true),
}
)
BeforeEach(func() {
chart = framework.NewHelmChart("traefik-example-default-securitycontext",
"stable/traefik",
"1.86.1",
"v3",
map[string]intstr.IntOrString{
"rbac.enabled": {
Type: intstr.String,
StrVal: "true",
},
"ssl.enabled": {
Type: intstr.String,
StrVal: "true",
},
})
chart, err = framework.CreateHelmChart(chart, framework.Namespace)
Expect(err).ToNot(HaveOccurred())

labelSelector := labels.SelectorFromSet(labels.Set{
"owner": "helm",
"name": chart.Name,
})
_, err = framework.WaitForRelease(chart, labelSelector, 120*time.Second, 1)
Expect(err).ToNot(HaveOccurred())

chart, err = framework.GetHelmChart(chart.Name, chart.Namespace)
Expect(err).ToNot(HaveOccurred())
job, err = framework.GetJob(chart)
Expect(err).ToNot(HaveOccurred())
})
It("Should have correct container securityContext", func() {
Expect(*job.Spec.Template.Spec.Containers[0].SecurityContext).To(Equal(*defaultSecurityContext))
})
AfterEach(func() {
err = framework.DeleteHelmChart(chart.Name, framework.Namespace)
Expect(err).ToNot(HaveOccurred())

Eventually(func() bool {
_, err := framework.GetHelmChart(chart.Name, framework.Namespace)
return err != nil && apierrors.IsNotFound(err)
}, 120*time.Second, 5*time.Second).Should(BeTrue())
})
})
})

0 comments on commit f9103f6

Please sign in to comment.