From 3c5c192bafe13589bc291fd188e275e2feea84b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Tue, 18 Jul 2023 14:21:00 +0200 Subject: [PATCH] c8d/resolveImage: Fix Digested and Named reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When resolving a reference that is both a Named and Digested, it could be resolved to an image that has the same digest, but completely different repository name. Signed-off-by: Paweł Gronowski (cherry picked from commit 48fc306764fc5c39d4284021520a0337ef7e0cb0) Signed-off-by: Paweł Gronowski --- daemon/containerd/image.go | 18 ++++++++++++++++ integration/image/remove_test.go | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/daemon/containerd/image.go b/daemon/containerd/image.go index 26dd20de29..589ca30619 100644 --- a/daemon/containerd/image.go +++ b/daemon/containerd/image.go @@ -274,6 +274,24 @@ func (i *ImageService) resolveImage(ctx context.Context, refOrID string) (contai return containerdimages.Image{}, images.ErrImageDoesNotExist{Ref: parsed} } + // If reference is both Named and Digested, make sure we don't match + // images with a different repository even if digest matches. + // For example, busybox@sha256:abcdef..., shouldn't match asdf@sha256:abcdef... + if parsedNamed, ok := parsed.(reference.Named); ok { + for _, img := range imgs { + imgNamed, err := reference.ParseNormalizedNamed(img.Name) + if err != nil { + logrus.WithError(err).WithField("image", img.Name).Warn("image with invalid name encountered") + continue + } + + if parsedNamed.Name() == imgNamed.Name() { + return img, nil + } + } + return containerdimages.Image{}, images.ErrImageDoesNotExist{Ref: parsed} + } + return imgs[0], nil } diff --git a/integration/image/remove_test.go b/integration/image/remove_test.go index 11d8141da3..6401313427 100644 --- a/integration/image/remove_test.go +++ b/integration/image/remove_test.go @@ -6,9 +6,11 @@ import ( "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" "github.com/docker/docker/integration/internal/container" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/skip" ) func TestRemoveImageOrphaning(t *testing.T) { @@ -57,3 +59,36 @@ func TestRemoveImageOrphaning(t *testing.T) { _, _, err = client.ImageInspectWithRaw(ctx, commitResp2.ID) assert.Check(t, is.ErrorContains(err, "No such image:")) } + +func TestRemoveByDigest(t *testing.T) { + skip.If(t, !testEnv.UsingSnapshotter(), "RepoDigests doesn't include tags when using graphdrivers") + + defer setupTest(t)() + ctx := context.Background() + client := testEnv.APIClient() + + err := client.ImageTag(ctx, "busybox", "test-remove-by-digest:latest") + assert.NilError(t, err) + + inspect, _, err := client.ImageInspectWithRaw(ctx, "test-remove-by-digest") + assert.NilError(t, err) + + id := "" + for _, ref := range inspect.RepoDigests { + if strings.Contains(ref, "test-remove-by-digest") { + id = ref + break + } + } + assert.Assert(t, id != "") + + t.Logf("removing %s", id) + _, err = client.ImageRemove(ctx, id, types.ImageRemoveOptions{}) + assert.NilError(t, err) + + inspect, _, err = client.ImageInspectWithRaw(ctx, "busybox") + assert.Check(t, err, "busybox image got deleted") + + inspect, _, err = client.ImageInspectWithRaw(ctx, "test-remove-by-digest") + assert.Check(t, is.ErrorType(err, errdefs.IsNotFound)) +}