Repository: aquasecurity/starboard
Pull request #902 (2022-01-13 14:13:16)

feat: add pod template labels to scanJob
This is the preparation for the use of starboard together with https://github.com/Azure/aad-pod-identity. This is necessary to support Azure Acr #278.

also this https://github.com/aquasecurity/fanal/pull/371 is needed  to allow trivy to pull from acr.

codecov[bot]: # [Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/902?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) Report
> Merging [#902](https://codecov.io/gh/aquasecurity/starboard/pull/902?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) (951b681) into [main](https://codecov.io/gh/aquasecurity/starboard/commit/3fcbac4f4f29fdb1f24fb4c4a3baf885fe32e8e8?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) (3fcbac4) will **decrease** coverage by `19.36%`.
> The diff coverage is `32.14%`.

[![Impacted file tree graph](https://codecov.io/gh/aquasecurity/starboard/pull/902/graphs/tree.svg?width=650&height=150&src=pr&token=qqTGKAJ0Wb&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity)](https://codecov.io/gh/aquasecurity/starboard/pull/902?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity)

```diff
@@             Coverage Diff             @@
##             main     #902       +/-   ##
===========================================
- Coverage   64.80%   45.43%   -19.37%     
===========================================
  Files          59       42       -17     
  Lines        6603     5489     -1114     
===========================================
- Hits         4279     2494     -1785     
- Misses       1883     2840      +957     
+ Partials      441      155      -286     
```


| [Impacted Files](https://codecov.io/gh/aquasecurity/starboard/pull/902?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) | Coverage Δ | |
|---|---|---|
| [pkg/configauditreport/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/902/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2NvbmZpZ2F1ZGl0cmVwb3J0L3NjYW5uZXIuZ28=) | `0.00% <0.00%> (-56.39%)` | :arrow_down: |
| [pkg/kubebench/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/902/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2t1YmViZW5jaC9zY2FubmVyLmdv) | `58.30% <0.00%> (-27.53%)` | :arrow_down: |
| [pkg/kubehunter/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/902/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2t1YmVodW50ZXIvc2Nhbm5lci5nbw==) | `0.00% <0.00%> (-75.72%)` | :arrow_down: |
| [pkg/operator/controller/ciskubebenchreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/902/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL2NvbnRyb2xsZXIvY2lza3ViZWJlbmNocmVwb3J0Lmdv) | `0.00% <0.00%> (-55.03%)` | :arrow_down: |
| [pkg/operator/controller/configauditreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/902/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL2NvbnRyb2xsZXIvY29uZmlnYXVkaXRyZXBvcnQuZ28=) | `0.00% <0.00%> (-65.89%)` | :arrow_down: |
| [pkg/operator/controller/vulnerabilityreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/902/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL2NvbnRyb2xsZXIvdnVsbmVyYWJpbGl0eXJlcG9ydC5nbw==) | `0.00% <0.00%> (-59.47%)` | :arrow_down: |
| [pkg/vulnerabilityreport/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/902/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL3Z1bG5lcmFiaWxpdHlyZXBvcnQvc2Nhbm5lci5nbw==) | `0.00% <0.00%> (-60.40%)` | :arrow_down: |
| [pkg/vulnerabilityreport/builder.go](https://codecov.io/gh/aquasecurity/starboard/pull/902/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL3Z1bG5lcmFiaWxpdHlyZXBvcnQvYnVpbGRlci5nbw==) | `70.39% <53.84%> (-9.33%)` | :arrow_down: |
| [pkg/configauditreport/builder.go](https://codecov.io/gh/aquasecurity/starboard/pull/902/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2NvbmZpZ2F1ZGl0cmVwb3J0L2J1aWxkZXIuZ28=) | `67.39% <68.18%> (-11.92%)` | :arrow_down: |
| [pkg/starboard/config.go](https://codecov.io/gh/aquasecurity/starboard/pull/902/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL3N0YXJib2FyZC9jb25maWcuZ28=) | `72.50% <100.00%> (+2.06%)` | :arrow_up: |
| ... and [40 more](https://codecov.io/gh/aquasecurity/starboard/pull/902/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/902?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/902?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity). Last update [3fcbac4...951b681](https://codecov.io/gh/aquasecurity/starboard/pull/902?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity).

chen-keinan: @cgroschupp great job  LGTM ! , it will be great if you could : 
-  rebase your branch with upstream
- add `feat: ` prefix to commit messages to follow git convention
cgroschupp: @chen-keinan done

Patch:
diff --git a/deploy/helm/templates/config.yaml b/deploy/helm/templates/config.yaml
index e75f710..ac5387e 100644
--- a/deploy/helm/templates/config.yaml
+++ b/deploy/helm/templates/config.yaml
@@ -12,6 +12,9 @@ data:
   {{- with .Values.starboard.scanJobAnnotations }}
   scanJob.annotations: {{ . | quote }}
   {{- end }}
+  {{- with .Values.starboard.scanJobPodTemplateLabels }}
+  scanJob.podTemplateLabels: {{ . | quote }}
+  {{- end }}
   {{- if .Values.operator.vulnerabilityScannerEnabled }}
   vulnerabilityReports.scanner: {{ .Values.starboard.vulnerabilityReportsPlugin | quote }}
   {{- end }}
diff --git a/deploy/helm/values.yaml b/deploy/helm/values.yaml
index afc579d..1fe08fa 100644
--- a/deploy/helm/values.yaml
+++ b/deploy/helm/values.yaml
@@ -83,6 +83,10 @@ starboard:
   # annotated with. Example: `foo=bar,env=stage` will annotate the scanner pods with the annotations `foo: bar` and `env: stage`
   scanJobAnnotations: ""
 
+  # scanJobPodTemplateLabels comma-separated representation of the labels which the user wants the scanner pods to be
+  # labeled with. Example: `foo=bar,env=stage` will labeled the scanner pods with the labels `foo: bar` and `env: stage`
+  scanJobPodTemplateLabels: ""
+
 trivy:
   # createConfig indicates whether to create config objects
   createConfig: true
diff --git a/docs/settings.md b/docs/settings.md
index c019cf1..7a96e66 100644
--- a/docs/settings.md
+++ b/docs/settings.md
@@ -58,6 +58,7 @@ configuration settings for common use cases. For example, switch Trivy from [Sta
 | `configAuditReports.scanner`   | `Polaris`                             | The name of the plugin that generates config audit reports. Either `Polaris` or `Conftest`. |
 | `scanJob.tolerations`          | N/A                                   | JSON representation of the [tolerations] to be applied to the scanner pods so that they can run on nodes with matching taints. Example: `'[{"key":"key1", "operator":"Equal", "value":"value1", "effect":"NoSchedule"}]'` |
 | `scanJob.annotations`          | N/A                                   | One-line comma-separated representation of the annotations which the user wants the scanner pods to be annotated with. Example: `foo=bar,env=stage` will annotate the scanner pods with the annotations `foo: bar` and `env: stage` |
+| `scanJob.templateLabel`        | N/A                                   | One-line comma-separated representation of the template labels which the user wants the scanner pods to be labeled with. Example: `foo=bar,env=stage` will labeled the scanner pods with the labels `foo: bar` and `env: stage` |
 | `kube-bench.imageRef`          | `docker.io/aquasec/kube-bench:v0.6.5`  | kube-bench image reference |
 | `kube-hunter.imageRef`         | `docker.io/aquasec/kube-hunter:0.6.3` | kube-hunter image reference |
 | `kube-hunter.quick`            | `"false"`                             | Whether to use kube-hunter's "quick" scanning mode (subnet 24). Set to `"true"` to enable. |
diff --git a/pkg/configauditreport/builder.go b/pkg/configauditreport/builder.go
index 579f013..0517a9e 100644
--- a/pkg/configauditreport/builder.go
+++ b/pkg/configauditreport/builder.go
@@ -12,6 +12,7 @@ import (
 	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/util/validation"
 	"k8s.io/utils/pointer"
@@ -20,12 +21,13 @@ import (
 )
 
 type ScanJobBuilder struct {
-	plugin        Plugin
-	pluginContext starboard.PluginContext
-	timeout       time.Duration
-	object        client.Object
-	tolerations   []corev1.Toleration
-	annotations   map[string]string
+	plugin            Plugin
+	pluginContext     starboard.PluginContext
+	timeout           time.Duration
+	object            client.Object
+	tolerations       []corev1.Toleration
+	annotations       map[string]string
+	podTemplateLabels labels.Set
 }
 
 func NewScanJobBuilder() *ScanJobBuilder {
@@ -62,6 +64,11 @@ func (s *ScanJobBuilder) WithAnnotations(annotations map[string]string) *ScanJob
 	return s
 }
 
+func (s *ScanJobBuilder) WithPodTemplateLabels(podTemplateLabels labels.Set) *ScanJobBuilder {
+	s.podTemplateLabels = podTemplateLabels
+	return s
+}
+
 func (s *ScanJobBuilder) Get() (*batchv1.Job, []*corev1.Secret, error) {
 	jobSpec, secrets, err := s.plugin.GetScanJobSpec(s.pluginContext, s.object)
 	if err != nil {
@@ -80,18 +87,26 @@ func (s *ScanJobBuilder) Get() (*batchv1.Job, []*corev1.Secret, error) {
 		return nil, nil, err
 	}
 
-	labels := map[string]string{
+	labelsSet := labels.Set{
 		starboard.LabelResourceSpecHash:         resourceSpecHash,
 		starboard.LabelPluginConfigHash:         pluginConfigHash,
 		starboard.LabelConfigAuditReportScanner: s.pluginContext.GetName(),
 		starboard.LabelK8SAppManagedBy:          starboard.AppStarboard,
 	}
 
+	podTemplateLabelsSet := make(labels.Set)
+	for index, element := range labelsSet {
+		podTemplateLabelsSet[index] = element
+	}
+	for index, element := range s.podTemplateLabels {
+		podTemplateLabelsSet[index] = element
+	}
+
 	job := &batchv1.Job{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:        GetScanJobName(s.object),
 			Namespace:   s.pluginContext.GetNamespace(),
-			Labels:      labels,
+			Labels:      labelsSet,
 			Annotations: s.annotations,
 		},
 		Spec: batchv1.JobSpec{
@@ -100,7 +115,7 @@ func (s *ScanJobBuilder) Get() (*batchv1.Job, []*corev1.Secret, error) {
 			ActiveDeadlineSeconds: kube.GetActiveDeadlineSeconds(s.timeout),
 			Template: corev1.PodTemplateSpec{
 				ObjectMeta: metav1.ObjectMeta{
-					Labels:      labels,
+					Labels:      podTemplateLabelsSet,
 					Annotations: s.annotations,
 				},
 				Spec: jobSpec,
@@ -122,7 +137,7 @@ func (s *ScanJobBuilder) Get() (*batchv1.Job, []*corev1.Secret, error) {
 		if secret.Labels == nil {
 			secret.Labels = make(map[string]string)
 		}
-		for k, v := range labels {
+		for k, v := range labelsSet {
 			secret.Labels[k] = v
 		}
 		err = kube.ObjectToObjectMetadata(s.object, &secret.ObjectMeta)
@@ -184,18 +199,18 @@ func (b *ReportBuilder) reportName() string {
 }
 
 func (b *ReportBuilder) GetClusterReport() (v1alpha1.ClusterConfigAuditReport, error) {
-	labels := make(map[string]string)
+	labelsSet := make(labels.Set)
 	if b.resourceSpecHash != "" {
-		labels[starboard.LabelResourceSpecHash] = b.resourceSpecHash
+		labelsSet[starboard.LabelResourceSpecHash] = b.resourceSpecHash
 	}
 	if b.pluginConfigHash != "" {
-		labels[starboard.LabelPluginConfigHash] = b.pluginConfigHash
+		labelsSet[starboard.LabelPluginConfigHash] = b.pluginConfigHash
 	}
 
 	report := v1alpha1.ClusterConfigAuditReport{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:   b.reportName(),
-			Labels: labels,
+			Labels: labelsSet,
 		},
 		Report: b.data,
 	}
@@ -220,19 +235,19 @@ func (b *ReportBuilder) GetClusterReport() (v1alpha1.ClusterConfigAuditReport, e
 }
 
 func (b *ReportBuilder) GetReport() (v1alpha1.ConfigAuditReport, error) {
-	labels := make(map[string]string)
+	labelsSet := make(labels.Set)
 	if b.resourceSpecHash != "" {
-		labels[starboard.LabelResourceSpecHash] = b.resourceSpecHash
+		labelsSet[starboard.LabelResourceSpecHash] = b.resourceSpecHash
 	}
 	if b.pluginConfigHash != "" {
-		labels[starboard.LabelPluginConfigHash] = b.pluginConfigHash
+		labelsSet[starboard.LabelPluginConfigHash] = b.pluginConfigHash
 	}
 
 	report := v1alpha1.ConfigAuditReport{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      b.reportName(),
 			Namespace: b.controller.GetNamespace(),
-			Labels:    labels,
+			Labels:    labelsSet,
 		},
 		Report: b.data,
 	}
diff --git a/pkg/configauditreport/scanner.go b/pkg/configauditreport/scanner.go
index b9d81ca..32e0c08 100644
--- a/pkg/configauditreport/scanner.go
+++ b/pkg/configauditreport/scanner.go
@@ -77,6 +77,11 @@ func (s *Scanner) Scan(ctx context.Context, partial kube.ObjectRef) (*ReportBuil
 		return nil, fmt.Errorf("getting scan job annotations: %w", err)
 	}
 
+	scanJobPodTemplateLabels, err := s.config.GetScanJobPodTemplateLabels()
+	if err != nil {
+		return nil, fmt.Errorf("getting scan job template labels: %w", err)
+	}
+
 	klog.V(3).Infof("Scanning with options: %+v", s.opts)
 	job, secrets, err := NewScanJobBuilder().
 		WithPlugin(s.plugin).
@@ -85,6 +90,7 @@ func (s *Scanner) Scan(ctx context.Context, partial kube.ObjectRef) (*ReportBuil
 		WithObject(owner).
 		WithTolerations(scanJobTolerations).
 		WithAnnotations(scanJobAnnotations).
+		WithPodTemplateLabels(scanJobPodTemplateLabels).
 		Get()
 	if err != nil {
 		return nil, fmt.Errorf("constructing scan job: %w", err)
diff --git a/pkg/kubebench/scanner.go b/pkg/kubebench/scanner.go
index 6d6e2bb..67a68d7 100644
--- a/pkg/kubebench/scanner.go
+++ b/pkg/kubebench/scanner.go
@@ -120,14 +120,29 @@ func (s *Scanner) prepareKubeBenchJob(node corev1.Node) (*batchv1.Job, error) {
 		return nil, err
 	}
 
+	scanJobPodTemplateLabels, err := s.config.GetScanJobPodTemplateLabels()
+	if err != nil {
+		return nil, err
+	}
+
+	labelsSet := labels.Set{
+		starboard.LabelResourceKind: string(kube.KindNode),
+		starboard.LabelResourceName: node.Name,
+	}
+
+	podTemplateLabelsSet := make(labels.Set)
+	for index, element := range labelsSet {
+		podTemplateLabelsSet[index] = element
+	}
+	for index, element := range scanJobPodTemplateLabels {
+		podTemplateLabelsSet[index] = element
+	}
+
 	return &batchv1.Job{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      "scan-cisbenchmark-" + kube.ComputeHash(node.Name),
 			Namespace: starboard.NamespaceName,
-			Labels: labels.Set{
-				starboard.LabelResourceKind: string(kube.KindNode),
-				starboard.LabelResourceName: node.Name,
-			},
+			Labels:    labelsSet,
 		},
 		Spec: batchv1.JobSpec{
 			BackoffLimit:          pointer.Int32Ptr(0),
@@ -135,10 +150,7 @@ func (s *Scanner) prepareKubeBenchJob(node corev1.Node) (*batchv1.Job, error) {
 			ActiveDeadlineSeconds: kube.GetActiveDeadlineSeconds(s.opts.ScanJobTimeout),
 			Template: corev1.PodTemplateSpec{
 				ObjectMeta: metav1.ObjectMeta{
-					Labels: labels.Set{
-						starboard.LabelResourceKind: string(kube.KindNode),
-						starboard.LabelResourceName: node.Name,
-					},
+					Labels:      podTemplateLabelsSet,
 					Annotations: scanJobAnnotations,
 				},
 				Spec: templateSpec,
diff --git a/pkg/kubehunter/scanner.go b/pkg/kubehunter/scanner.go
index 81ec319..a82f356 100644
--- a/pkg/kubehunter/scanner.go
+++ b/pkg/kubehunter/scanner.go
@@ -117,6 +117,11 @@ func (s *Scanner) prepareKubeHunterJob() (*batchv1.Job, error) {
 		return nil, err
 	}
 
+	scanJobPodTemplateLabels, err := s.config.GetScanJobPodTemplateLabels()
+	if err != nil {
+		return nil, err
+	}
+
 	var (
 		podSecurityContext       *corev1.PodSecurityContext
 		containerSecurityContext *corev1.SecurityContext
@@ -156,6 +161,7 @@ func (s *Scanner) prepareKubeHunterJob() (*batchv1.Job, error) {
 			Template: corev1.PodTemplateSpec{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: scanJobAnnotations,
+					Labels:      scanJobPodTemplateLabels,
 				},
 				Spec: corev1.PodSpec{
 					ServiceAccountName: starboard.ServiceAccountName,
diff --git a/pkg/operator/controller/ciskubebenchreport.go b/pkg/operator/controller/ciskubebenchreport.go
index d030b0e..d9d8bac 100644
--- a/pkg/operator/controller/ciskubebenchreport.go
+++ b/pkg/operator/controller/ciskubebenchreport.go
@@ -169,16 +169,31 @@ func (r *CISKubeBenchReportReconciler) newScanJob(node *corev1.Node) (*batchv1.J
 		return nil, err
 	}
 
+	scanJobPodTemplateLabels, err := r.ConfigData.GetScanJobPodTemplateLabels()
+	if err != nil {
+		return nil, err
+	}
+
+	labelsSet := labels.Set{
+		starboard.LabelResourceKind:           string(kube.KindNode),
+		starboard.LabelResourceName:           node.Name,
+		starboard.LabelK8SAppManagedBy:        starboard.AppStarboard,
+		starboard.LabelKubeBenchReportScanner: "true",
+	}
+
+	podTemplateLabelsSet := make(labels.Set)
+	for index, element := range labelsSet {
+		podTemplateLabelsSet[index] = element
+	}
+	for index, element := range scanJobPodTemplateLabels {
+		podTemplateLabelsSet[index] = element
+	}
+
 	return &batchv1.Job{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      r.getScanJobName(node),
 			Namespace: r.Config.Namespace,
-			Labels: labels.Set{
-				starboard.LabelResourceKind:           string(kube.KindNode),
-				starboard.LabelResourceName:           node.Name,
-				starboard.LabelK8SAppManagedBy:        starboard.AppStarboard,
-				starboard.LabelKubeBenchReportScanner: "true",
-			},
+			Labels:    labelsSet,
 		},
 		Spec: batchv1.JobSpec{
 			BackoffLimit:          pointer.Int32Ptr(0),
@@ -186,12 +201,7 @@ func (r *CISKubeBenchReportReconciler) newScanJob(node *corev1.Node) (*batchv1.J
 			ActiveDeadlineSeconds: kube.GetActiveDeadlineSeconds(r.Config.ScanJobTimeout),
 			Template: corev1.PodTemplateSpec{
 				ObjectMeta: metav1.ObjectMeta{
-					Labels: labels.Set{
-						starboard.LabelResourceKind:           string(kube.KindNode),
-						starboard.LabelResourceName:           node.Name,
-						starboard.LabelK8SAppManagedBy:        starboard.AppStarboard,
-						starboard.LabelKubeBenchReportScanner: "true",
-					},
+					Labels:      podTemplateLabelsSet,
 					Annotations: scanJobAnnotations,
 				},
 				Spec: templateSpec,
diff --git a/pkg/operator/controller/configauditreport.go b/pkg/operator/controller/configauditreport.go
index fdc5af3..ecb2d15 100644
--- a/pkg/operator/controller/configauditreport.go
+++ b/pkg/operator/controller/configauditreport.go
@@ -237,6 +237,11 @@ func (r *ConfigAuditReportReconciler) reconcileResource(resourceKind kube.Kind)
 			return ctrl.Result{}, fmt.Errorf("getting scan job annotations: %w", err)
 		}
 
+		scanJobPodTemplateLabels, err := r.GetScanJobPodTemplateLabels()
+		if err != nil {
+			return ctrl.Result{}, fmt.Errorf("getting scan job template labels: %w", err)
+		}
+
 		job, secrets, err := configauditreport.NewScanJobBuilder().
 			WithPlugin(r.Plugin).
 			WithPluginContext(r.PluginContext).
@@ -244,6 +249,7 @@ func (r *ConfigAuditReportReconciler) reconcileResource(resourceKind kube.Kind)
 			WithObject(resource).
 			WithTolerations(scanJobTolerations).
 			WithAnnotations(scanJobAnnotations).
+			WithPodTemplateLabels(scanJobPodTemplateLabels).
 			Get()
 		if err != nil {
 			return ctrl.Result{}, fmt.Errorf("constructing scan job: %w", err)
diff --git a/pkg/operator/controller/vulnerabilityreport.go b/pkg/operator/controller/vulnerabilityreport.go
index 1fcd5cf..0a00a6c 100644
--- a/pkg/operator/controller/vulnerabilityreport.go
+++ b/pkg/operator/controller/vulnerabilityreport.go
@@ -232,6 +232,11 @@ func (r *VulnerabilityReportReconciler) submitScanJob(ctx context.Context, owner
 		return fmt.Errorf("getting scan job annotations: %w", err)
 	}
 
+	scanJobPodTemplateLabels, err := r.GetScanJobPodTemplateLabels()
+	if err != nil {
+		return fmt.Errorf("getting scan job template labels: %w", err)
+	}
+
 	scanJob, secrets, err := vulnerabilityreport.NewScanJobBuilder().
 		WithPlugin(r.Plugin).
 		WithPluginContext(r.PluginContext).
@@ -239,6 +244,7 @@ func (r *VulnerabilityReportReconciler) submitScanJob(ctx context.Context, owner
 		WithObject(owner).
 		WithTolerations(scanJobTolerations).
 		WithAnnotations(scanJobAnnotations).
+		WithPodTemplateLabels(scanJobPodTemplateLabels).
 		WithCredentials(credentials).
 		Get()
 
diff --git a/pkg/starboard/config.go b/pkg/starboard/config.go
index 2893a3e..95aed88 100644
--- a/pkg/starboard/config.go
+++ b/pkg/starboard/config.go
@@ -146,6 +146,25 @@ func (c ConfigData) GetScanJobAnnotations() (map[string]string, error) {
 	return scanJobAnnotationsMap, nil
 }
 
+func (c ConfigData) GetScanJobPodTemplateLabels() (labels.Set, error) {
+	scanJobPodTemplateLabelsStr, found := c[AnnotationScanJobPodTemplateLabels]
+	if !found || strings.TrimSpace(scanJobPodTemplateLabelsStr) == "" {
+		return labels.Set{}, nil
+	}
+
+	scanJobPodTemplateLabelsMap := map[string]string{}
+	for _, annotation := range strings.Split(scanJobPodTemplateLabelsStr, ",") {
+		sepByEqual := strings.Split(annotation, "=")
+		if len(sepByEqual) != 2 {
+			return labels.Set{}, fmt.Errorf("custom template labels found to be wrongfully provided: %s", scanJobPodTemplateLabelsStr)
+		}
+		key, value := sepByEqual[0], sepByEqual[1]
+		scanJobPodTemplateLabelsMap[key] = value
+	}
+
+	return scanJobPodTemplateLabelsMap, nil
+}
+
 func (c ConfigData) GetKubeBenchImageRef() (string, error) {
 	return c.GetRequiredData("kube-bench.imageRef")
 }
diff --git a/pkg/starboard/constants.go b/pkg/starboard/constants.go
index d1e528f..c7c7a1a 100644
--- a/pkg/starboard/constants.go
+++ b/pkg/starboard/constants.go
@@ -42,6 +42,7 @@ const (
 )
 
 const (
-	AnnotationContainerImages    = "starboard.container-images"
-	AnnotationScanJobAnnotations = "scanJob.annotations"
+	AnnotationContainerImages          = "starboard.container-images"
+	AnnotationScanJobAnnotations       = "scanJob.annotations"
+	AnnotationScanJobPodTemplateLabels = "scanJob.podTemplateLabels"
 )
diff --git a/pkg/vulnerabilityreport/builder.go b/pkg/vulnerabilityreport/builder.go
index 5474049..b326b3e 100644
--- a/pkg/vulnerabilityreport/builder.go
+++ b/pkg/vulnerabilityreport/builder.go
@@ -12,6 +12,7 @@ import (
 	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/util/validation"
 	"k8s.io/utils/pointer"
@@ -20,13 +21,14 @@ import (
 )
 
 type ScanJobBuilder struct {
-	plugin        Plugin
-	pluginContext starboard.PluginContext
-	timeout       time.Duration
-	object        client.Object
-	credentials   map[string]docker.Auth
-	tolerations   []corev1.Toleration
-	annotations   map[string]string
+	plugin            Plugin
+	pluginContext     starboard.PluginContext
+	timeout           time.Duration
+	object            client.Object
+	credentials       map[string]docker.Auth
+	tolerations       []corev1.Toleration
+	annotations       map[string]string
+	podTemplateLabels labels.Set
 }
 
 func NewScanJobBuilder() *ScanJobBuilder {
@@ -63,6 +65,11 @@ func (s *ScanJobBuilder) WithAnnotations(annotations map[string]string) *ScanJob
 	return s
 }
 
+func (s *ScanJobBuilder) WithPodTemplateLabels(podTemplateLabels labels.Set) *ScanJobBuilder {
+	s.podTemplateLabels = podTemplateLabels
+	return s
+}
+
 func (s *ScanJobBuilder) WithCredentials(credentials map[string]docker.Auth) *ScanJobBuilder {
 	s.credentials = credentials
 	return s
@@ -87,17 +94,24 @@ func (s *ScanJobBuilder) Get() (*batchv1.Job, []*corev1.Secret, error) {
 
 	podSpecHash := kube.ComputeHash(spec)
 
-	labels := map[string]string{
+	labelsSet := map[string]string{
 		starboard.LabelResourceSpecHash:           podSpecHash,
 		starboard.LabelK8SAppManagedBy:            starboard.AppStarboard,
 		starboard.LabelVulnerabilityReportScanner: s.pluginContext.GetName(),
 	}
+	podTemplateLabelsSet := make(labels.Set)
+	for index, element := range labelsSet {
+		podTemplateLabelsSet[index] = element
+	}
+	for index, element := range s.podTemplateLabels {
+		podTemplateLabelsSet[index] = element
+	}
 
 	job := &batchv1.Job{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      GetScanJobName(s.object),
 			Namespace: s.pluginContext.GetNamespace(),
-			Labels:    labels,
+			Labels:    labelsSet,
 			Annotations: map[string]string{
 				starboard.AnnotationContainerImages: containerImagesAsJSON,
 			},
@@ -108,7 +122,7 @@ func (s *ScanJobBuilder) Get() (*batchv1.Job, []*corev1.Secret, error) {
 			ActiveDeadlineSeconds: kube.GetActiveDeadlineSeconds(s.timeout),
 			Template: corev1.PodTemplateSpec{
 				ObjectMeta: metav1.ObjectMeta{
-					Labels:      labels,
+					Labels:      podTemplateLabelsSet,
 					Annotations: s.annotations,
 				},
 				Spec: templateSpec,
diff --git a/pkg/vulnerabilityreport/scanner.go b/pkg/vulnerabilityreport/scanner.go
index e894df7..d7cfa74 100644
--- a/pkg/vulnerabilityreport/scanner.go
+++ b/pkg/vulnerabilityreport/scanner.go
@@ -84,6 +84,11 @@ func (s *Scanner) Scan(ctx context.Context, workload kube.ObjectRef) ([]v1alpha1
 		return nil, fmt.Errorf("getting scan job annotations: %w", err)
 	}
 
+	scanJobPodTemplateLabels, err := s.config.GetScanJobPodTemplateLabels()
+	if err != nil {
+		return nil, fmt.Errorf("getting scan job template labels: %w", err)
+	}
+
 	klog.V(3).Infof("Scanning with options: %+v", s.opts)
 
 	credentials, err := s.secretsReader.CredentialsByWorkload(ctx, owner)
@@ -99,6 +104,7 @@ func (s *Scanner) Scan(ctx context.Context, workload kube.ObjectRef) ([]v1alpha1
 		WithCredentials(credentials).
 		WithTolerations(scanJobTolerations).
 		WithAnnotations(scanJobAnnotations).
+		WithPodTemplateLabels(scanJobPodTemplateLabels).
 		Get()
 
 	if err != nil {


Test patch:
diff --git a/pkg/starboard/config_test.go b/pkg/starboard/config_test.go
index 9f3208f..de5f941 100644
--- a/pkg/starboard/config_test.go
+++ b/pkg/starboard/config_test.go
@@ -11,6 +11,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/client-go/kubernetes/fake"
 )
 
@@ -494,3 +495,56 @@ func TestGetScanJobAnnotations(t *testing.T) {
 		})
 	}
 }
+
+func TestGetScanJobPodTemplateLabels(t *testing.T) {
+	testCases := []struct {
+		name        string
+		config      starboard.ConfigData
+		expected    labels.Set
+		expectError string
+	}{
+		{
+			name: "scan job template labels can be fetched successfully",
+			config: starboard.ConfigData{
+				"scanJob.podTemplateLabels": "a.b=c.d/e,foo=bar",
+			},
+			expected: labels.Set{
+				"foo": "bar",
+				"a.b": "c.d/e",
+			},
+		},
+		{
+			name:     "gracefully deal with unprovided annotations",
+			config:   starboard.ConfigData{},
+			expected: labels.Set{},
+		},
+		{
+			name: "raise an error on being provided with annotations in wrong format",
+			config: starboard.ConfigData{
+				"scanJob.podTemplateLabels": "foo",
+			},
+			expected:    labels.Set{},
+			expectError: "custom template labels found to be wrongfully provided: foo",
+		},
+		{
+			name: "raise an error on being provided with template labels in wrong format",
+			config: starboard.ConfigData{
+				"scanJob.podTemplateLabels": "foo=bar,a=b=c",
+			},
+			expected:    labels.Set{},
+			expectError: "custom template labels found to be wrongfully provided: foo=bar,a=b=c",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			scanJobPodTemplateLabels, err := tc.config.GetScanJobPodTemplateLabels()
+			if tc.expectError != "" {
+				assert.Error(t, err, tc.expectError, tc.name)
+			} else {
+				assert.NoError(t, err, tc.name)
+			}
+			assert.Equal(t, tc.expected, scanJobPodTemplateLabels, tc.name)
+		})
+	}
+}