From f8c635edbafacd24cd33e7d1b2e26085ae9e99f3 Mon Sep 17 00:00:00 2001 From: Idan Frimark <40820488+FrimIdan@users.noreply.github.com> Date: Wed, 10 May 2023 12:09:51 +0300 Subject: [PATCH] Add a support to set the default scanners namespace (#416) --- .../templates/scanner-template-configmap.yaml | 1 + charts/kubeclarity/values.yaml | 7 + runtime_scan/pkg/scanner/job_managment.go | 10 +- .../pkg/scanner/job_managment_test.go | 162 ++++++++++++++++++ 4 files changed, 178 insertions(+), 2 deletions(-) diff --git a/charts/kubeclarity/templates/scanner-template-configmap.yaml b/charts/kubeclarity/templates/scanner-template-configmap.yaml index 30135b6..cb53466 100644 --- a/charts/kubeclarity/templates/scanner-template-configmap.yaml +++ b/charts/kubeclarity/templates/scanner-template-configmap.yaml @@ -17,6 +17,7 @@ data: apiVersion: batch/v1 kind: Job metadata: + namespace: {{ index .Values "kubeclarity-runtime-scan" "namespace" | quote }} labels: {{- toYaml (index .Values "kubeclarity-runtime-scan" "labels") | nindent 8 }} spec: diff --git a/charts/kubeclarity/values.yaml b/charts/kubeclarity/values.yaml index 52d00ca..58b968d 100755 --- a/charts/kubeclarity/values.yaml +++ b/charts/kubeclarity/values.yaml @@ -118,6 +118,13 @@ kubeclarity-runtime-scan: app: kubeclarity-scanner sidecar.istio.io/inject: "false" + ## Scanner jobs namespace. + # If left blank, the scanner jobs will run in the same namespace as pod being scanned. + # If set, the scanner jobs will run in the given namespace unless: + # 1. The scanner job must run in the pod namespace to fetch image pull secrets, OR + # 2. The scanner job must run in the release namespace to fetch service credentials (more info in https://github.com/openclarity/kubeclarity#private-registries-support-for-k8s-runtime-scan) + namespace: "" + ## Scanner pods tolerations. # tolerations: # - key: key1 diff --git a/runtime_scan/pkg/scanner/job_managment.go b/runtime_scan/pkg/scanner/job_managment.go index 91d1161..663d5b6 100644 --- a/runtime_scan/pkg/scanner/job_managment.go +++ b/runtime_scan/pkg/scanner/job_managment.go @@ -284,19 +284,25 @@ func (s *Scanner) createJob(data *scanData) (*batchv1.Job, error) { removeCISDockerBenchmarkScannerFromJob(job) } job.SetName(jobName) - job.SetNamespace(podContext.namespace) + if job.Namespace == "" { + // Set job namespace to pod namespace if not set in the template. + // May be overridden later if support for image pull secrets or SA credentials is needed. + job.SetNamespace(podContext.namespace) + } setJobScanUUID(job, data.scanUUID) setJobImageIDToScan(job, data.imageID) setJobImageHashToScan(job, data.imageHash) setJobImageNameToScan(job, podContext.imageName) if len(podContext.imagePullSecrets) > 0 { + // Job must run on pod namespace to be able to mount image pull secrets. + job.SetNamespace(podContext.namespace) for _, secretName := range podContext.imagePullSecrets { addJobImagePullSecretVolume(job, secretName) } setJobImagePullSecretPath(job) } else { - // Use private repo sa credentials only if there is no imagePullSecret + // Use private repo sa credentials only if there is no imagePullSecret. for _, adder := range s.credentialAdders { if adder.ShouldAdd() { adder.Add(job) diff --git a/runtime_scan/pkg/scanner/job_managment_test.go b/runtime_scan/pkg/scanner/job_managment_test.go index f4cb26b..87be27e 100644 --- a/runtime_scan/pkg/scanner/job_managment_test.go +++ b/runtime_scan/pkg/scanner/job_managment_test.go @@ -734,6 +734,54 @@ spec: cpu: "1000m" `) +var testScannerJobTemplateWithNamespaceSet = []byte(`apiVersion: batch/v1 +kind: Job +metadata: + namespace: test + labels: + app: scanner + sidecar.istio.io/inject: "false" +spec: + backoffLimit: 0 + ttlSecondsAfterFinished: 300 + template: + metadata: + labels: + app: scanner + sidecar.istio.io/inject: "false" + spec: + restartPolicy: Never + containers: + - name: vulnerability-scanner + image: TBD + args: + - scan + env: + - name: REGISTRY_INSECURE + value: "false" + - name: RESULT_SERVICE_HOST + value: kubeclarity.kubeclarity + - name: RESULT_SERVICE_PORT + value: 8888 + securityContext: + capabilities: + drop: + - all + runAsNonRoot: true + runAsGroup: 1001 + runAsUser: 1001 + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "1000Mi" + cpu: "1000m" +`) + var expectedScannerJobTemplate = []byte(`apiVersion: batch/v1 kind: Job metadata: @@ -790,6 +838,62 @@ spec: cpu: "1000m" `) +var expectedScannerJobTemplateWithNamespaceSet = []byte(`apiVersion: batch/v1 +kind: Job +metadata: + namespace: test + labels: + app: scanner + sidecar.istio.io/inject: "false" +spec: + backoffLimit: 0 + ttlSecondsAfterFinished: 300 + template: + metadata: + labels: + app: scanner + sidecar.istio.io/inject: "false" + spec: + restartPolicy: Never + containers: + - name: vulnerability-scanner + image: TBD + args: + - scan + env: + - name: REGISTRY_INSECURE + value: "false" + - name: RESULT_SERVICE_HOST + value: kubeclarity.kubeclarity + - name: RESULT_SERVICE_PORT + value: 8888 + - name: SCAN_UUID + value: "scanUUID" + - name: IMAGE_ID_TO_SCAN + value: "image-id" + - name: IMAGE_HASH_TO_SCAN + value: "image-hash" + - name: IMAGE_NAME_TO_SCAN + value: "image-name" + securityContext: + capabilities: + drop: + - all + runAsNonRoot: true + runAsGroup: 1001 + runAsUser: 1001 + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "1000Mi" + cpu: "1000m" +`) + var expectedScannerJobTemplateWithImagePullSecret = []byte(`apiVersion: batch/v1 kind: Job metadata: @@ -924,6 +1028,14 @@ func TestScanner_createJob(t *testing.T) { err = yaml.Unmarshal(expectedScannerJobTemplate, &expectedScannerJob) assert.NilError(t, err) + var scannerJobTemplateWithNamespaceSet batchv1.Job + err = yaml.Unmarshal(testScannerJobTemplateWithNamespaceSet, &scannerJobTemplateWithNamespaceSet) + assert.NilError(t, err) + + var expectedScannerJobWithNamespaceSet batchv1.Job + err = yaml.Unmarshal(expectedScannerJobTemplateWithNamespaceSet, &expectedScannerJobWithNamespaceSet) + assert.NilError(t, err) + var expectedScannerJobWithImagePullSecret batchv1.Job err = yaml.Unmarshal(expectedScannerJobTemplateWithImagePullSecret, &expectedScannerJobWithImagePullSecret) assert.NilError(t, err) @@ -1000,6 +1112,31 @@ func TestScanner_createJob(t *testing.T) { want: &expectedScannerJob, wantErr: false, }, + { + name: "sanity with scanner namespace set without imagePullSecret", + fields: fields{ + scannerJobTemplate: &scannerJobTemplateWithNamespaceSet, + }, + args: args{ + data: &scanData{ + imageHash: "image-hash", + imageID: "image-id", + contexts: []*imagePodContext{ + { + containerName: "containerName", + podName: "podName", + namespace: "namespace", + imagePullSecrets: []string{}, + imageName: "image-name", + podUID: "podUID", + }, + }, + scanUUID: "scanUUID", + }, + }, + want: &expectedScannerJobWithNamespaceSet, + wantErr: false, + }, { name: "sanity with imagePullSecret", fields: fields{ @@ -1025,6 +1162,31 @@ func TestScanner_createJob(t *testing.T) { want: &expectedScannerJobWithImagePullSecret, wantErr: false, }, + { + name: "sanity with scanner namespace set with imagePullSecret", + fields: fields{ + scannerJobTemplate: &scannerJobTemplateWithNamespaceSet, + }, + args: args{ + data: &scanData{ + imageHash: "image-hash", + imageID: "image-id", + contexts: []*imagePodContext{ + { + containerName: "containerName", + podName: "podName", + namespace: "namespace", + imagePullSecrets: []string{"imagePullSecret"}, + imageName: "image-name", + podUID: "podUID", + }, + }, + scanUUID: "scanUUID", + }, + }, + want: &expectedScannerJobWithImagePullSecret, + wantErr: false, + }, { name: "sanity with credentialAdders", fields: fields{