Compute the digest in (skopeo inspect) instead of trusting the registry

Compute the digest ourselves, the registry is in general untrusted and
computing it ourserlves is easy enough.

The stop passing the unverifiedCanonicalDigest value around, simplifying
ImageSource.GetManifest and related code.  In particular, remove
retrieveRawManifest and have internal users just call Manifest() now that
we don't need the digest.
This commit is contained in:
Miloslav Trmač 2016-05-16 17:40:16 +02:00
parent e3d257e7b5
commit 60e8d63712
7 changed files with 36 additions and 46 deletions

View File

@ -54,7 +54,7 @@ func copyHandler(context *cli.Context) {
}
signBy := context.String("sign-by")
manifest, _, err := src.GetManifest()
manifest, err := src.GetManifest()
if err != nil {
logrus.Fatalf("Error reading manifest: %s", err.Error())
}

View File

@ -7,6 +7,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/codegangsta/cli"
"github.com/projectatomic/skopeo/dockerutils"
)
// inspectOutput is the output format of (skopeo inspect), primarily so that we can format it with a simple json.MarshalIndent.
@ -37,22 +38,26 @@ var inspectCmd = cli.Command{
if err != nil {
logrus.Fatal(err)
}
rawManifest, err := img.Manifest()
if err != nil {
logrus.Fatal(err)
}
if c.Bool("raw") {
b, err := img.Manifest()
if err != nil {
logrus.Fatal(err)
}
fmt.Println(string(b))
fmt.Println(string(rawManifest))
return
}
imgInspect, err := img.Inspect()
if err != nil {
logrus.Fatal(err)
}
manifestDigest, err := dockerutils.ManifestDigest(rawManifest)
if err != nil {
logrus.Fatalf("Error computing manifest digest: %s", err.Error())
}
outputData := inspectOutput{
Name: imgInspect.Name,
Tag: imgInspect.Tag,
Digest: imgInspect.Digest,
Digest: manifestDigest,
RepoTags: imgInspect.RepoTags,
Created: imgInspect.Created,
DockerVersion: imgInspect.DockerVersion,

View File

@ -84,13 +84,8 @@ func (s *dirImageSource) IntendedDockerReference() string {
return ""
}
func (s *dirImageSource) GetManifest() ([]byte, string, error) {
manifest, err := ioutil.ReadFile(manifestPath(s.dir))
if err != nil {
return nil, "", err
}
return manifest, "", nil // FIXME? unverifiedCanonicalDigest value - really primarily used by dockerImage
func (s *dirImageSource) GetManifest() ([]byte, error) {
return ioutil.ReadFile(manifestPath(s.dir))
}
func (s *dirImageSource) GetLayer(digest string) (io.ReadCloser, error) {

View File

@ -20,8 +20,7 @@ var (
type dockerImage struct {
src *dockerImageSource
digest string
rawManifest []byte
cachedManifest []byte // Private cache for Manifest(); nil if not yet known.
cachedSignatures [][]byte // Private cache for Signatures(); nil if not yet known.
}
@ -44,10 +43,14 @@ func (i *dockerImage) IntendedDockerReference() string {
// Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need.
func (i *dockerImage) Manifest() ([]byte, error) {
if err := i.retrieveRawManifest(); err != nil {
return nil, err
if i.cachedManifest == nil {
m, err := i.src.GetManifest()
if err != nil {
return nil, err
}
i.cachedManifest = m
}
return i.rawManifest, nil
return i.cachedManifest, nil
}
// Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
@ -76,7 +79,7 @@ func (i *dockerImage) Inspect() (*types.DockerImageManifest, error) {
if err != nil {
return nil, err
}
imgManifest, err := makeImageManifest(i.src.ref.FullName(), ms1, i.digest, tags)
imgManifest, err := makeImageManifest(i.src.ref.FullName(), ms1, tags)
if err != nil {
return nil, err
}
@ -122,7 +125,7 @@ type v1Image struct {
OS string `json:"os,omitempty"`
}
func makeImageManifest(name string, m *manifestSchema1, dgst string, tagList []string) (*types.DockerImageManifest, error) {
func makeImageManifest(name string, m *manifestSchema1, tagList []string) (*types.DockerImageManifest, error) {
v1 := &v1Image{}
if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), v1); err != nil {
return nil, err
@ -130,7 +133,6 @@ func makeImageManifest(name string, m *manifestSchema1, dgst string, tagList []s
return &types.DockerImageManifest{
Name: name,
Tag: m.Tag,
Digest: dgst,
RepoTags: tagList,
DockerVersion: v1.DockerVersion,
Created: v1.Created,
@ -181,25 +183,13 @@ func sanitize(s string) string {
return strings.Replace(s, "/", "-", -1)
}
func (i *dockerImage) retrieveRawManifest() error {
if i.rawManifest != nil {
return nil
}
manblob, unverifiedCanonicalDigest, err := i.src.GetManifest()
if err != nil {
return err
}
i.rawManifest = manblob
i.digest = unverifiedCanonicalDigest
return nil
}
func (i *dockerImage) getSchema1Manifest() (manifest, error) {
if err := i.retrieveRawManifest(); err != nil {
manblob, err := i.Manifest()
if err != nil {
return nil, err
}
mschema1 := &manifestSchema1{}
if err := json.Unmarshal(i.rawManifest, mschema1); err != nil {
if err := json.Unmarshal(manblob, mschema1); err != nil {
return nil, err
}
if err := fixManifestLayers(mschema1); err != nil {

View File

@ -55,23 +55,24 @@ func (s *dockerImageSource) IntendedDockerReference() string {
return fmt.Sprintf("%s:%s", s.ref.Name(), s.tag)
}
func (s *dockerImageSource) GetManifest() (manifest []byte, unverifiedCanonicalDigest string, err error) {
func (s *dockerImageSource) GetManifest() ([]byte, 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
// TODO(runcom) NO, switch on the resulter manifest like Docker is doing
res, err := s.c.makeRequest("GET", url, nil, nil)
if err != nil {
return nil, "", err
return nil, err
}
defer res.Body.Close()
manblob, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, "", err
return nil, err
}
if res.StatusCode != http.StatusOK {
return nil, "", errFetchManifest{res.StatusCode, manblob}
return nil, errFetchManifest{res.StatusCode, manblob}
}
return manblob, res.Header.Get("Docker-Content-Digest"), nil
// We might validate manblob against the Docker-Content-Digest header here to protect against transport errors.
return manblob, nil
}
func (s *dockerImageSource) GetLayer(digest string) (io.ReadCloser, error) {

View File

@ -193,9 +193,9 @@ func (s *openshiftImageSource) IntendedDockerReference() string {
return s.client.canonicalDockerReference()
}
func (s *openshiftImageSource) GetManifest() (manifest []byte, unverifiedCanonicalDigest string, err error) {
func (s *openshiftImageSource) GetManifest() ([]byte, error) {
if err := s.ensureImageIsResolved(); err != nil {
return nil, "", err
return nil, err
}
return s.docker.GetManifest()
}

View File

@ -35,7 +35,7 @@ type ImageSource interface {
// May be "" if unknown.
IntendedDockerReference() string
// GetManifest returns the image's manifest. It may use a remote (= slow) service.
GetManifest() (manifest []byte, unverifiedCanonicalDigest string, err error)
GetManifest() ([]byte, error)
GetLayer(digest string) (io.ReadCloser, error)
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
GetSignatures() ([][]byte, error)
@ -71,7 +71,6 @@ type Image interface {
type DockerImageManifest struct {
Name string
Tag string
Digest string
RepoTags []string
Created time.Time
DockerVersion string