diff --git a/.github/workflows/test-registries.yml b/.github/workflows/test-registries.yml index 1c24bcff..f8c541ba 100644 --- a/.github/workflows/test-registries.yml +++ b/.github/workflows/test-registries.yml @@ -94,5 +94,19 @@ jobs: --docker-email="${{ secrets.TEST_EMAIL }}" \ -o json --dry-run=client | jq -r '.data.".dockerconfigjson"' > auth/hub.yaml + - name: Prepare legacy Hub secrets + shell: bash + run: | + cat << EOF > .dockercfg + { + "https://index.docker.io/v1/": { "username": "${{ secrets.TEST_HUB_USERNAME }}", "password": "${{ secrets.TEST_HUB_PASSWORD }}" } + } + EOF + + kubectl create secret generic hub-secret \ + --from-file=.dockercfg \ + --type=kubernetes.io/dockercfg \ + -o json --dry-run=client | jq -r '.data.".dockercfg"' > auth/legacy-hub.yaml + - name: Execute Registry-Tests run: make test-registries diff --git a/internal/kubernetes/kubernetes.go b/internal/kubernetes/kubernetes.go index 4e84babf..f92345d3 100644 --- a/internal/kubernetes/kubernetes.go +++ b/internal/kubernetes/kubernetes.go @@ -16,10 +16,11 @@ import ( ) type ContainerImage struct { - Image string - ImageID string - Auth []byte - Pods []corev1.Pod + Image string + ImageID string + Auth []byte + LegacyAuth bool + Pods []corev1.Pod } type KubeClient struct { @@ -95,7 +96,7 @@ func (client *KubeClient) LoadImageInfos(namespaces []corev1.Namespace, podLabel statuses = append(statuses, pod.Status.InitContainerStatuses...) statuses = append(statuses, pod.Status.EphemeralContainerStatuses...) - pullSecrets, err := client.loadSecrets(pod.Namespace, pod.Spec.ImagePullSecrets) + pullSecrets, legacy, err := client.loadSecrets(pod.Namespace, pod.Spec.ImagePullSecrets) if err != nil { logrus.WithError(err).Errorf("PullSecrets could not be retrieved for pod %s/%s", ns.Name, pod.Name) @@ -107,7 +108,13 @@ func (client *KubeClient) LoadImageInfos(namespaces []corev1.Namespace, podLabel if !client.hasAnnotation(annotations, c) { img, ok := images[c.ImageID] if !ok { - img = ContainerImage{Image: c.Image, ImageID: c.ImageID, Auth: pullSecrets, Pods: []corev1.Pod{}} + img = ContainerImage{ + Image: c.Image, + ImageID: c.ImageID, + Auth: pullSecrets, + LegacyAuth: legacy, + Pods: []corev1.Pod{}, + } } img.Pods = append(img.Pods, pod) @@ -173,28 +180,30 @@ func (client *KubeClient) hasAnnotation(annotations map[string]string, status co return false } -func (client *KubeClient) loadSecrets(namespace string, secrets []corev1.LocalObjectReference) ([]byte, error) { +func (client *KubeClient) loadSecrets(namespace string, secrets []corev1.LocalObjectReference) ([]byte, bool, error) { // TODO: Support all secrets which are referenced as imagePullSecrets instead of only the first one. for _, s := range secrets { secret, err := client.Client.CoreV1().Secrets(namespace).Get(context.Background(), s.Name, meta.GetOptions{}) if err != nil { - return nil, err + return nil, false, err } var creds []byte + legacy := false if secret.Type == corev1.SecretTypeDockerConfigJson { creds = secret.Data[corev1.DockerConfigJsonKey] } else if secret.Type == corev1.SecretTypeDockercfg { creds = secret.Data[corev1.DockerConfigKey] + legacy = true } else { - return nil, fmt.Errorf("invalid secret-type %s for pullSecret %s/%s", secret.Type, secret.Namespace, secret.Name) + return nil, false, fmt.Errorf("invalid secret-type %s for pullSecret %s/%s", secret.Type, secret.Namespace, secret.Name) } if len(creds) > 0 { - return creds, nil + return creds, legacy, nil } } - return nil, nil + return nil, false, nil } diff --git a/internal/registry/registry.go b/internal/registry/registry.go index 094c3f8d..1941093c 100644 --- a/internal/registry/registry.go +++ b/internal/registry/registry.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/docker/cli/cli/config" + "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/config/types" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/crane" @@ -23,7 +24,16 @@ func SaveImage(imagePath string, image kubernetes.ContainerImage) error { o := crane.GetOptions() if len(image.Auth) > 0 { - cf, err := config.LoadFromReader(bytes.NewReader(image.Auth)) + var cf *configfile.ConfigFile + var err error + + if image.LegacyAuth { + cf = configfile.New("") + err = cf.LegacyLoadFromReader(bytes.NewReader(image.Auth)) + } else { + cf, err = config.LoadFromReader(bytes.NewReader(image.Auth)) + } + if err != nil { return err } diff --git a/internal/registry/registry_suite_test.go b/internal/registry/registry_suite_test.go index e47b45de..cd32fe75 100644 --- a/internal/registry/registry_suite_test.go +++ b/internal/registry/registry_suite_test.go @@ -16,7 +16,7 @@ func TestRegistry(t *testing.T) { RunSpecs(t, "Registry Suite") } -func testRegistry(name, image string) { +func testRegistry(name, image string, legacy bool) { b, err := os.ReadFile("../../auth/" + name + ".yaml") Expect(err).To(BeNil()) @@ -24,7 +24,7 @@ func testRegistry(name, image string) { Expect(err).To(BeNil()) file := "/tmp/1.0.0.tar.gz" - err = registry.SaveImage(file, kubernetes.ContainerImage{ImageID: image, Auth: []byte(decoded)}) + err = registry.SaveImage(file, kubernetes.ContainerImage{ImageID: image, Auth: []byte(decoded), LegacyAuth: legacy}) if err == nil { stat, _ := os.Stat(file) @@ -38,37 +38,43 @@ func testRegistry(name, image string) { var _ = Describe("Registry", func() { Describe("Storing image from GCR", func() { It("should work correctly", func() { - testRegistry("gcr", "gcr.io/sbom-git-operator/integration-test-image:1.0.0") + testRegistry("gcr", "gcr.io/sbom-git-operator/integration-test-image:1.0.0", false) }) }) Describe("Storing image from GAR", func() { It("should work correctly", func() { - testRegistry("gar", "europe-west3-docker.pkg.dev/sbom-git-operator/sbom-git-operator/integration-test-image:1.0.0") + testRegistry("gar", "europe-west3-docker.pkg.dev/sbom-git-operator/sbom-git-operator/integration-test-image:1.0.0", false) }) }) Describe("Storing image from ECR", func() { It("should work correctly", func() { - testRegistry("ecr", "055403865123.dkr.ecr.eu-central-1.amazonaws.com/sbom-git-operator/integration-test-image:1.0.0") + testRegistry("ecr", "055403865123.dkr.ecr.eu-central-1.amazonaws.com/sbom-git-operator/integration-test-image:1.0.0", false) }) }) Describe("Storing image from ACR", func() { It("should work correctly", func() { - testRegistry("acr", "sbomgitoperator.azurecr.io/integration-test-image:1.0.0") + testRegistry("acr", "sbomgitoperator.azurecr.io/integration-test-image:1.0.0", false) }) }) Describe("Storing image from DockerHub", func() { It("should work correctly", func() { - testRegistry("hub", "docker.io/ckotzbauer/integration-test-image:1.0.0") + testRegistry("hub", "docker.io/ckotzbauer/integration-test-image:1.0.0", false) }) }) Describe("Storing image from GHCR", func() { It("should work correctly", func() { - testRegistry("ghcr", "ghcr.io/ckotzbauer-kubernetes-bot/sbom-git-operator-integration-test:1.0.0") + testRegistry("ghcr", "ghcr.io/ckotzbauer-kubernetes-bot/sbom-git-operator-integration-test:1.0.0", false) + }) + }) + + Describe("Storing image from DockerHub - legacy .dockercfg", func() { + It("should work correctly", func() { + testRegistry("legacy-hub", "docker.io/ckotzbauer/integration-test-image:1.0.0", true) }) }) })