diff --git a/cmd/skopeo/copy.go b/cmd/skopeo/copy.go index 953d2ecc..fbf18178 100644 --- a/cmd/skopeo/copy.go +++ b/cmd/skopeo/copy.go @@ -26,24 +26,24 @@ func copyHandler(context *cli.Context) error { } signBy := context.String("sign-by") - manifest, err := src.Manifest() + manifest, _, err := src.Manifest() if err != nil { return fmt.Errorf("Error reading manifest: %v", err) } - layers, err := src.LayerDigests() + blobDigests, err := src.BlobDigests() if err != nil { return fmt.Errorf("Error parsing manifest: %v", err) } - for _, layer := range layers { + for _, digest := range blobDigests { // TODO(mitr): do not ignore the size param returned here - stream, _, err := rawSource.GetBlob(layer) + stream, _, err := rawSource.GetBlob(digest) if err != nil { - return fmt.Errorf("Error reading layer %s: %v", layer, err) + return fmt.Errorf("Error reading blob %s: %v", digest, err) } defer stream.Close() - if err := dest.PutBlob(layer, stream); err != nil { - return fmt.Errorf("Error writing layer: %v", err) + if err := dest.PutBlob(digest, stream); err != nil { + return fmt.Errorf("Error writing blob: %v", err) } } diff --git a/cmd/skopeo/inspect.go b/cmd/skopeo/inspect.go index bee9500c..49b40807 100644 --- a/cmd/skopeo/inspect.go +++ b/cmd/skopeo/inspect.go @@ -38,7 +38,7 @@ var inspectCmd = cli.Command{ if err != nil { return err } - rawManifest, err := img.Manifest() + rawManifest, _, err := img.Manifest() if err != nil { return err } diff --git a/cmd/skopeo/layers.go b/cmd/skopeo/layers.go index 1427dd0e..579b33f8 100644 --- a/cmd/skopeo/layers.go +++ b/cmd/skopeo/layers.go @@ -19,35 +19,35 @@ var layersCmd = cli.Command{ return err } src := image.FromSource(rawSource) - layers := c.Args().Tail() - if len(layers) == 0 { - ls, err := src.LayerDigests() + blobDigests := c.Args().Tail() + if len(blobDigests) == 0 { + b, err := src.BlobDigests() if err != nil { return err } - layers = ls + blobDigests = b } tmpDir, err := ioutil.TempDir(".", "layers-") if err != nil { return err } dest := directory.NewDirImageDestination(tmpDir) - manifest, err := src.Manifest() + manifest, _, err := src.Manifest() if err != nil { return err } if err := dest.PutManifest(manifest); err != nil { return err } - for _, l := range layers { - if !strings.HasPrefix(l, "sha256:") { - l = "sha256:" + l + for _, digest := range blobDigests { + if !strings.HasPrefix(digest, "sha256:") { + digest = "sha256:" + digest } - r, _, err := rawSource.GetBlob(l) + r, _, err := rawSource.GetBlob(digest) if err != nil { return err } - if err := dest.PutBlob(l, r); err != nil { + if err := dest.PutBlob(digest, r); err != nil { r.Close() return err } diff --git a/vendor/github.com/containers/image/directory/directory.go b/vendor/github.com/containers/image/directory/directory.go index 6e3ca638..9d417191 100644 --- a/vendor/github.com/containers/image/directory/directory.go +++ b/vendor/github.com/containers/image/directory/directory.go @@ -2,13 +2,8 @@ package directory import ( "fmt" - "io" - "io/ioutil" - "os" "path/filepath" "strings" - - "github.com/containers/image/types" ) // manifestPath returns a path for the manifest within a directory using our conventions. @@ -16,110 +11,13 @@ func manifestPath(dir string) string { return filepath.Join(dir, "manifest.json") } -// manifestPath returns a path for a layer tarball within a directory using our conventions. +// layerPath returns a path for a layer tarball within a directory using our conventions. func layerPath(dir string, digest string) string { // FIXME: Should we keep the digest identification? return filepath.Join(dir, strings.TrimPrefix(digest, "sha256:")+".tar") } -// manifestPath returns a path for a signature within a directory using our conventions. +// signaturePath returns a path for a signature within a directory using our conventions. func signaturePath(dir string, index int) string { return filepath.Join(dir, fmt.Sprintf("signature-%d", index+1)) } - -type dirImageDestination struct { - dir string -} - -// NewDirImageDestination returns an ImageDestination for writing to an existing directory. -func NewDirImageDestination(dir string) types.ImageDestination { - return &dirImageDestination{dir} -} - -func (d *dirImageDestination) CanonicalDockerReference() (string, error) { - return "", fmt.Errorf("Can not determine canonical Docker reference for a local directory") -} - -func (d *dirImageDestination) PutManifest(manifest []byte) error { - return ioutil.WriteFile(manifestPath(d.dir), manifest, 0644) -} - -func (d *dirImageDestination) PutBlob(digest string, stream io.Reader) error { - layerFile, err := os.Create(layerPath(d.dir, digest)) - if err != nil { - return err - } - defer layerFile.Close() - if _, err := io.Copy(layerFile, stream); err != nil { - return err - } - if err := layerFile.Sync(); err != nil { - return err - } - return nil -} - -func (d *dirImageDestination) PutSignatures(signatures [][]byte) error { - for i, sig := range signatures { - if err := ioutil.WriteFile(signaturePath(d.dir, i), sig, 0644); err != nil { - return err - } - } - return nil -} - -type dirImageSource struct { - dir string -} - -// NewDirImageSource returns an ImageSource reading from an existing directory. -func NewDirImageSource(dir string) types.ImageSource { - return &dirImageSource{dir} -} - -// IntendedDockerReference returns the full, unambiguous, Docker reference for this image, _as specified by the user_ -// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image. -// May be "" if unknown. -func (s *dirImageSource) IntendedDockerReference() string { - return "" -} - -// it's up to the caller to determine the MIME type of the returned manifest's bytes -func (s *dirImageSource) GetManifest(_ []string) ([]byte, string, error) { - m, err := ioutil.ReadFile(manifestPath(s.dir)) - if err != nil { - return nil, "", err - } - return m, "", err -} - -func (s *dirImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) { - r, err := os.Open(layerPath(s.dir, digest)) - if err != nil { - return nil, 0, nil - } - fi, err := os.Stat(layerPath(s.dir, digest)) - if err != nil { - return nil, 0, nil - } - return r, fi.Size(), nil -} - -func (s *dirImageSource) GetSignatures() ([][]byte, error) { - signatures := [][]byte{} - for i := 0; ; i++ { - signature, err := ioutil.ReadFile(signaturePath(s.dir, i)) - if err != nil { - if os.IsNotExist(err) { - break - } - return nil, err - } - signatures = append(signatures, signature) - } - return signatures, nil -} - -func (s *dirImageSource) Delete() error { - return fmt.Errorf("directory#dirImageSource.Delete() not implmented") -} diff --git a/vendor/github.com/containers/image/directory/directory_dest.go b/vendor/github.com/containers/image/directory/directory_dest.go new file mode 100644 index 00000000..aba18c60 --- /dev/null +++ b/vendor/github.com/containers/image/directory/directory_dest.go @@ -0,0 +1,51 @@ +package directory + +import ( + "fmt" + "io" + "io/ioutil" + "os" + + "github.com/containers/image/types" +) + +type dirImageDestination struct { + dir string +} + +// NewDirImageDestination returns an ImageDestination for writing to an existing directory. +func NewDirImageDestination(dir string) types.ImageDestination { + return &dirImageDestination{dir} +} + +func (d *dirImageDestination) CanonicalDockerReference() (string, error) { + return "", fmt.Errorf("Can not determine canonical Docker reference for a local directory") +} + +func (d *dirImageDestination) PutManifest(manifest []byte) error { + return ioutil.WriteFile(manifestPath(d.dir), manifest, 0644) +} + +func (d *dirImageDestination) PutBlob(digest string, stream io.Reader) error { + layerFile, err := os.Create(layerPath(d.dir, digest)) + if err != nil { + return err + } + defer layerFile.Close() + if _, err := io.Copy(layerFile, stream); err != nil { + return err + } + if err := layerFile.Sync(); err != nil { + return err + } + return nil +} + +func (d *dirImageDestination) PutSignatures(signatures [][]byte) error { + for i, sig := range signatures { + if err := ioutil.WriteFile(signaturePath(d.dir, i), sig, 0644); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/containers/image/directory/directory_src.go b/vendor/github.com/containers/image/directory/directory_src.go new file mode 100644 index 00000000..dd22dafb --- /dev/null +++ b/vendor/github.com/containers/image/directory/directory_src.go @@ -0,0 +1,66 @@ +package directory + +import ( + "fmt" + "io" + "io/ioutil" + "os" + + "github.com/containers/image/types" +) + +type dirImageSource struct { + dir string +} + +// NewDirImageSource returns an ImageSource reading from an existing directory. +func NewDirImageSource(dir string) types.ImageSource { + return &dirImageSource{dir} +} + +// IntendedDockerReference returns the full, unambiguous, Docker reference for this image, _as specified by the user_ +// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image. +// May be "" if unknown. +func (s *dirImageSource) IntendedDockerReference() string { + return "" +} + +// it's up to the caller to determine the MIME type of the returned manifest's bytes +func (s *dirImageSource) GetManifest(_ []string) ([]byte, string, error) { + m, err := ioutil.ReadFile(manifestPath(s.dir)) + if err != nil { + return nil, "", err + } + return m, "", err +} + +func (s *dirImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) { + r, err := os.Open(layerPath(s.dir, digest)) + if err != nil { + return nil, 0, nil + } + fi, err := os.Stat(layerPath(s.dir, digest)) + if err != nil { + return nil, 0, nil + } + return r, fi.Size(), nil +} + +func (s *dirImageSource) GetSignatures() ([][]byte, error) { + signatures := [][]byte{} + for i := 0; ; i++ { + signature, err := ioutil.ReadFile(signaturePath(s.dir, i)) + if err != nil { + if os.IsNotExist(err) { + break + } + return nil, err + } + signatures = append(signatures, signature) + } + return signatures, nil +} + +func (s *dirImageSource) Delete() error { + return fmt.Errorf("directory#dirImageSource.Delete() not implmented") +} diff --git a/vendor/github.com/containers/image/docker/docker_image_src.go b/vendor/github.com/containers/image/docker/docker_image_src.go index 56cb0c13..8371f418 100644 --- a/vendor/github.com/containers/image/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/docker/docker_image_src.go @@ -4,12 +4,13 @@ import ( "fmt" "io" "io/ioutil" + "mime" "net/http" "strconv" "github.com/Sirupsen/logrus" - "github.com/containers/image/manifest" "github.com/containers/image/docker/reference" + "github.com/containers/image/manifest" "github.com/containers/image/types" ) @@ -57,6 +58,19 @@ func (s *dockerImageSource) IntendedDockerReference() string { return fmt.Sprintf("%s:%s", s.ref.Name(), s.tag) } +// simplifyContentType drops parameters from a HTTP media type (see https://tools.ietf.org/html/rfc7231#section-3.1.1.1) +// Alternatively, an empty string is returned unchanged, and invalid values are "simplified" to an empty string. +func simplifyContentType(contentType string) string { + if contentType == "" { + return contentType + } + mimeType, _, err := mime.ParseMediaType(contentType) + if err != nil { + return "" + } + return mimeType +} + func (s *dockerImageSource) GetManifest(mimetypes []string) ([]byte, string, error) { url := fmt.Sprintf(manifestURL, s.ref.RemoteName(), s.tag) // TODO(runcom) set manifest version header! schema1 for now - then schema2 etc etc and v1 @@ -76,7 +90,7 @@ func (s *dockerImageSource) GetManifest(mimetypes []string) ([]byte, string, err return nil, "", errFetchManifest{res.StatusCode, manblob} } // We might validate manblob against the Docker-Content-Digest header here to protect against transport errors. - return manblob, res.Header.Get("Content-Type"), nil + return manblob, simplifyContentType(res.Header.Get("Content-Type")), nil } func (s *dockerImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) { diff --git a/vendor/github.com/containers/image/image/image.go b/vendor/github.com/containers/image/image/image.go index 1179e507..5850c40b 100644 --- a/vendor/github.com/containers/image/image/image.go +++ b/vendor/github.com/containers/image/image/image.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "fmt" + "io/ioutil" "regexp" "time" @@ -24,9 +25,15 @@ var ( // may not be a `genericImage` directly. However, most users of `types.Image` // do not care, and those who care about `skopeo/docker.Image` know they do. type genericImage struct { - src types.ImageSource - cachedManifest []byte // Private cache for Manifest(); nil if not yet known. - cachedSignatures [][]byte // Private cache for Signatures(); nil if not yet known. + src types.ImageSource + // private cache for Manifest(); nil if not yet known. + cachedManifest []byte + // private cache for the manifest media type w/o having to guess it + // this may be the empty string in case the MIME Type wasn't guessed correctly + // this field is valid only if cachedManifest is not nil + cachedManifestMIMEType string + // private cache for Signatures(); nil if not yet known. + cachedSignatures [][]byte } // FromSource returns a types.Image implementation for source. @@ -42,16 +49,20 @@ func (i *genericImage) IntendedDockerReference() string { } // Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need. -// NOTE: It is essential for signature verification that Manifest returns the manifest from which LayerDigests is computed. -func (i *genericImage) Manifest() ([]byte, error) { +// NOTE: It is essential for signature verification that Manifest returns the manifest from which BlobDigests is computed. +func (i *genericImage) Manifest() ([]byte, string, error) { if i.cachedManifest == nil { - m, _, err := i.src.GetManifest([]string{manifest.DockerV2Schema1MIMEType}) + m, mt, err := i.src.GetManifest([]string{manifest.DockerV2Schema1SignedMIMEType, manifest.DockerV2Schema1MIMEType}) if err != nil { - return nil, err + return nil, "", err } i.cachedManifest = m + if mt == "" { + mt = manifest.GuessMIMEType(i.cachedManifest) + } + i.cachedManifestMIMEType = mt } - return i.cachedManifest, nil + return i.cachedManifest, i.cachedManifestMIMEType, nil } // Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need. @@ -68,27 +79,11 @@ func (i *genericImage) Signatures() ([][]byte, error) { func (i *genericImage) Inspect() (*types.ImageInspectInfo, error) { // TODO(runcom): unused version param for now, default to docker v2-1 - m, err := i.getSchema1Manifest() + m, err := i.getParsedManifest() if err != nil { return nil, err } - ms1, ok := m.(*manifestSchema1) - if !ok { - return nil, fmt.Errorf("error retrivieng manifest schema1") - } - v1 := &v1Image{} - if err := json.Unmarshal([]byte(ms1.History[0].V1Compatibility), v1); err != nil { - return nil, err - } - return &types.ImageInspectInfo{ - Tag: ms1.Tag, - DockerVersion: v1.DockerVersion, - Created: v1.Created, - Labels: v1.Config.Labels, - Architecture: v1.Architecture, - Os: v1.OS, - Layers: ms1.GetLayers(), - }, nil + return m.ImageInspectInfo() } type config struct { @@ -110,13 +105,19 @@ type v1Image struct { // will support v1 one day... type genericManifest interface { - GetLayers() []string + Config() ([]byte, error) + LayerDigests() []string + BlobDigests() []string + ImageInspectInfo() (*types.ImageInspectInfo, error) } type fsLayersSchema1 struct { BlobSum string `json:"blobSum"` } +// compile-time check that manifestSchema1 implements genericManifest +var _ genericManifest = (*manifestSchema1)(nil) + type manifestSchema1 struct { Name string Tag string @@ -128,7 +129,7 @@ type manifestSchema1 struct { //Signature []byte `json:"signature"` } -func (m *manifestSchema1) GetLayers() []string { +func (m *manifestSchema1) LayerDigests() []string { layers := make([]string, len(m.FSLayers)) for i, layer := range m.FSLayers { layers[i] = layer.BlobSum @@ -136,38 +137,140 @@ func (m *manifestSchema1) GetLayers() []string { return layers } -// getSchema1Manifest parses the manifest into a data structure, cleans it up, and returns it. -// NOTE: The manifest may have been modified in the process; DO NOT reserialize and store the return value -// if you want to preserve the original manifest; use the blob returned by Manifest() directly. -// NOTE: It is essential for signature verification that the object is computed from the same manifest which is returned by Manifest(). -func (i *genericImage) getSchema1Manifest() (genericManifest, error) { - manblob, err := i.Manifest() +func (m *manifestSchema1) BlobDigests() []string { + return m.LayerDigests() +} + +func (m *manifestSchema1) Config() ([]byte, error) { + return []byte(m.History[0].V1Compatibility), nil +} + +func (m *manifestSchema1) ImageInspectInfo() (*types.ImageInspectInfo, error) { + v1 := &v1Image{} + config, err := m.Config() if err != nil { return nil, err } - mschema1 := &manifestSchema1{} - if err := json.Unmarshal(manblob, mschema1); err != nil { + if err := json.Unmarshal(config, v1); err != nil { return nil, err } - if err := fixManifestLayers(mschema1); err != nil { - return nil, err - } - // TODO(runcom): verify manifest schema 1, 2 etc - //if len(m.FSLayers) != len(m.History) { - //return nil, fmt.Errorf("length of history not equal to number of layers for %q", ref.String()) - //} - //if len(m.FSLayers) == 0 { - //return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String()) - //} - return mschema1, nil + return &types.ImageInspectInfo{ + Tag: m.Tag, + DockerVersion: v1.DockerVersion, + Created: v1.Created, + Labels: v1.Config.Labels, + Architecture: v1.Architecture, + Os: v1.OS, + Layers: m.LayerDigests(), + }, nil } -// uniqueLayerDigests returns a list of layer digests referenced from a manifest. +// compile-time check that manifestSchema2 implements genericManifest +var _ genericManifest = (*manifestSchema2)(nil) + +type manifestSchema2 struct { + src types.ImageSource + ConfigDescriptor descriptor `json:"config"` + LayersDescriptors []descriptor `json:"layers"` +} + +type descriptor struct { + MediaType string `json:"mediaType"` + Size int64 `json:"size"` + Digest string `json:"digest"` +} + +func (m *manifestSchema2) LayerDigests() []string { + blobs := []string{} + for _, layer := range m.LayersDescriptors { + blobs = append(blobs, layer.Digest) + } + return blobs +} + +func (m *manifestSchema2) BlobDigests() []string { + blobs := m.LayerDigests() + blobs = append(blobs, m.ConfigDescriptor.Digest) + return blobs +} + +func (m *manifestSchema2) Config() ([]byte, error) { + rawConfig, _, err := m.src.GetBlob(m.ConfigDescriptor.Digest) + if err != nil { + return nil, err + } + config, err := ioutil.ReadAll(rawConfig) + rawConfig.Close() + return config, err +} + +func (m *manifestSchema2) ImageInspectInfo() (*types.ImageInspectInfo, error) { + config, err := m.Config() + if err != nil { + return nil, err + } + v1 := &v1Image{} + if err := json.Unmarshal(config, v1); err != nil { + return nil, err + } + return &types.ImageInspectInfo{ + DockerVersion: v1.DockerVersion, + Created: v1.Created, + Labels: v1.Config.Labels, + Architecture: v1.Architecture, + Os: v1.OS, + Layers: m.LayerDigests(), + }, nil +} + +// getParsedManifest parses the manifest into a data structure, cleans it up, and returns it. +// NOTE: The manifest may have been modified in the process; DO NOT reserialize and store the return value +// if you want to preserve the original manifest; use the blob returned by Manifest() directly. +// NOTE: It is essential for signature verification that the object is computed from the same manifest which is returned by Manifest(). +func (i *genericImage) getParsedManifest() (genericManifest, error) { + manblob, mt, err := i.Manifest() + if err != nil { + return nil, err + } + switch mt { + // "application/json" is a valid v2s1 value per https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-1.md . + // This works for now, when nothing else seems to return "application/json"; if that were not true, the mapping/detection might + // need to happen within the ImageSource. + case manifest.DockerV2Schema1MIMEType, manifest.DockerV2Schema1SignedMIMEType, "application/json": + mschema1 := &manifestSchema1{} + if err := json.Unmarshal(manblob, mschema1); err != nil { + return nil, err + } + if err := fixManifestLayers(mschema1); err != nil { + return nil, err + } + // TODO(runcom): verify manifest schema 1, 2 etc + //if len(m.FSLayers) != len(m.History) { + //return nil, fmt.Errorf("length of history not equal to number of layers for %q", ref.String()) + //} + //if len(m.FSLayers) == 0 { + //return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String()) + //} + return mschema1, nil + case manifest.DockerV2Schema2MIMEType: + v2s2 := manifestSchema2{src: i.src} + if err := json.Unmarshal(manblob, &v2s2); err != nil { + return nil, err + } + return &v2s2, nil + case "": + return nil, errors.New("could not guess manifest media type") + default: + return nil, fmt.Errorf("unsupported manifest media type %s", mt) + } +} + +// uniqueBlobDigests returns a list of blob digests referenced from a manifest. // The list will not contain duplicates; it is not intended to correspond to the "history" or "parent chain" of a Docker image. -func uniqueLayerDigests(m genericManifest) []string { +func uniqueBlobDigests(m genericManifest) []string { var res []string seen := make(map[string]struct{}) - for _, digest := range m.GetLayers() { + for _, digest := range m.BlobDigests() { if _, ok := seen[digest]; ok { continue } @@ -177,15 +280,15 @@ func uniqueLayerDigests(m genericManifest) []string { return res } -// LayerDigests returns a list of layer digests referenced by this image. +// BlobDigests returns a list of blob digests referenced by this image. // The list will not contain duplicates; it is not intended to correspond to the "history" or "parent chain" of a Docker image. -// NOTE: It is essential for signature verification that LayerDigests is computed from the same manifest which is returned by Manifest(). -func (i *genericImage) LayerDigests() ([]string, error) { - m, err := i.getSchema1Manifest() +// NOTE: It is essential for signature verification that BlobDigests is computed from the same manifest which is returned by Manifest(). +func (i *genericImage) BlobDigests() ([]string, error) { + m, err := i.getParsedManifest() if err != nil { return nil, err } - return uniqueLayerDigests(m), nil + return uniqueBlobDigests(m), nil } func (i *genericImage) getLayer(dest types.ImageDestination, digest string) error { diff --git a/vendor/github.com/containers/image/manifest/manifest.go b/vendor/github.com/containers/image/manifest/manifest.go index cd73ce06..1b3a76ca 100644 --- a/vendor/github.com/containers/image/manifest/manifest.go +++ b/vendor/github.com/containers/image/manifest/manifest.go @@ -14,11 +14,12 @@ import ( const ( // DockerV2Schema1MIMEType MIME type represents Docker manifest schema 1 DockerV2Schema1MIMEType = "application/vnd.docker.distribution.manifest.v1+json" + // DockerV2Schema1MIMEType MIME type represents Docker manifest schema 1 with a JWS signature + DockerV2Schema1SignedMIMEType = "application/vnd.docker.distribution.manifest.v1+prettyjws" // DockerV2Schema2MIMEType MIME type represents Docker manifest schema 2 DockerV2Schema2MIMEType = "application/vnd.docker.distribution.manifest.v2+json" // DockerV2ListMIMEType MIME type represents Docker manifest schema 2 list DockerV2ListMIMEType = "application/vnd.docker.distribution.manifest.list.v2+json" - // OCIV1DescriptorMIMEType TODO OCIV1DescriptorMIMEType = "application/vnd.oci.descriptor.v1+json" // OCIV1ImageManifestMIMEType TODO @@ -40,8 +41,9 @@ func GuessMIMEType(manifest []byte) string { // A subset of manifest fields; the rest is silently ignored by json.Unmarshal. // Also docker/distribution/manifest.Versioned. meta := struct { - MediaType string `json:"mediaType"` - SchemaVersion int `json:"schemaVersion"` + MediaType string `json:"mediaType"` + SchemaVersion int `json:"schemaVersion"` + Signatures interface{} `json:"signatures"` }{} if err := json.Unmarshal(manifest, &meta); err != nil { return "" @@ -54,6 +56,9 @@ func GuessMIMEType(manifest []byte) string { // this is the only way the function can return DockerV2Schema1MIMEType, and recognizing that is essential for stripping the JWS signatures = computing the correct manifest digest. switch meta.SchemaVersion { case 1: + if meta.Signatures != nil { + return DockerV2Schema1SignedMIMEType + } return DockerV2Schema1MIMEType case 2: // Really should not happen, meta.MediaType should have been set. But given the data, this is our best guess. return DockerV2Schema2MIMEType @@ -63,7 +68,7 @@ func GuessMIMEType(manifest []byte) string { // Digest returns the a digest of a docker manifest, with any necessary implied transformations like stripping v1s1 signatures. func Digest(manifest []byte) (string, error) { - if GuessMIMEType(manifest) == DockerV2Schema1MIMEType { + if GuessMIMEType(manifest) == DockerV2Schema1SignedMIMEType { sig, err := libtrust.ParsePrettySignature(manifest, "signatures") if err != nil { return "", err diff --git a/vendor/github.com/containers/image/signature/policy_eval_signedby.go b/vendor/github.com/containers/image/signature/policy_eval_signedby.go index 9f9bef82..e6889c89 100644 --- a/vendor/github.com/containers/image/signature/policy_eval_signedby.go +++ b/vendor/github.com/containers/image/signature/policy_eval_signedby.go @@ -76,7 +76,7 @@ func (pr *prSignedBy) isSignatureAuthorAccepted(image types.Image, sig []byte) ( return nil }, validateSignedDockerManifestDigest: func(digest string) error { - m, err := image.Manifest() + m, _, err := image.Manifest() if err != nil { return err } diff --git a/vendor/github.com/containers/image/types/types.go b/vendor/github.com/containers/image/types/types.go index 1c4609be..212748bc 100644 --- a/vendor/github.com/containers/image/types/types.go +++ b/vendor/github.com/containers/image/types/types.go @@ -44,19 +44,21 @@ type Image interface { // May be "" if unknown. IntendedDockerReference() string // Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need. - // NOTE: It is essential for signature verification that Manifest returns the manifest from which LayerDigests is computed. - Manifest() ([]byte, error) + // NOTE: It is essential for signature verification that Manifest returns the manifest from which BlobDigests is computed. + Manifest() ([]byte, string, error) // Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need. Signatures() ([][]byte, error) - // LayerDigests returns a list of layer digests referenced by this image. + // BlobDigests returns a list of blob digests referenced by this image. // The list will not contain duplicates; it is not intended to correspond to the "history" or "parent chain" of a Docker image. - // NOTE: It is essential for signature verification that LayerDigests is computed from the same manifest which is returned by Manifest(). - LayerDigests() ([]string, error) + // NOTE: It is essential for signature verification that BlobDigests is computed from the same manifest which is returned by Manifest(). + BlobDigests() ([]string, error) // Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration. Inspect() (*ImageInspectInfo, error) } // ImageInspectInfo is a set of metadata describing Docker images, primarily their manifest and configuration. +// The Tag field is a legacy field which is here just for the Docker v2s1 manifest. It won't be supported +// for other manifest types. type ImageInspectInfo struct { Tag string Created time.Time