mirror of
https://github.com/containers/skopeo.git
synced 2025-08-11 03:12:26 +00:00
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:
parent
e3d257e7b5
commit
60e8d63712
@ -54,7 +54,7 @@ func copyHandler(context *cli.Context) {
|
|||||||
}
|
}
|
||||||
signBy := context.String("sign-by")
|
signBy := context.String("sign-by")
|
||||||
|
|
||||||
manifest, _, err := src.GetManifest()
|
manifest, err := src.GetManifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatalf("Error reading manifest: %s", err.Error())
|
logrus.Fatalf("Error reading manifest: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/cli"
|
"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.
|
// 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 {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
if c.Bool("raw") {
|
rawManifest, err := img.Manifest()
|
||||||
b, err := img.Manifest()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
fmt.Println(string(b))
|
if c.Bool("raw") {
|
||||||
|
fmt.Println(string(rawManifest))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
imgInspect, err := img.Inspect()
|
imgInspect, err := img.Inspect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
manifestDigest, err := dockerutils.ManifestDigest(rawManifest)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("Error computing manifest digest: %s", err.Error())
|
||||||
|
}
|
||||||
outputData := inspectOutput{
|
outputData := inspectOutput{
|
||||||
Name: imgInspect.Name,
|
Name: imgInspect.Name,
|
||||||
Tag: imgInspect.Tag,
|
Tag: imgInspect.Tag,
|
||||||
Digest: imgInspect.Digest,
|
Digest: manifestDigest,
|
||||||
RepoTags: imgInspect.RepoTags,
|
RepoTags: imgInspect.RepoTags,
|
||||||
Created: imgInspect.Created,
|
Created: imgInspect.Created,
|
||||||
DockerVersion: imgInspect.DockerVersion,
|
DockerVersion: imgInspect.DockerVersion,
|
||||||
|
@ -84,13 +84,8 @@ func (s *dirImageSource) IntendedDockerReference() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *dirImageSource) GetManifest() ([]byte, string, error) {
|
func (s *dirImageSource) GetManifest() ([]byte, error) {
|
||||||
manifest, err := ioutil.ReadFile(manifestPath(s.dir))
|
return 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) GetLayer(digest string) (io.ReadCloser, error) {
|
func (s *dirImageSource) GetLayer(digest string) (io.ReadCloser, error) {
|
||||||
|
@ -20,8 +20,7 @@ var (
|
|||||||
|
|
||||||
type dockerImage struct {
|
type dockerImage struct {
|
||||||
src *dockerImageSource
|
src *dockerImageSource
|
||||||
digest string
|
cachedManifest []byte // Private cache for Manifest(); nil if not yet known.
|
||||||
rawManifest []byte
|
|
||||||
cachedSignatures [][]byte // Private cache for Signatures(); 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.
|
// 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) {
|
func (i *dockerImage) Manifest() ([]byte, error) {
|
||||||
if err := i.retrieveRawManifest(); err != nil {
|
if i.cachedManifest == nil {
|
||||||
|
m, err := i.src.GetManifest()
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return i.rawManifest, nil
|
i.cachedManifest = m
|
||||||
|
}
|
||||||
|
return i.cachedManifest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -122,7 +125,7 @@ type v1Image struct {
|
|||||||
OS string `json:"os,omitempty"`
|
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{}
|
v1 := &v1Image{}
|
||||||
if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), v1); err != nil {
|
if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), v1); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -130,7 +133,6 @@ func makeImageManifest(name string, m *manifestSchema1, dgst string, tagList []s
|
|||||||
return &types.DockerImageManifest{
|
return &types.DockerImageManifest{
|
||||||
Name: name,
|
Name: name,
|
||||||
Tag: m.Tag,
|
Tag: m.Tag,
|
||||||
Digest: dgst,
|
|
||||||
RepoTags: tagList,
|
RepoTags: tagList,
|
||||||
DockerVersion: v1.DockerVersion,
|
DockerVersion: v1.DockerVersion,
|
||||||
Created: v1.Created,
|
Created: v1.Created,
|
||||||
@ -181,25 +183,13 @@ func sanitize(s string) string {
|
|||||||
return strings.Replace(s, "/", "-", -1)
|
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) {
|
func (i *dockerImage) getSchema1Manifest() (manifest, error) {
|
||||||
if err := i.retrieveRawManifest(); err != nil {
|
manblob, err := i.Manifest()
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
mschema1 := &manifestSchema1{}
|
mschema1 := &manifestSchema1{}
|
||||||
if err := json.Unmarshal(i.rawManifest, mschema1); err != nil {
|
if err := json.Unmarshal(manblob, mschema1); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := fixManifestLayers(mschema1); err != nil {
|
if err := fixManifestLayers(mschema1); err != nil {
|
||||||
|
@ -55,23 +55,24 @@ func (s *dockerImageSource) IntendedDockerReference() string {
|
|||||||
return fmt.Sprintf("%s:%s", s.ref.Name(), s.tag)
|
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)
|
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) 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
|
// TODO(runcom) NO, switch on the resulter manifest like Docker is doing
|
||||||
res, err := s.c.makeRequest("GET", url, nil, nil)
|
res, err := s.c.makeRequest("GET", url, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
manblob, err := ioutil.ReadAll(res.Body)
|
manblob, err := ioutil.ReadAll(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
if res.StatusCode != http.StatusOK {
|
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) {
|
func (s *dockerImageSource) GetLayer(digest string) (io.ReadCloser, error) {
|
||||||
|
@ -193,9 +193,9 @@ func (s *openshiftImageSource) IntendedDockerReference() string {
|
|||||||
return s.client.canonicalDockerReference()
|
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 {
|
if err := s.ensureImageIsResolved(); err != nil {
|
||||||
return nil, "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return s.docker.GetManifest()
|
return s.docker.GetManifest()
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ type ImageSource interface {
|
|||||||
// May be "" if unknown.
|
// May be "" if unknown.
|
||||||
IntendedDockerReference() string
|
IntendedDockerReference() string
|
||||||
// GetManifest returns the image's manifest. It may use a remote (= slow) service.
|
// 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)
|
GetLayer(digest string) (io.ReadCloser, error)
|
||||||
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
||||||
GetSignatures() ([][]byte, error)
|
GetSignatures() ([][]byte, error)
|
||||||
@ -71,7 +71,6 @@ type Image interface {
|
|||||||
type DockerImageManifest struct {
|
type DockerImageManifest struct {
|
||||||
Name string
|
Name string
|
||||||
Tag string
|
Tag string
|
||||||
Digest string
|
|
||||||
RepoTags []string
|
RepoTags []string
|
||||||
Created time.Time
|
Created time.Time
|
||||||
DockerVersion string
|
DockerVersion string
|
||||||
|
Loading…
Reference in New Issue
Block a user