Repository: grafana/k6-operator
Pull request #105 (2022-03-25 17:25:07)

Fix security context PRs
References: #74 and #101


Patch:
diff --git a/README.md b/README.md
index 88b6b4d..93025ee 100644
--- a/README.md
+++ b/README.md
@@ -76,6 +76,10 @@ spec:
         cool-label: foo
       annotations:
         cool-annotation: bar
+    securityContext:
+      runAsUser: 1000
+      runAsGroup: 1000
+      runAsNonRoot: true
     resources:
       limits:
         cpu: 200m
@@ -90,6 +94,10 @@ spec:
         cool-label: foo
       annotations:
         cool-annotation: bar
+    securityContext:
+      runAsUser: 2000
+      runAsGroup: 2000
+      runAsNonRoot: true
 ```
 
 The test configuration is applied using
diff --git a/api/v1alpha1/k6_types.go b/api/v1alpha1/k6_types.go
index 3a906f6..8d901cf 100644
--- a/api/v1alpha1/k6_types.go
+++ b/api/v1alpha1/k6_types.go
@@ -33,6 +33,7 @@ type Pod struct {
 	NodeSelector                 map[string]string           `json:"nodeselector,omitempty"`
 	Resources                    corev1.ResourceRequirements `json:"resources,omitempty"`
 	ServiceAccountName           string                      `json:"serviceAccountName,omitempty"`
+	SecurityContext              corev1.PodSecurityContext   `json:"securityContext,omitempty"`
 }
 
 type K6Scuttle struct {
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 2a1bfce..4164701 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -210,6 +210,7 @@ func (in *Pod) DeepCopyInto(out *Pod) {
 		}
 	}
 	in.Resources.DeepCopyInto(&out.Resources)
+	in.SecurityContext.DeepCopyInto(&out.SecurityContext)
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Pod.
diff --git a/config/crd/bases/k6.io_k6s.yaml b/config/crd/bases/k6.io_k6s.yaml
index 29d9e54..0ce4aa8 100644
--- a/config/crd/bases/k6.io_k6s.yaml
+++ b/config/crd/bases/k6.io_k6s.yaml
@@ -857,6 +857,135 @@ spec:
                           to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
                         type: object
                     type: object
+                  securityContext:
+                    description: PodSecurityContext holds pod-level security attributes
+                      and common container settings. Some fields are also present
+                      in container.securityContext.  Field values of container.securityContext
+                      take precedence over field values of PodSecurityContext.
+                    properties:
+                      fsGroup:
+                        description: "A special supplemental group that applies to
+                          all containers in a pod. Some volume types allow the Kubelet
+                          to change the ownership of that volume to be owned by the
+                          pod: \n 1. The owning GID will be the FSGroup 2. The setgid
+                          bit is set (new files created in the volume will be owned
+                          by FSGroup) 3. The permission bits are OR'd with rw-rw----
+                          \n If unset, the Kubelet will not modify the ownership and
+                          permissions of any volume."
+                        format: int64
+                        type: integer
+                      fsGroupChangePolicy:
+                        description: 'fsGroupChangePolicy defines behavior of changing
+                          ownership and permission of the volume before being exposed
+                          inside Pod. This field will only apply to volume types which
+                          support fsGroup based ownership(and permissions). It will
+                          have no effect on ephemeral volume types such as: secret,
+                          configmaps and emptydir. Valid values are "OnRootMismatch"
+                          and "Always". If not specified defaults to "Always".'
+                        type: string
+                      runAsGroup:
+                        description: The GID to run the entrypoint of the container
+                          process. Uses runtime default if unset. May also be set
+                          in SecurityContext.  If set in both SecurityContext and
+                          PodSecurityContext, the value specified in SecurityContext
+                          takes precedence for that container.
+                        format: int64
+                        type: integer
+                      runAsNonRoot:
+                        description: Indicates that the container must run as a non-root
+                          user. If true, the Kubelet will validate the image at runtime
+                          to ensure that it does not run as UID 0 (root) and fail
+                          to start the container if it does. If unset or false, no
+                          such validation will be performed. May also be set in SecurityContext.  If
+                          set in both SecurityContext and PodSecurityContext, the
+                          value specified in SecurityContext takes precedence.
+                        type: boolean
+                      runAsUser:
+                        description: The UID to run the entrypoint of the container
+                          process. Defaults to user specified in image metadata if
+                          unspecified. May also be set in SecurityContext.  If set
+                          in both SecurityContext and PodSecurityContext, the value
+                          specified in SecurityContext takes precedence for that container.
+                        format: int64
+                        type: integer
+                      seLinuxOptions:
+                        description: The SELinux context to be applied to all containers.
+                          If unspecified, the container runtime will allocate a random
+                          SELinux context for each container.  May also be set in
+                          SecurityContext.  If set in both SecurityContext and PodSecurityContext,
+                          the value specified in SecurityContext takes precedence
+                          for that container.
+                        properties:
+                          level:
+                            description: Level is SELinux level label that applies
+                              to the container.
+                            type: string
+                          role:
+                            description: Role is a SELinux role label that applies
+                              to the container.
+                            type: string
+                          type:
+                            description: Type is a SELinux type label that applies
+                              to the container.
+                            type: string
+                          user:
+                            description: User is a SELinux user label that applies
+                              to the container.
+                            type: string
+                        type: object
+                      supplementalGroups:
+                        description: A list of groups applied to the first process
+                          run in each container, in addition to the container's primary
+                          GID.  If unspecified, no groups will be added to any container.
+                        items:
+                          format: int64
+                          type: integer
+                        type: array
+                      sysctls:
+                        description: Sysctls hold a list of namespaced sysctls used
+                          for the pod. Pods with unsupported sysctls (by the container
+                          runtime) might fail to launch.
+                        items:
+                          description: Sysctl defines a kernel parameter to be set
+                          properties:
+                            name:
+                              description: Name of a property to set
+                              type: string
+                            value:
+                              description: Value of a property to set
+                              type: string
+                          required:
+                          - name
+                          - value
+                          type: object
+                        type: array
+                      windowsOptions:
+                        description: The Windows specific settings applied to all
+                          containers. If unspecified, the options within a container's
+                          SecurityContext will be used. If set in both SecurityContext
+                          and PodSecurityContext, the value specified in SecurityContext
+                          takes precedence.
+                        properties:
+                          gmsaCredentialSpec:
+                            description: GMSACredentialSpec is where the GMSA admission
+                              webhook (https://github.com/kubernetes-sigs/windows-gmsa)
+                              inlines the contents of the GMSA credential spec named
+                              by the GMSACredentialSpecName field.
+                            type: string
+                          gmsaCredentialSpecName:
+                            description: GMSACredentialSpecName is the name of the
+                              GMSA credential spec to use.
+                            type: string
+                          runAsUserName:
+                            description: The UserName in Windows to run the entrypoint
+                              of the container process. Defaults to the user specified
+                              in image metadata if unspecified. May also be set in
+                              PodSecurityContext. If set in both SecurityContext and
+                              PodSecurityContext, the value specified in SecurityContext
+                              takes precedence.
+                            type: string
+                        type: object
+                    type: object
                   serviceAccountName:
                     type: string
                 type: object
@@ -1684,6 +1813,135 @@ spec:
                           to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
                         type: object
                     type: object
+                  securityContext:
+                    description: PodSecurityContext holds pod-level security attributes
+                      and common container settings. Some fields are also present
+                      in container.securityContext.  Field values of container.securityContext
+                      take precedence over field values of PodSecurityContext.
+                    properties:
+                      fsGroup:
+                        description: "A special supplemental group that applies to
+                          all containers in a pod. Some volume types allow the Kubelet
+                          to change the ownership of that volume to be owned by the
+                          pod: \n 1. The owning GID will be the FSGroup 2. The setgid
+                          bit is set (new files created in the volume will be owned
+                          by FSGroup) 3. The permission bits are OR'd with rw-rw----
+                          \n If unset, the Kubelet will not modify the ownership and
+                          permissions of any volume."
+                        format: int64
+                        type: integer
+                      fsGroupChangePolicy:
+                        description: 'fsGroupChangePolicy defines behavior of changing
+                          ownership and permission of the volume before being exposed
+                          inside Pod. This field will only apply to volume types which
+                          support fsGroup based ownership(and permissions). It will
+                          have no effect on ephemeral volume types such as: secret,
+                          configmaps and emptydir. Valid values are "OnRootMismatch"
+                          and "Always". If not specified defaults to "Always".'
+                        type: string
+                      runAsGroup:
+                        description: The GID to run the entrypoint of the container
+                          process. Uses runtime default if unset. May also be set
+                          in SecurityContext.  If set in both SecurityContext and
+                          PodSecurityContext, the value specified in SecurityContext
+                          takes precedence for that container.
+                        format: int64
+                        type: integer
+                      runAsNonRoot:
+                        description: Indicates that the container must run as a non-root
+                          user. If true, the Kubelet will validate the image at runtime
+                          to ensure that it does not run as UID 0 (root) and fail
+                          to start the container if it does. If unset or false, no
+                          such validation will be performed. May also be set in SecurityContext.  If
+                          set in both SecurityContext and PodSecurityContext, the
+                          value specified in SecurityContext takes precedence.
+                        type: boolean
+                      runAsUser:
+                        description: The UID to run the entrypoint of the container
+                          process. Defaults to user specified in image metadata if
+                          unspecified. May also be set in SecurityContext.  If set
+                          in both SecurityContext and PodSecurityContext, the value
+                          specified in SecurityContext takes precedence for that container.
+                        format: int64
+                        type: integer
+                      seLinuxOptions:
+                        description: The SELinux context to be applied to all containers.
+                          If unspecified, the container runtime will allocate a random
+                          SELinux context for each container.  May also be set in
+                          SecurityContext.  If set in both SecurityContext and PodSecurityContext,
+                          the value specified in SecurityContext takes precedence
+                          for that container.
+                        properties:
+                          level:
+                            description: Level is SELinux level label that applies
+                              to the container.
+                            type: string
+                          role:
+                            description: Role is a SELinux role label that applies
+                              to the container.
+                            type: string
+                          type:
+                            description: Type is a SELinux type label that applies
+                              to the container.
+                            type: string
+                          user:
+                            description: User is a SELinux user label that applies
+                              to the container.
+                            type: string
+                        type: object
+                      supplementalGroups:
+                        description: A list of groups applied to the first process
+                          run in each container, in addition to the container's primary
+                          GID.  If unspecified, no groups will be added to any container.
+                        items:
+                          format: int64
+                          type: integer
+                        type: array
+                      sysctls:
+                        description: Sysctls hold a list of namespaced sysctls used
+                          for the pod. Pods with unsupported sysctls (by the container
+                          runtime) might fail to launch.
+                        items:
+                          description: Sysctl defines a kernel parameter to be set
+                          properties:
+                            name:
+                              description: Name of a property to set
+                              type: string
+                            value:
+                              description: Value of a property to set
+                              type: string
+                          required:
+                          - name
+                          - value
+                          type: object
+                        type: array
+                      windowsOptions:
+                        description: The Windows specific settings applied to all
+                          containers. If unspecified, the options within a container's
+                          SecurityContext will be used. If set in both SecurityContext
+                          and PodSecurityContext, the value specified in SecurityContext
+                          takes precedence.
+                        properties:
+                          gmsaCredentialSpec:
+                            description: GMSACredentialSpec is where the GMSA admission
+                              webhook (https://github.com/kubernetes-sigs/windows-gmsa)
+                              inlines the contents of the GMSA credential spec named
+                              by the GMSACredentialSpecName field.
+                            type: string
+                          gmsaCredentialSpecName:
+                            description: GMSACredentialSpecName is the name of the
+                              GMSA credential spec to use.
+                            type: string
+                          runAsUserName:
+                            description: The UserName in Windows to run the entrypoint
+                              of the container process. Defaults to the user specified
+                              in image metadata if unspecified. May also be set in
+                              PodSecurityContext. If set in both SecurityContext and
+                              PodSecurityContext, the value specified in SecurityContext
+                              takes precedence.
+                            type: string
+                        type: object
+                    type: object
                   serviceAccountName:
                     type: string
                 type: object
diff --git a/pkg/resources/jobs/runner.go b/pkg/resources/jobs/runner.go
index 15b816d..ae68690 100644
--- a/pkg/resources/jobs/runner.go
+++ b/pkg/resources/jobs/runner.go
@@ -132,6 +132,7 @@ func NewRunnerJob(k6 *v1alpha1.K6, index int) (*batchv1.Job, error) {
 					RestartPolicy:                corev1.RestartPolicyNever,
 					Affinity:                     k6.Spec.Runner.Affinity,
 					NodeSelector:                 k6.Spec.Runner.NodeSelector,
+					SecurityContext:              &k6.Spec.Runner.SecurityContext,
 					Containers: []corev1.Container{{
 						Image:        image,
 						Name:         "k6",
diff --git a/pkg/resources/jobs/starter.go b/pkg/resources/jobs/starter.go
index 651154a..7f433d7 100644
--- a/pkg/resources/jobs/starter.go
+++ b/pkg/resources/jobs/starter.go
@@ -40,6 +40,7 @@ func NewStarterJob(k6 *v1alpha1.K6, hostname []string) *batchv1.Job {
 	if k6.Spec.Starter.AutomountServiceAccountToken != "" {
 		automountServiceAccountToken, _ = strconv.ParseBool(k6.Spec.Starter.AutomountServiceAccountToken)
 	}
+
 	command, istioEnabled := newIstioCommand(k6.Spec.Scuttle.Enabled, []string{"sh", "-c"})
 	env := newIstioEnvVar(k6.Spec.Scuttle, istioEnabled)
 	return &batchv1.Job{
@@ -61,6 +62,7 @@ func NewStarterJob(k6 *v1alpha1.K6, hostname []string) *batchv1.Job {
 					Affinity:                     k6.Spec.Starter.Affinity,
 					NodeSelector:                 k6.Spec.Starter.NodeSelector,
 					RestartPolicy:                corev1.RestartPolicyNever,
+					SecurityContext:              &k6.Spec.Starter.SecurityContext,
 					Containers: []corev1.Container{
 						containers.NewCurlContainer(hostname, starterImage, command, env),
 					},


Test patch:
diff --git a/pkg/resources/jobs/runner_test.go b/pkg/resources/jobs/runner_test.go
index 9ab8793..6d84c30 100644
--- a/pkg/resources/jobs/runner_test.go
+++ b/pkg/resources/jobs/runner_test.go
@@ -276,6 +276,7 @@ func TestNewRunnerJob(t *testing.T) {
 				Spec: corev1.PodSpec{
 					Hostname:                     "test-1",
 					RestartPolicy:                corev1.RestartPolicyNever,
+					SecurityContext:              &corev1.PodSecurityContext{},
 					Affinity:                     nil,
 					NodeSelector:                 nil,
 					ServiceAccountName:           "default",
@@ -374,6 +375,7 @@ func TestNewRunnerJobNoisy(t *testing.T) {
 					NodeSelector:                 nil,
 					ServiceAccountName:           "default",
 					AutomountServiceAccountToken: &automountServiceAccountToken,
+					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{{
 						Image:     "ghcr.io/grafana/operator:latest-runner",
 						Name:      "k6",
@@ -469,6 +471,7 @@ func TestNewRunnerJobUnpaused(t *testing.T) {
 					NodeSelector:                 nil,
 					ServiceAccountName:           "default",
 					AutomountServiceAccountToken: &automountServiceAccountToken,
+					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{{
 						Image:     "ghcr.io/grafana/operator:latest-runner",
 						Name:      "k6",
@@ -564,6 +567,7 @@ func TestNewRunnerJobArguments(t *testing.T) {
 					NodeSelector:                 nil,
 					ServiceAccountName:           "default",
 					AutomountServiceAccountToken: &automountServiceAccountToken,
+					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{{
 						Image:     "ghcr.io/grafana/operator:latest-runner",
 						Name:      "k6",
@@ -660,6 +664,7 @@ func TestNewRunnerJobServiceAccount(t *testing.T) {
 					NodeSelector:                 nil,
 					ServiceAccountName:           "test",
 					AutomountServiceAccountToken: &automountServiceAccountToken,
+					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{{
 						Image:     "ghcr.io/grafana/operator:latest-runner",
 						Name:      "k6",
@@ -757,6 +762,7 @@ func TestNewRunnerJobIstio(t *testing.T) {
 					NodeSelector:                 nil,
 					ServiceAccountName:           "default",
 					AutomountServiceAccountToken: &automountServiceAccountToken,
+					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{{
 						Image:   "ghcr.io/grafana/operator:latest-runner",
 						Name:    "k6",
@@ -867,6 +873,7 @@ func TestNewRunnerJobLocalFile(t *testing.T) {
 					NodeSelector:                 nil,
 					ServiceAccountName:           "default",
 					AutomountServiceAccountToken: &automountServiceAccountToken,
+					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{{
 						Image:        "ghcr.io/grafana/operator:latest-runner",
 						Name:         "k6",
diff --git a/pkg/resources/jobs/starter_test.go b/pkg/resources/jobs/starter_test.go
index 3b8baeb..a40c66d 100644
--- a/pkg/resources/jobs/starter_test.go
+++ b/pkg/resources/jobs/starter_test.go
@@ -46,6 +46,7 @@ func TestNewStarterJob(t *testing.T) {
 					Affinity:                     nil,
 					NodeSelector:                 nil,
 					RestartPolicy:                corev1.RestartPolicyNever,
+					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{
 						containers.NewCurlContainer([]string{"testing"}, "image", []string{"sh", "-c"},
 							[]corev1.EnvVar{}),
@@ -123,6 +124,7 @@ func TestNewStarterJobIstio(t *testing.T) {
 					Affinity:                     nil,
 					NodeSelector:                 nil,
 					RestartPolicy:                corev1.RestartPolicyNever,
+					SecurityContext:              &corev1.PodSecurityContext{},
 					Containers: []corev1.Container{
 						containers.NewCurlContainer([]string{"testing"}, "image", []string{"scuttle", "sh", "-c"}, []corev1.EnvVar{
 							{