From 472fd920cdd39cf850904f6f659bbabf41143a86 Mon Sep 17 00:00:00 2001 From: xyoxo <49061426+xyoxo@users.noreply.github.com> Date: Mon, 10 May 2021 02:20:30 +0600 Subject: [PATCH] feat(trivy): Configure insecure image registries (#548) Resolves: #545 --- docs/settings.md | 1 + pkg/plugin/trivy/plugin.go | 29 +++ pkg/plugin/trivy/plugin_test.go | 361 +++++++++++++++++++++++++++++++- pkg/starboard/config.go | 12 ++ pkg/starboard/config_test.go | 35 ++++ 5 files changed, 436 insertions(+), 2 deletions(-) diff --git a/docs/settings.md b/docs/settings.md index d6c9b0d16..f9ddb9155 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -68,6 +68,7 @@ The following tables list available configuration settings with their default va | `trivy.mode` | `Standalone` | Trivy client mode. Either `Standalone` or `ClientServer`. Depending on the active mode other settings might be applicable or required. | | `trivy.serverURL` | N/A | The endpoint URL of the Trivy server. Required in `ClientServer` mode. | | `trivy.serverTokenHeader` | `Trivy-Token` | The name of the HTTP header to send the authentication token to Trivy server. Only application in `ClientServer` mode when `trivy.serverToken` is specified. | +| `trivy.insecureRegistry.` | N/A | The registry to which insecure connections are allowed. There can be multiple registries with different registry ``. | | `aqua.imageRef` | `docker.io/aquasec/scanner:5.3` | Aqua scanner image reference. The tag determines the version of the `scanner` binary executable and it must be compatible with version of Aqua console. | | `aqua.serverURL` | N/A | The endpoint URL of Aqua management console | | `kube-bench.imageRef` | `docker.io/aquasec/kube-bench:0.5.0` | kube-bench image reference | diff --git a/pkg/plugin/trivy/plugin.go b/pkg/plugin/trivy/plugin.go index 68e64b587..15f6ef141 100644 --- a/pkg/plugin/trivy/plugin.go +++ b/pkg/plugin/trivy/plugin.go @@ -10,6 +10,7 @@ import ( "github.com/aquasecurity/starboard/pkg/kube" "github.com/aquasecurity/starboard/pkg/starboard" "github.com/aquasecurity/starboard/pkg/vulnerabilityreport" + "github.com/google/go-containerregistry/pkg/name" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -40,6 +41,7 @@ type Config interface { GetTrivyImageRef() (string, error) GetTrivyMode() (starboard.TrivyMode, error) GetTrivyServerURL() (string, error) + GetTrivyInsecureRegistries() map[string]bool } // NewPlugin constructs a new vulnerabilityreport.Plugin, which is using an @@ -271,6 +273,11 @@ func (s *scanner) getPodSpecForStandaloneMode(spec corev1.PodSpec, credentials m }) } + env, err = s.appendTrivyInsecureEnv(c.Image, env) + if err != nil { + return corev1.PodSpec{}, nil, err + } + containers = append(containers, corev1.Container{ Name: c.Name, Image: trivyImageRef, @@ -478,6 +485,11 @@ func (s *scanner) getPodSpecForClientServerMode(spec corev1.PodSpec, credentials }) } + env, err = s.appendTrivyInsecureEnv(container.Image, env) + if err != nil { + return corev1.PodSpec{}, nil, err + } + containers = append(containers, corev1.Container{ Name: container.Name, Image: trivyImageRef, @@ -514,3 +526,20 @@ func (s *scanner) ParseVulnerabilityScanResult(imageRef string, logsReader io.Re } return result, nil } + +func (s *scanner) appendTrivyInsecureEnv(image string, env []corev1.EnvVar) ([]corev1.EnvVar, error) { + ref, err := name.ParseReference(image) + if err != nil { + return nil, err + } + + insecureRegistries := s.config.GetTrivyInsecureRegistries() + if insecureRegistries[ref.Context().RegistryStr()] { + env = append(env, corev1.EnvVar{ + Name: "TRIVY_INSECURE", + Value: "true", + }) + } + + return env, nil +} diff --git a/pkg/plugin/trivy/plugin_test.go b/pkg/plugin/trivy/plugin_test.go index 6295feed4..a0cae05f8 100644 --- a/pkg/plugin/trivy/plugin_test.go +++ b/pkg/plugin/trivy/plugin_test.go @@ -25,7 +25,7 @@ func TestScanner_GetScanJobSpec(t *testing.T) { expectedJobSpec corev1.PodSpec }{ { - name: "With Standalone mode", + name: "Standalone mode without insecure registry", config: starboard.ConfigData{ "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", "trivy.mode": string(starboard.Standalone), @@ -237,7 +237,223 @@ func TestScanner_GetScanJobSpec(t *testing.T) { }, }, { - name: "With ClientServer mode", + name: "Standalone mode with insecure registry", + config: starboard.ConfigData{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(starboard.Standalone), + "trivy.insecureRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", + }, + workloadSpec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + { + Name: "data", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumDefault, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.14.0", + 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: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: starboard.ConfigMapName, + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: starboard.ConfigMapName, + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: starboard.SecretName, + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--download-db-only", + "--cache-dir", "/var/lib/trivy", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "data", + MountPath: "/var/lib/trivy", + ReadOnly: false, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + 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), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: starboard.ConfigMapName, + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: starboard.ConfigMapName, + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_INSECURE", + Value: "true", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--skip-update", + "--cache-dir", "/var/lib/trivy", + "--quiet", + "--format", "json", + "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "data", + ReadOnly: false, + MountPath: "/var/lib/trivy", + }, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{ + RunAsUser: pointer.Int64Ptr(1000), + RunAsGroup: pointer.Int64Ptr(1000), + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, + }, + }, + }, + { + name: "ClientServer mode without insecure registry", config: starboard.ConfigData{ "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", "trivy.mode": string(starboard.ClientServer), @@ -372,6 +588,147 @@ func TestScanner_GetScanJobSpec(t *testing.T) { }, }, }, + { + name: "ClientServer mode with insecure registry", + config: starboard.ConfigData{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(starboard.ClientServer), + "trivy.serverURL": "http://trivy.trivy:4954", + "trivy.insecureRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", + }, + workloadSpec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyNever, + AutomountServiceAccountToken: pointer.BoolPtr(false), + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + 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: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: starboard.ConfigMapName, + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: starboard.ConfigMapName, + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: starboard.ConfigMapName, + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN_HEADER", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: starboard.SecretName, + }, + Key: "trivy.serverTokenHeader", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: starboard.SecretName, + }, + Key: "trivy.serverToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_CUSTOM_HEADERS", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: starboard.SecretName, + }, + Key: "trivy.serverCustomHeaders", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_INSECURE", + Value: "true", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--quiet", + "client", + "--format", + "json", + "--remote", + "http://trivy.trivy:4954", + "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + }, + }, + }, + }, } for _, tc := range testCases { diff --git a/pkg/starboard/config.go b/pkg/starboard/config.go index 5c874451a..fdb90338d 100644 --- a/pkg/starboard/config.go +++ b/pkg/starboard/config.go @@ -3,6 +3,7 @@ package starboard import ( "context" "fmt" + "strings" "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1" "github.com/google/go-containerregistry/pkg/name" @@ -356,6 +357,17 @@ func (c ConfigData) GetTrivyServerURL() (string, error) { return c.getRequiredProperty(keyTrivyServerURL) } +func (c ConfigData) GetTrivyInsecureRegistries() map[string]bool { + insecureRegistries := make(map[string]bool) + for key, val := range c { + if strings.HasPrefix(key, "trivy.insecureRegistry.") { + insecureRegistries[val] = true + } + } + + return insecureRegistries +} + func (c ConfigData) GetAquaImageRef() (string, error) { return c.getRequiredProperty("aqua.imageRef") } diff --git a/pkg/starboard/config_test.go b/pkg/starboard/config_test.go index 262ffdcc7..8124476f7 100644 --- a/pkg/starboard/config_test.go +++ b/pkg/starboard/config_test.go @@ -567,3 +567,38 @@ func TestConfigManager_Delete(t *testing.T) { assert.True(t, errors.IsNotFound(err)) }) } + +func TestConfigData_GetTrivyInsecureRegistries(t *testing.T) { + testCases := []struct { + name string + configData starboard.ConfigData + expectedOutput map[string]bool + }{ + { + name: "Should return nil map when there is no key with trivy.insecureRegistry. prefix", + configData: starboard.ConfigData{ + "foo": "bar", + }, + expectedOutput: make(map[string]bool), + }, + { + name: "Should return insecure registries in map", + configData: starboard.ConfigData{ + "foo": "bar", + "trivy.insecureRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", + "trivy.insecureRegistry.qaRegistry": "qa.registry.aquasec.com", + }, + expectedOutput: map[string]bool{ + "poc.myregistry.harbor.com.pl": true, + "qa.registry.aquasec.com": true, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + insecureRegistries := tc.configData.GetTrivyInsecureRegistries() + assert.Equal(t, tc.expectedOutput, insecureRegistries) + }) + } +}