Repository: aquasecurity/starboard
Pull request #224 (2020-10-23 12:09:08)

feat(operator): Allow configuring scanners
Resolves: #212

Signed-off-by: Daniel Pacak <pacak.daniel@gmail.com>

codecov[bot]: # [Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/224?src=pr&el=h1) Report
> Merging [#224](https://codecov.io/gh/aquasecurity/starboard/pull/224?src=pr&el=desc) into [master](https://codecov.io/gh/aquasecurity/starboard/commit/f7197fb118c92b5992d3d46a1a41db17349395d7?el=desc) will **decrease** coverage by `17.56%`.
> The diff coverage is `46.51%`.

[![Impacted file tree graph](https://codecov.io/gh/aquasecurity/starboard/pull/224/graphs/tree.svg?width=650&height=150&src=pr&token=qqTGKAJ0Wb)](https://codecov.io/gh/aquasecurity/starboard/pull/224?src=pr&el=tree)

```diff
@@             Coverage Diff             @@
##           master     #224       +/-   ##
===========================================
- Coverage   35.60%   18.04%   -17.57%     
===========================================
  Files          38       38               
  Lines        1848     1851        +3     
===========================================
- Hits          658      334      -324     
- Misses       1079     1478      +399     
+ Partials      111       39       -72     
```


| [Impacted Files](https://codecov.io/gh/aquasecurity/starboard/pull/224?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pkg/cmd/cleanup.go](https://codecov.io/gh/aquasecurity/starboard/pull/224/diff?src=pr&el=tree#diff-cGtnL2NtZC9jbGVhbnVwLmdv) | `0.00% <0.00%> (-62.50%)` | :arrow_down: |
| [pkg/cmd/config.go](https://codecov.io/gh/aquasecurity/starboard/pull/224/diff?src=pr&el=tree#diff-cGtnL2NtZC9jb25maWcuZ28=) | `0.00% <0.00%> (-18.92%)` | :arrow_down: |
| [pkg/cmd/find\_vulnerabilities.go](https://codecov.io/gh/aquasecurity/starboard/pull/224/diff?src=pr&el=tree#diff-cGtnL2NtZC9maW5kX3Z1bG5lcmFiaWxpdGllcy5nbw==) | `0.00% <0.00%> (-68.97%)` | :arrow_down: |
| [pkg/cmd/init.go](https://codecov.io/gh/aquasecurity/starboard/pull/224/diff?src=pr&el=tree#diff-cGtnL2NtZC9pbml0Lmdv) | `0.00% <0.00%> (-78.58%)` | :arrow_down: |
| [pkg/cmd/kube\_bench.go](https://codecov.io/gh/aquasecurity/starboard/pull/224/diff?src=pr&el=tree#diff-cGtnL2NtZC9rdWJlX2JlbmNoLmdv) | `0.00% <0.00%> (-53.66%)` | :arrow_down: |
| [pkg/ext/id\_generator.go](https://codecov.io/gh/aquasecurity/starboard/pull/224/diff?src=pr&el=tree#diff-cGtnL2V4dC9pZF9nZW5lcmF0b3IuZ28=) | `100.00% <ø> (ø)` | |
| [pkg/find/vulnerabilities/trivy/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/224/diff?src=pr&el=tree#diff-cGtnL2ZpbmQvdnVsbmVyYWJpbGl0aWVzL3RyaXZ5L3NjYW5uZXIuZ28=) | `0.00% <ø> (ø)` | |
| [pkg/kube/cr\_manager.go](https://codecov.io/gh/aquasecurity/starboard/pull/224/diff?src=pr&el=tree#diff-cGtnL2t1YmUvY3JfbWFuYWdlci5nbw==) | `0.00% <0.00%> (-51.29%)` | :arrow_down: |
| [pkg/kube/runnable\_job.go](https://codecov.io/gh/aquasecurity/starboard/pull/224/diff?src=pr&el=tree#diff-cGtnL2t1YmUvcnVubmFibGVfam9iLmdv) | `0.00% <ø> (-77.15%)` | :arrow_down: |
| [pkg/operator/etc/config.go](https://codecov.io/gh/aquasecurity/starboard/pull/224/diff?src=pr&el=tree#diff-cGtnL29wZXJhdG9yL2V0Yy9jb25maWcuZ28=) | `72.72% <ø> (+3.16%)` | :arrow_up: |
| ... and [19 more](https://codecov.io/gh/aquasecurity/starboard/pull/224/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/224?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/224?src=pr&el=footer). Last update [cf4be11...8c8883d](https://codecov.io/gh/aquasecurity/starboard/pull/224?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/cmd/starboard-operator/main.go b/cmd/starboard-operator/main.go
index c1ab064..d277a17 100644
--- a/cmd/starboard-operator/main.go
+++ b/cmd/starboard-operator/main.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"context"
 	"errors"
 	"fmt"
 
@@ -74,22 +75,22 @@ func main() {
 
 func run() error {
 	setupLog.Info("Starting operator", "version", versionInfo)
-	config, err := etc.GetOperatorConfig()
+	operatorConfig, err := etc.GetOperatorConfig()
 	if err != nil {
 		return fmt.Errorf("getting operator config: %w", err)
 	}
 
-	log.SetLogger(zap.New(zap.UseDevMode(config.Operator.LogDevMode)))
+	log.SetLogger(zap.New(zap.UseDevMode(operatorConfig.Operator.LogDevMode)))
 
 	// Validate configured namespaces to resolve install mode.
-	operatorNamespace, err := config.Operator.GetOperatorNamespace()
+	operatorNamespace, err := operatorConfig.Operator.GetOperatorNamespace()
 	if err != nil {
 		return fmt.Errorf("getting operator namespace: %w", err)
 	}
 
-	targetNamespaces := config.Operator.GetTargetNamespaces()
+	targetNamespaces := operatorConfig.Operator.GetTargetNamespaces()
 
-	installMode, err := config.Operator.GetInstallMode()
+	installMode, err := operatorConfig.Operator.GetInstallMode()
 	if err != nil {
 		return fmt.Errorf("getting install mode: %w", err)
 	}
@@ -100,8 +101,8 @@ func run() error {
 	// Set the default manager options.
 	options := manager.Options{
 		Scheme:                 scheme,
-		MetricsBindAddress:     config.Operator.MetricsBindAddress,
-		HealthProbeBindAddress: config.Operator.HealthProbeBindAddress,
+		MetricsBindAddress:     operatorConfig.Operator.MetricsBindAddress,
+		HealthProbeBindAddress: operatorConfig.Operator.HealthProbeBindAddress,
 	}
 
 	switch installMode {
@@ -158,16 +159,27 @@ func run() error {
 		return err
 	}
 
+	configManager := starboard.NewConfigManager(kubernetesClientset, operatorNamespace)
+	err = configManager.EnsureDefault(context.Background())
+	if err != nil {
+		return err
+	}
+
+	starboardConfig, err := configManager.Read(context.Background())
+	if err != nil {
+		return err
+	}
+
 	store := reports.NewStore(mgr.GetClient(), scheme)
 	idGenerator := ext.NewGoogleUUIDGenerator()
 
-	scanner, err := getEnabledScanner(idGenerator, config)
+	scanner, err := getEnabledScanner(idGenerator, operatorConfig, starboardConfig)
 	if err != nil {
 		return err
 	}
 
 	if err = (&pod.PodController{
-		Config:      config.Operator,
+		Config:      operatorConfig.Operator,
 		Client:      mgr.GetClient(),
 		IDGenerator: idGenerator,
 		Store:       store,
@@ -178,7 +190,7 @@ func run() error {
 	}
 
 	if err = (&job.JobController{
-		Config:     config.Operator,
+		Config:     operatorConfig.Operator,
 		LogsReader: logs.NewReader(kubernetesClientset),
 		Client:     mgr.GetClient(),
 		Store:      store,
@@ -196,7 +208,7 @@ func run() error {
 	return nil
 }
 
-func getEnabledScanner(idGenerator ext.IDGenerator, config etc.Config) (scanner.VulnerabilityScanner, error) {
+func getEnabledScanner(idGenerator ext.IDGenerator, config etc.Config, starboardConfig starboard.ConfigData) (scanner.VulnerabilityScanner, error) {
 	if config.ScannerTrivy.Enabled && config.ScannerAquaCSP.Enabled {
 		return nil, fmt.Errorf("invalid configuration: multiple vulnerability scanners enabled")
 	}
@@ -204,8 +216,8 @@ func getEnabledScanner(idGenerator ext.IDGenerator, config etc.Config) (scanner.
 		return nil, fmt.Errorf("invalid configuration: none vulnerability scanner enabled")
 	}
 	if config.ScannerTrivy.Enabled {
-		setupLog.Info("Using Trivy as vulnerability scanner", "image", config.ScannerTrivy.ImageRef)
-		return trivy.NewScanner(idGenerator, config.ScannerTrivy), nil
+		setupLog.Info("Using Trivy as vulnerability scanner", "image", starboardConfig.GetTrivyImageRef())
+		return trivy.NewScanner(idGenerator, starboardConfig), nil
 	}
 	if config.ScannerAquaCSP.Enabled {
 		setupLog.Info("Using Aqua CSP as vulnerability scanner", "image", config.ScannerAquaCSP.ImageRef)
diff --git a/deploy/helm/templates/rbac.yaml b/deploy/helm/templates/rbac.yaml
index 984d727..85fe4c0 100644
--- a/deploy/helm/templates/rbac.yaml
+++ b/deploy/helm/templates/rbac.yaml
@@ -36,6 +36,13 @@ rules:
       - get
       - list
       - watch
+  - apiGroups:
+      - ""
+    resources:
+      - configmaps
+    verbs:
+      - get
+      - create
   - apiGroups:
       - apps
     resources: # resources that own pods are inspected
diff --git a/deploy/kubectl/03-starboard-operator.clusterrole.yaml b/deploy/kubectl/03-starboard-operator.clusterrole.yaml
index 23c50b8..f9a6d8d 100644
--- a/deploy/kubectl/03-starboard-operator.clusterrole.yaml
+++ b/deploy/kubectl/03-starboard-operator.clusterrole.yaml
@@ -14,6 +14,13 @@ rules:
       - get
       - list
       - watch
+  - apiGroups:
+      - ""
+    resources:
+      - configmaps
+    verbs:
+      - get
+      - create
   - apiGroups:
       - apps
     resources:
diff --git a/pkg/cmd/cleanup.go b/pkg/cmd/cleanup.go
index 429a0e5..d49cdb4 100644
--- a/pkg/cmd/cleanup.go
+++ b/pkg/cmd/cleanup.go
@@ -3,6 +3,8 @@ package cmd
 import (
 	"context"
 
+	"github.com/aquasecurity/starboard/pkg/starboard"
+
 	"github.com/aquasecurity/starboard/pkg/kube"
 	"github.com/spf13/cobra"
 	extapi "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
@@ -14,22 +16,22 @@ func NewCleanupCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "cleanup",
 		Short: "Delete custom resource definitions created by starboard",
-		RunE: func(cmd *cobra.Command, args []string) (err error) {
+		RunE: func(cmd *cobra.Command, args []string) error {
 			ctx := context.Background()
 			config, err := cf.ToRESTConfig()
 			if err != nil {
-				return
+				return err
 			}
 			clientset, err := kubernetes.NewForConfig(config)
 			if err != nil {
-				return
+				return err
 			}
 			clientsetext, err := extapi.NewForConfig(config)
 			if err != nil {
-				return
+				return err
 			}
-			err = kube.NewCRManager(clientset, clientsetext).Cleanup(ctx)
-			return
+			return kube.NewCRManager(starboard.NewConfigManager(clientset, starboard.NamespaceName), clientset, clientsetext).
+				Cleanup(ctx)
 		},
 	}
 	return cmd
diff --git a/pkg/cmd/config.go b/pkg/cmd/config.go
index bdf974d..778960d 100644
--- a/pkg/cmd/config.go
+++ b/pkg/cmd/config.go
@@ -33,7 +33,7 @@ func NewConfigCmd(cf *genericclioptions.ConfigFlags, outWriter io.Writer) *cobra
 			if err != nil {
 				return
 			}
-			config, err := starboard.NewConfigReader(clientset).Read(ctx)
+			config, err := starboard.NewConfigManager(clientset, starboard.NamespaceName).Read(ctx)
 			if err != nil {
 				return
 			}
diff --git a/pkg/cmd/find_vulnerabilities.go b/pkg/cmd/find_vulnerabilities.go
index cb13670..3c5f7df 100644
--- a/pkg/cmd/find_vulnerabilities.go
+++ b/pkg/cmd/find_vulnerabilities.go
@@ -72,7 +72,7 @@ NAME is the name of a particular Kubernetes workload.
 			if err != nil {
 				return err
 			}
-			config, err := starboard.NewConfigReader(kubernetesClientset).Read(ctx)
+			config, err := starboard.NewConfigManager(kubernetesClientset, starboard.NamespaceName).Read(ctx)
 			if err != nil {
 				return
 			}
diff --git a/pkg/cmd/init.go b/pkg/cmd/init.go
index 1c3aeed..8c0239b 100644
--- a/pkg/cmd/init.go
+++ b/pkg/cmd/init.go
@@ -3,6 +3,8 @@ package cmd
 import (
 	"context"
 
+	"github.com/aquasecurity/starboard/pkg/starboard"
+
 	"github.com/aquasecurity/starboard/pkg/kube"
 	"github.com/spf13/cobra"
 	extensionsapi "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
@@ -14,8 +16,7 @@ func NewInitCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "init",
 		Short: "Create custom resource definitions used by starboard",
-		Long: `
-Create all the resources used by starboard.  It will create the following
+		Long: `Create all the resources used by starboard. It will create the following
 in the cluster:
 
  - custom resource definitions
@@ -27,24 +28,23 @@ in the cluster:
 The config map contains the default configuration parameters. However this
 can be modified to change the behaviour of the scanner.
 
-These resources can be removed from the cluster using the "cleanup" command.
-`,
-		RunE: func(cmd *cobra.Command, args []string) (err error) {
+These resources can be removed from the cluster using the "cleanup" command.`,
+		RunE: func(cmd *cobra.Command, args []string) error {
 			ctx := context.Background()
 			config, err := cf.ToRESTConfig()
 			if err != nil {
-				return
+				return err
 			}
 			clientset, err := kubernetes.NewForConfig(config)
 			if err != nil {
-				return
+				return err
 			}
 			clientsetext, err := extensionsapi.NewForConfig(config)
 			if err != nil {
-				return
+				return err
 			}
-			err = kube.NewCRManager(clientset, clientsetext).Init(ctx)
-			return
+			return kube.NewCRManager(starboard.NewConfigManager(clientset, starboard.NamespaceName), clientset, clientsetext).
+				Init(ctx)
 		},
 	}
 	return cmd
diff --git a/pkg/cmd/kube_bench.go b/pkg/cmd/kube_bench.go
index f6c05c2..a304533 100644
--- a/pkg/cmd/kube_bench.go
+++ b/pkg/cmd/kube_bench.go
@@ -46,7 +46,7 @@ func NewKubeBenchCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
 				err = fmt.Errorf("listing nodes: %w", err)
 				return
 			}
-			config, err := starboard.NewConfigReader(kubernetesClientset).Read(ctx)
+			config, err := starboard.NewConfigManager(kubernetesClientset, starboard.NamespaceName).Read(ctx)
 			if err != nil {
 				return err
 			}
diff --git a/pkg/ext/id_generator.go b/pkg/ext/id_generator.go
index 5666237..e45555f 100644
--- a/pkg/ext/id_generator.go
+++ b/pkg/ext/id_generator.go
@@ -2,8 +2,9 @@ package ext
 
 import (
 	"fmt"
-	"github.com/google/uuid"
 	"sync/atomic"
+
+	"github.com/google/uuid"
 )
 
 // IDGenerator defines contract for generating universally unique identifiers.
diff --git a/pkg/find/vulnerabilities/scanner.go b/pkg/find/vulnerabilities/scanner.go
index 844dccc..e9f6114 100644
--- a/pkg/find/vulnerabilities/scanner.go
+++ b/pkg/find/vulnerabilities/scanner.go
@@ -2,6 +2,7 @@ package vulnerabilities
 
 import (
 	"context"
+
 	"github.com/aquasecurity/starboard/pkg/docker"
 
 	starboard "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
diff --git a/pkg/find/vulnerabilities/trivy/scanner.go b/pkg/find/vulnerabilities/trivy/scanner.go
index 823adab..47e14bf 100644
--- a/pkg/find/vulnerabilities/trivy/scanner.go
+++ b/pkg/find/vulnerabilities/trivy/scanner.go
@@ -3,9 +3,10 @@ package trivy
 import (
 	"context"
 	"fmt"
-	"github.com/aquasecurity/starboard/pkg/starboard"
 	"io"
 
+	"github.com/aquasecurity/starboard/pkg/starboard"
+
 	"github.com/aquasecurity/starboard/pkg/docker"
 	"github.com/aquasecurity/starboard/pkg/kube/secrets"
 	"github.com/aquasecurity/starboard/pkg/scanners"
diff --git a/pkg/kube/cr_manager.go b/pkg/kube/cr_manager.go
index 8a66f78..a7add34 100644
--- a/pkg/kube/cr_manager.go
+++ b/pkg/kube/cr_manager.go
@@ -3,9 +3,10 @@ package kube
 import (
 	"context"
 	"fmt"
-	"github.com/aquasecurity/starboard/pkg/starboard"
 	"time"
 
+	"github.com/aquasecurity/starboard/pkg/starboard"
+
 	"k8s.io/utils/pointer"
 
 	"k8s.io/apimachinery/pkg/labels"
@@ -44,15 +45,6 @@ var (
 		},
 		AutomountServiceAccountToken: pointer.BoolPtr(false),
 	}
-	configMap = &core.ConfigMap{
-		ObjectMeta: meta.ObjectMeta{
-			Name: starboard.ConfigMapName,
-			Labels: labels.Set{
-				"app.kubernetes.io/managed-by": "starboard",
-			},
-		},
-		Data: starboard.GetDefaultConfig(),
-	}
 	clusterRole = &rbac.ClusterRole{
 		ObjectMeta: meta.ObjectMeta{
 			Name: clusterRoleStarboard,
@@ -136,15 +128,20 @@ type CRManager interface {
 }
 
 type crManager struct {
-	clientset    kubernetes.Interface
-	clientsetext extapi.ApiextensionsV1beta1Interface
+	configManager starboard.ConfigManager
+	clientset     kubernetes.Interface
+	clientsetext  extapi.ApiextensionsV1beta1Interface
 }
 
-// NewCRManager constructs a CRManager with the given Kubernetes interface.
-func NewCRManager(clientset kubernetes.Interface, clientsetext extapi.ApiextensionsV1beta1Interface) CRManager {
+// NewCRManager constructs a CRManager with the given starboard.ConfigManager and kubernetes.Interface.
+func NewCRManager(
+	configManager starboard.ConfigManager,
+	clientset kubernetes.Interface,
+	clientsetext extapi.ApiextensionsV1beta1Interface) CRManager {
 	return &crManager{
-		clientset:    clientset,
-		clientsetext: clientsetext,
+		configManager: configManager,
+		clientset:     clientset,
+		clientsetext:  clientsetext,
 	}
 }
 
@@ -175,7 +172,7 @@ func (m *crManager) Init(ctx context.Context) (err error) {
 		return
 	}
 
-	err = m.createConfigMapIfNotFound(ctx, configMap)
+	err = m.configManager.EnsureDefault(ctx)
 	if err != nil {
 		return
 	}
@@ -275,21 +272,6 @@ func (m *crManager) createServiceAccountIfNotFound(ctx context.Context, sa *core
 	return
 }
 
-func (m *crManager) createConfigMapIfNotFound(ctx context.Context, cm *core.ConfigMap) (err error) {
-	name := cm.Name
-	_, err = m.clientset.CoreV1().ConfigMaps(starboard.NamespaceName).Get(ctx, name, meta.GetOptions{})
-	switch {
-	case err == nil:
-		klog.V(3).Infof("ConfigMap %q already exists", starboard.NamespaceName+"/"+name)
-		return
-	case errors.IsNotFound(err):
-		klog.V(3).Infof("Creating ConfigMap %q", starboard.NamespaceName+"/"+name)
-		_, err = m.clientset.CoreV1().ConfigMaps(starboard.NamespaceName).Create(ctx, cm, meta.CreateOptions{})
-		return
-	}
-	return
-}
-
 func (m *crManager) createOrUpdateClusterRole(ctx context.Context, cr *rbac.ClusterRole) (err error) {
 	existingRole, err := m.clientset.RbacV1().ClusterRoles().Get(ctx, cr.GetName(), meta.GetOptions{})
 	switch {
@@ -374,9 +356,8 @@ func (m *crManager) Cleanup(ctx context.Context) (err error) {
 		return
 	}
 
-	klog.V(3).Infof("Deleting ConfigMap %q", starboard.NamespaceName+"/"+starboard.ConfigMapName)
-	err = m.clientset.CoreV1().ConfigMaps(starboard.NamespaceName).Delete(ctx, starboard.ConfigMapName, meta.DeleteOptions{})
-	if err != nil && !errors.IsNotFound(err) {
+	err = m.configManager.Delete(ctx)
+	if err != nil {
 		return
 	}
 
diff --git a/pkg/kube/runnable_job.go b/pkg/kube/runnable_job.go
index e932512..1a8ae25 100644
--- a/pkg/kube/runnable_job.go
+++ b/pkg/kube/runnable_job.go
@@ -3,9 +3,10 @@ package kube
 import (
 	"context"
 	"fmt"
-	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"time"
 
+	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
+
 	"k8s.io/klog"
 
 	"k8s.io/apimachinery/pkg/util/wait"
diff --git a/pkg/kubehunter/model.go b/pkg/kubehunter/model.go
index 195ee76..32587d0 100644
--- a/pkg/kubehunter/model.go
+++ b/pkg/kubehunter/model.go
@@ -2,8 +2,9 @@ package kubehunter
 
 import (
 	"encoding/json"
-	sec "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
 	"io"
+
+	sec "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
 )
 
 func toSummary(vulnerabilities []sec.KubeHunterVulnerability) (summary sec.KubeHunterSummary) {
diff --git a/pkg/operator/etc/config.go b/pkg/operator/etc/config.go
index 1d10bfb..2dc438a 100644
--- a/pkg/operator/etc/config.go
+++ b/pkg/operator/etc/config.go
@@ -29,12 +29,7 @@ type Operator struct {
 }
 
 type ScannerTrivy struct {
-	Enabled  bool   `env:"OPERATOR_SCANNER_TRIVY_ENABLED" envDefault:"true"`
-	ImageRef string `env:"OPERATOR_SCANNER_TRIVY_IMAGE" envDefault:"aquasec/trivy:0.11.0"`
-}
-
-func (c ScannerTrivy) GetTrivyImageRef() string {
-	return c.ImageRef
+	Enabled bool `env:"OPERATOR_SCANNER_TRIVY_ENABLED" envDefault:"true"`
 }
 
 type ScannerAquaCSP struct {
diff --git a/pkg/operator/trivy/scanner.go b/pkg/operator/trivy/scanner.go
index 502e13c..335751b 100644
--- a/pkg/operator/trivy/scanner.go
+++ b/pkg/operator/trivy/scanner.go
@@ -3,12 +3,12 @@ package trivy
 import (
 	"io"
 
-	"github.com/aquasecurity/starboard/pkg/ext"
+	"github.com/aquasecurity/starboard/pkg/starboard"
 
-	"github.com/aquasecurity/starboard/pkg/find/vulnerabilities/trivy"
-	"github.com/aquasecurity/starboard/pkg/operator/etc"
+	"github.com/aquasecurity/starboard/pkg/ext"
 
 	"github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
+	"github.com/aquasecurity/starboard/pkg/find/vulnerabilities/trivy"
 	"github.com/aquasecurity/starboard/pkg/operator/scanner"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
@@ -17,12 +17,12 @@ import (
 
 type trivyScanner struct {
 	idGenerator ext.IDGenerator
-	config      etc.ScannerTrivy
+	config      trivy.Config
 }
 
 // NewScanner constructs a new VulnerabilityScanner, which is using an official
 // Trivy container image to scan pod containers.
-func NewScanner(idGenerator ext.IDGenerator, config etc.ScannerTrivy) scanner.VulnerabilityScanner {
+func NewScanner(idGenerator ext.IDGenerator, config trivy.Config) scanner.VulnerabilityScanner {
 	return &trivyScanner{
 		idGenerator: idGenerator,
 		config:      config,
@@ -33,9 +33,35 @@ func (s *trivyScanner) GetPodTemplateSpec(spec corev1.PodSpec, options scanner.O
 	initContainers := []corev1.Container{
 		{
 			Name:                     s.idGenerator.GenerateID(),
-			Image:                    s.config.ImageRef,
+			Image:                    s.config.GetTrivyImageRef(),
 			ImagePullPolicy:          corev1.PullIfNotPresent,
 			TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
+			Env: []corev1.EnvVar{
+				{
+					Name: "HTTP_PROXY",
+					ValueFrom: &corev1.EnvVarSource{
+						ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
+							LocalObjectReference: corev1.LocalObjectReference{
+								Name: starboard.ConfigMapName,
+							},
+							Key:      "trivy.httpProxy",
+							Optional: pointer.BoolPtr(true),
+						},
+					},
+				},
+				{
+					Name: "GITHUB_TOKEN",
+					ValueFrom: &corev1.EnvVarSource{
+						ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
+							LocalObjectReference: corev1.LocalObjectReference{
+								Name: starboard.ConfigMapName,
+							},
+							Key:      "trivy.githubToken",
+							Optional: pointer.BoolPtr(true),
+						},
+					},
+				},
+			},
 			Command: []string{
 				"trivy",
 			},
@@ -66,14 +92,38 @@ func (s *trivyScanner) GetPodTemplateSpec(spec corev1.PodSpec, options scanner.O
 
 	containers := make([]corev1.Container, len(spec.Containers))
 	for i, c := range spec.Containers {
-		var envs []corev1.EnvVar
 
 		containers[i] = corev1.Container{
 			Name:                     c.Name,
-			Image:                    s.config.ImageRef,
+			Image:                    s.config.GetTrivyImageRef(),
 			ImagePullPolicy:          corev1.PullIfNotPresent,
 			TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
-			Env:                      envs,
+			Env: []corev1.EnvVar{
+				{
+					Name: "TRIVY_SEVERITY",
+					ValueFrom: &corev1.EnvVarSource{
+						ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
+							LocalObjectReference: corev1.LocalObjectReference{
+								Name: starboard.ConfigMapName,
+							},
+							Key:      "trivy.severity",
+							Optional: pointer.BoolPtr(true),
+						},
+					},
+				},
+				{
+					Name: "HTTP_PROXY",
+					ValueFrom: &corev1.EnvVarSource{
+						ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
+							LocalObjectReference: corev1.LocalObjectReference{
+								Name: starboard.ConfigMapName,
+							},
+							Key:      "trivy.httpProxy",
+							Optional: pointer.BoolPtr(true),
+						},
+					},
+				},
+			},
 			Command: []string{
 				"trivy",
 			},
diff --git a/pkg/starboard/config.go b/pkg/starboard/config.go
index 6ae7b4a..6220eb6 100644
--- a/pkg/starboard/config.go
+++ b/pkg/starboard/config.go
@@ -4,7 +4,10 @@ import (
 	"context"
 	"fmt"
 
+	corev1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/client-go/kubernetes"
 
 	"github.com/google/go-containerregistry/pkg/name"
@@ -209,8 +212,11 @@ type BuildInfo struct {
 // of key-value pairs.
 type ConfigData map[string]string
 
-type ConfigReader interface {
+// ConfigManager defines methods for managing ConfigData.
+type ConfigManager interface {
+	EnsureDefault(ctx context.Context) error
 	Read(ctx context.Context) (ConfigData, error)
+	Delete(ctx context.Context) error
 }
 
 // GetDefaultConfig returns the default configuration data.
@@ -256,22 +262,50 @@ func GetVersionFromImageRef(imageRef string) (string, error) {
 	return version, nil
 }
 
-// NewConfigReader constructs a new ConfigReader that is using client-go
-// interfaces to read ConfigData from the ConfigMap.
-func NewConfigReader(client kubernetes.Interface) ConfigReader {
-	return &configReader{
-		client: client,
+// NewConfigManager constructs a new ConfigManager that is using kubernetes.Interface
+// to manage ConfigData backed by the the ConfigMap stored in the specified namespace.
+func NewConfigManager(client kubernetes.Interface, namespace string) ConfigManager {
+	return &configManager{
+		client:    client,
+		namespace: namespace,
 	}
 }
 
-type configReader struct {
-	client kubernetes.Interface
+type configManager struct {
+	client    kubernetes.Interface
+	namespace string
 }
 
-func (c *configReader) Read(ctx context.Context) (ConfigData, error) {
-	cm, err := c.client.CoreV1().ConfigMaps(NamespaceName).Get(ctx, ConfigMapName, metav1.GetOptions{})
+func (c *configManager) EnsureDefault(ctx context.Context) error {
+	cm := &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: c.namespace,
+			Name:      ConfigMapName,
+			Labels: labels.Set{
+				"app.kubernetes.io/managed-by": "starboard",
+			},
+		},
+		Data: GetDefaultConfig(),
+	}
+	_, err := c.client.CoreV1().ConfigMaps(c.namespace).Create(ctx, cm, metav1.CreateOptions{})
+	if !apierrors.IsAlreadyExists(err) {
+		return err
+	}
+	return nil
+}
+
+func (c *configManager) Read(ctx context.Context) (ConfigData, error) {
+	cm, err := c.client.CoreV1().ConfigMaps(c.namespace).Get(ctx, ConfigMapName, metav1.GetOptions{})
 	if err != nil {
 		return nil, err
 	}
 	return cm.Data, nil
 }
+
+func (c *configManager) Delete(ctx context.Context) error {
+	err := c.client.CoreV1().ConfigMaps(c.namespace).Delete(ctx, ConfigMapName, metav1.DeleteOptions{})
+	if apierrors.IsNotFound(err) {
+		return nil
+	}
+	return err
+}


Test patch:
diff --git a/pkg/kubebench/converter_test.go b/pkg/kubebench/converter_test.go
index 383b577..3598ab5 100644
--- a/pkg/kubebench/converter_test.go
+++ b/pkg/kubebench/converter_test.go
@@ -3,10 +3,11 @@ package kubebench_test
 import (
 	"encoding/json"
 	"errors"
-	"github.com/aquasecurity/starboard/pkg/kubebench"
 	"os"
 	"testing"
 
+	"github.com/aquasecurity/starboard/pkg/kubebench"
+
 	"github.com/aquasecurity/starboard/pkg/starboard"
 
 	starboardv1alpha1 "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
diff --git a/pkg/starboard/config_test.go b/pkg/starboard/config_test.go
index 3d2a61d..083a3cd 100644
--- a/pkg/starboard/config_test.go
+++ b/pkg/starboard/config_test.go
@@ -4,6 +4,8 @@ import (
 	"context"
 	"testing"
 
+	"k8s.io/apimachinery/pkg/api/errors"
+
 	"github.com/stretchr/testify/require"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -104,7 +106,7 @@ func TestConfigData_GetKubeBenchImageRef(t *testing.T) {
 	}
 }
 
-func TestConfigReader_Read(t *testing.T) {
+func TestConfigManager_Read(t *testing.T) {
 	clientset := fake.NewSimpleClientset(&corev1.ConfigMap{
 		ObjectMeta: metav1.ObjectMeta{
 			Namespace: starboard.NamespaceName,
@@ -114,9 +116,77 @@ func TestConfigReader_Read(t *testing.T) {
 			"foo": "bar",
 		},
 	})
-	configData, err := starboard.NewConfigReader(clientset).Read(context.TODO())
+	configData, err := starboard.NewConfigManager(clientset, starboard.NamespaceName).Read(context.TODO())
 	require.NoError(t, err)
 	assert.Equal(t, starboard.ConfigData{
 		"foo": "bar",
 	}, configData)
 }
+
+func TestConfigManager_EnsureDefault(t *testing.T) {
+
+	t.Run("Should create the ConfigMap with the default configuration settings", func(t *testing.T) {
+		clientset := fake.NewSimpleClientset()
+
+		err := starboard.NewConfigManager(clientset, starboard.NamespaceName).EnsureDefault(context.TODO())
+		require.NoError(t, err)
+
+		cm, err := clientset.CoreV1().
+			ConfigMaps(starboard.NamespaceName).
+			Get(context.TODO(), starboard.ConfigMapName, metav1.GetOptions{})
+		require.NoError(t, err)
+		assert.Equal(t, starboard.GetDefaultConfig(), starboard.ConfigData(cm.Data))
+	})
+
+	t.Run("Should not modify the ConfigMap if it already exists", func(t *testing.T) {
+		clientset := fake.NewSimpleClientset(&corev1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: starboard.NamespaceName,
+				Name:      starboard.ConfigMapName,
+			},
+			Data: map[string]string{
+				"foo": "bar",
+			},
+		})
+
+		err := starboard.NewConfigManager(clientset, starboard.NamespaceName).EnsureDefault(context.TODO())
+		require.NoError(t, err)
+
+		cm, err := clientset.CoreV1().
+			ConfigMaps(starboard.NamespaceName).
+			Get(context.TODO(), starboard.ConfigMapName, metav1.GetOptions{})
+		require.NoError(t, err)
+
+		assert.Equal(t, map[string]string{
+			"foo": "bar",
+		}, cm.Data)
+	})
+
+}
+
+func TestConfigManager_Delete(t *testing.T) {
+
+	t.Run("Should not return error when ConfigMap does not exist", func(t *testing.T) {
+		clientset := fake.NewSimpleClientset()
+		err := starboard.NewConfigManager(clientset, starboard.NamespaceName).Delete(context.TODO())
+		require.NoError(t, err)
+	})
+
+	t.Run("Should delete the ConfigMap", func(t *testing.T) {
+		clientset := fake.NewSimpleClientset(&corev1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: starboard.NamespaceName,
+				Name:      starboard.ConfigMapName,
+			},
+			Data: map[string]string{
+				"foo": "bar",
+			},
+		})
+
+		err := starboard.NewConfigManager(clientset, starboard.NamespaceName).Delete(context.TODO())
+		require.NoError(t, err)
+
+		_, err = clientset.CoreV1().ConfigMaps(starboard.NamespaceName).Get(context.TODO(), starboard.ConfigMapName, metav1.GetOptions{})
+		assert.True(t, errors.IsNotFound(err))
+	})
+}