Skip to content

Commit

Permalink
fix: Handle legacy image pull secrets of type kubernetes.io/dockercfg
Browse files Browse the repository at this point in the history
Resolves: #569

Signed-off-by: Daniel Pacak <pacak.daniel@gmail.com>
  • Loading branch information
danielpacak committed May 14, 2021
1 parent cd1943a commit e908c2c
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 122 deletions.
16 changes: 14 additions & 2 deletions pkg/kube/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,22 @@ func MapContainerNamesToDockerAuths(images ContainerImages, secrets []corev1.Sec
func MapDockerRegistryServersToAuths(imagePullSecrets []corev1.Secret) (map[string]docker.Auth, error) {
auths := make(map[string]docker.Auth)
for _, secret := range imagePullSecrets {
// Skip a deprecated secret of type "kubernetes.io/dockercfg" which contains a dockercfg file
// that follows the same format rules as ~/.dockercfg
// See https://docs.docker.com/engine/deprecated/#support-for-legacy-dockercfg-configuration-files
if secret.Type != corev1.SecretTypeDockerConfigJson {
continue
}
data, hasRequiredData := secret.Data[corev1.DockerConfigJsonKey]
// Skip a secrets of type "kubernetes.io/dockerconfigjson" which does not contain
// the required ".dockerconfigjson" key.
if !hasRequiredData {
continue
}
dockerConfig := &docker.Config{}
err := dockerConfig.Read(secret.Data[corev1.DockerConfigJsonKey])
err := dockerConfig.Read(data)
if err != nil {
return nil, err
return nil, fmt.Errorf("reading content of %s: %w", corev1.DockerConfigJsonKey, err)
}
for authKey, auth := range dockerConfig.Auths {
server, err := docker.GetServerFromDockerAuthKey(authKey)
Expand Down
220 changes: 123 additions & 97 deletions pkg/kube/secrets_test.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,36 @@
package kube_test

import (
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"

"testing"

"github.com/aquasecurity/starboard/pkg/docker"
"github.com/aquasecurity/starboard/pkg/kube"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestSecrets(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Secrets")
}
func TestNewImagePullSecret(t *testing.T) {

t.Run("should construct a new image secret with encoded data", func(t *testing.T) {
g := NewGomegaWithT(t)

secret, err := kube.NewImagePullSecret(metav1.ObjectMeta{
Name: "my-secret",
Namespace: "my-namespace",
}, "http://index.docker.io/v1", "root", "s3cret")

var _ = Describe("Secrets", func() {

Context("NewImagePullSecret", func() {

It("should construct a new image secret with encoded data", func() {
secret, err := kube.NewImagePullSecret(metav1.ObjectMeta{
Name: "my-secret",
Namespace: "my-namespace",
}, "http://index.docker.io/v1", "root", "s3cret")

Expect(err).ToNot(HaveOccurred())
Expect(*secret).To(MatchFields(IgnoreExtras, Fields{
"ObjectMeta": MatchFields(IgnoreExtras, Fields{
"Name": Equal("my-secret"),
"Namespace": Equal("my-namespace"),
}),
"Type": Equal(corev1.SecretTypeDockerConfigJson),
"Data": MatchAllKeys(Keys{
".dockerconfigjson": MatchJSON(`{
g.Expect(err).ToNot(HaveOccurred())
g.Expect(*secret).To(MatchFields(IgnoreExtras, Fields{
"ObjectMeta": MatchFields(IgnoreExtras, Fields{
"Name": Equal("my-secret"),
"Namespace": Equal("my-namespace"),
}),
"Type": Equal(corev1.SecretTypeDockerConfigJson),
"Data": MatchAllKeys(Keys{
".dockerconfigjson": MatchJSON(`{
"auths": {
"http://index.docker.io/v1": {
"auth": "cm9vdDpzM2NyZXQ=",
Expand All @@ -44,111 +39,142 @@ var _ = Describe("Secrets", func() {
}
}
}`),
}),
}))
})

}),
}))
})
}

func TestMapDockerRegistryServersToAuths(t *testing.T) {

Context("MapDockerRegistryServersToAuths", func() {
t.Run("should map Docker registry servers to Docker authentication credentials", func(t *testing.T) {
g := NewGomegaWithT(t)

It("should map Docker registry servers to Docker authentication credentials", func() {
auths, err := kube.MapDockerRegistryServersToAuths([]corev1.Secret{
{
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
corev1.DockerConfigJsonKey: []byte(`{
auths, err := kube.MapDockerRegistryServersToAuths([]corev1.Secret{
{
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
corev1.DockerConfigJsonKey: []byte(`{
"auths": {
"http://index.docker.io/v1": {
"auth": "cm9vdDpzM2NyZXQ="
}
}
}`),
},
},
{
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
corev1.DockerConfigJsonKey: []byte(`{
},
{
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
corev1.DockerConfigJsonKey: []byte(`{
"auths": {
"quay.io": {
"auth": "dXNlcjpBZG1pbjEyMzQ1"
}
}
}`),
},
},
})
Expect(err).ToNot(HaveOccurred())
Expect(auths).To(MatchAllKeys(Keys{
"index.docker.io": Equal(docker.Auth{
Auth: "cm9vdDpzM2NyZXQ=",
Username: "root",
Password: "s3cret",
}),
"quay.io": Equal(docker.Auth{
Auth: "dXNlcjpBZG1pbjEyMzQ1",
Username: "user",
Password: "Admin12345",
}),
}))
},
})
g.Expect(err).ToNot(HaveOccurred())
g.Expect(auths).To(MatchAllKeys(Keys{
"index.docker.io": Equal(docker.Auth{
Auth: "cm9vdDpzM2NyZXQ=",
Username: "root",
Password: "s3cret",
}),
"quay.io": Equal(docker.Auth{
Auth: "dXNlcjpBZG1pbjEyMzQ1",
Username: "user",
Password: "Admin12345",
}),
}))
})

t.Run(`should skip secret of type "kubernetes.io/dockercfg"`, func(t *testing.T) {
g := NewGomegaWithT(t)

auths, err := kube.MapDockerRegistryServersToAuths([]corev1.Secret{
{
Type: corev1.SecretTypeDockercfg,
Data: map[string][]byte{},
},
{
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
corev1.DockerConfigJsonKey: []byte(`{
"auths": {
"http://index.docker.io/v1": {
"auth": "cm9vdDpzM2NyZXQ="
}
}
}`),
},
},
})

g.Expect(err).ToNot(HaveOccurred())
g.Expect(auths).To(MatchAllKeys(Keys{
"index.docker.io": Equal(docker.Auth{
Auth: "cm9vdDpzM2NyZXQ=",
Username: "root",
Password: "s3cret",
}),
}))
})
}

func TestMapContainerNamesToDockerAuths(t *testing.T) {

t.Run("should map container images to Docker authentication credentials", func(t *testing.T) {
g := NewGomegaWithT(t)

Context("MapContainerNamesToDockerAuths", func() {

It("should map container images to Docker authentication credentials", func() {
auths, err := kube.MapContainerNamesToDockerAuths(kube.ContainerImages{
"container-1": "docker.io/my-organization/my-app-backend:0.1.0",
"container-2": "my-organization/my-app-frontend:0.3.2",
"container-3": "quay.io/my-company/my-service:2.0",
}, []corev1.Secret{
{
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
corev1.DockerConfigJsonKey: []byte(`{
auths, err := kube.MapContainerNamesToDockerAuths(kube.ContainerImages{
"container-1": "docker.io/my-organization/my-app-backend:0.1.0",
"container-2": "my-organization/my-app-frontend:0.3.2",
"container-3": "quay.io/my-company/my-service:2.0",
}, []corev1.Secret{
{
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
corev1.DockerConfigJsonKey: []byte(`{
"auths": {
"http://index.docker.io/v1": {
"auth": "cm9vdDpzM2NyZXQ="
}
}
}`),
},
},
{
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
corev1.DockerConfigJsonKey: []byte(`{
},
{
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
corev1.DockerConfigJsonKey: []byte(`{
"auths": {
"quay.io": {
"auth": "dXNlcjpBZG1pbjEyMzQ1"
}
}
}`),
},
},
})
Expect(err).ToNot(HaveOccurred())
Expect(auths).To(MatchAllKeys(Keys{
"container-1": Equal(docker.Auth{
Auth: "cm9vdDpzM2NyZXQ=",
Username: "root",
Password: "s3cret",
}),
"container-2": Equal(docker.Auth{
Auth: "cm9vdDpzM2NyZXQ=",
Username: "root",
Password: "s3cret",
}),
"container-3": Equal(docker.Auth{
Auth: "dXNlcjpBZG1pbjEyMzQ1",
Username: "user",
Password: "Admin12345",
}),
}))
},
})

g.Expect(err).ToNot(HaveOccurred())
g.Expect(auths).To(MatchAllKeys(Keys{
"container-1": Equal(docker.Auth{
Auth: "cm9vdDpzM2NyZXQ=",
Username: "root",
Password: "s3cret",
}),
"container-2": Equal(docker.Auth{
Auth: "cm9vdDpzM2NyZXQ=",
Username: "root",
Password: "s3cret",
}),
"container-3": Equal(docker.Auth{
Auth: "dXNlcjpBZG1pbjEyMzQ1",
Username: "user",
Password: "Admin12345",
}),
}))
})

})
}
12 changes: 3 additions & 9 deletions pkg/plugin/trivy/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,15 +329,9 @@ func (s *scanner) getPodSpecForStandaloneMode(spec corev1.PodSpec, credentials m
},
},
},
InitContainers: []corev1.Container{initContainer},
Containers: containers,
SecurityContext: &corev1.PodSecurityContext{
RunAsUser: pointer.Int64Ptr(1000),
RunAsGroup: pointer.Int64Ptr(1000),
SeccompProfile: &corev1.SeccompProfile{
Type: corev1.SeccompProfileTypeRuntimeDefault,
},
},
InitContainers: []corev1.Container{initContainer},
Containers: containers,
SecurityContext: &corev1.PodSecurityContext{},
}, secrets, nil
}

Expand Down
16 changes: 2 additions & 14 deletions pkg/plugin/trivy/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,13 +227,7 @@ func TestScanner_GetScanJobSpec(t *testing.T) {
},
},
},
SecurityContext: &corev1.PodSecurityContext{
RunAsUser: pointer.Int64Ptr(1000),
RunAsGroup: pointer.Int64Ptr(1000),
SeccompProfile: &corev1.SeccompProfile{
Type: corev1.SeccompProfileTypeRuntimeDefault,
},
},
SecurityContext: &corev1.PodSecurityContext{},
},
},
{
Expand Down Expand Up @@ -443,13 +437,7 @@ func TestScanner_GetScanJobSpec(t *testing.T) {
},
},
},
SecurityContext: &corev1.PodSecurityContext{
RunAsUser: pointer.Int64Ptr(1000),
RunAsGroup: pointer.Int64Ptr(1000),
SeccompProfile: &corev1.SeccompProfile{
Type: corev1.SeccompProfileTypeRuntimeDefault,
},
},
SecurityContext: &corev1.PodSecurityContext{},
},
},
{
Expand Down

0 comments on commit e908c2c

Please sign in to comment.