package server import ( "encoding/json" "fmt" "path" "github.com/docker/distribution" "github.com/docker/distribution/context" "github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/reference" "github.com/docker/libtrust" "k8s.io/kubernetes/pkg/util/sets" imageapi "github.com/openshift/origin/pkg/image/api" ) func unmarshalManifestSchema1(content []byte, signatures [][]byte) (distribution.Manifest, error) { // prefer signatures from the manifest if _, err := libtrust.ParsePrettySignature(content, "signatures"); err == nil { sm := schema1.SignedManifest{Canonical: content} if err = json.Unmarshal(content, &sm); err == nil { return &sm, nil } } jsig, err := libtrust.NewJSONSignature(content, signatures...) if err != nil { return nil, err } // Extract the pretty JWS content, err = jsig.PrettySignature("signatures") if err != nil { return nil, err } var sm schema1.SignedManifest if err = json.Unmarshal(content, &sm); err != nil { return nil, err } return &sm, nil } type manifestSchema1Handler struct { repo *repository manifest *schema1.SignedManifest } var _ ManifestHandler = &manifestSchema1Handler{} func (h *manifestSchema1Handler) FillImageMetadata(ctx context.Context, image *imageapi.Image) error { signatures, err := h.manifest.Signatures() if err != nil { return err } for _, signDigest := range signatures { image.DockerImageSignatures = append(image.DockerImageSignatures, signDigest) } if err := imageapi.ImageWithMetadata(image); err != nil { return err } refs := h.manifest.References() blobSet := sets.NewString() image.DockerImageMetadata.Size = int64(0) blobs := h.repo.Blobs(ctx) for i := range image.DockerImageLayers { layer := &image.DockerImageLayers[i] // DockerImageLayers represents h.manifest.Manifest.FSLayers in reversed order desc, err := blobs.Stat(ctx, refs[len(image.DockerImageLayers)-i-1].Digest) if err != nil { context.GetLogger(ctx).Errorf("failed to stat blob %s of image %s", layer.Name, image.DockerImageReference) return err } // The MediaType appeared in manifest schema v2. We need to fill it // manually in the old images if it is not already filled. if len(layer.MediaType) == 0 { if len(desc.MediaType) > 0 { layer.MediaType = desc.MediaType } else { layer.MediaType = schema1.MediaTypeManifestLayer } } layer.LayerSize = desc.Size // count empty layer just once (empty layer may actually have non-zero size) if !blobSet.Has(layer.Name) { image.DockerImageMetadata.Size += desc.Size blobSet.Insert(layer.Name) } } return nil } func (h *manifestSchema1Handler) Manifest() distribution.Manifest { return h.manifest } func (h *manifestSchema1Handler) Payload() (mediaType string, payload []byte, canonical []byte, err error) { mt, payload, err := h.manifest.Payload() return mt, payload, h.manifest.Canonical, err } func (h *manifestSchema1Handler) Verify(ctx context.Context, skipDependencyVerification bool) error { var errs distribution.ErrManifestVerification // we want to verify that referenced blobs exist locally - thus using upstream repository object directly repo := h.repo.Repository if len(path.Join(h.repo.registryAddr, h.manifest.Name)) > reference.NameTotalLengthMax { errs = append(errs, distribution.ErrManifestNameInvalid{ Name: h.manifest.Name, Reason: fmt.Errorf("/ must not be more than %d characters", reference.NameTotalLengthMax), }) } if !reference.NameRegexp.MatchString(h.manifest.Name) { errs = append(errs, distribution.ErrManifestNameInvalid{ Name: h.manifest.Name, Reason: fmt.Errorf("invalid manifest name format"), }) } if len(h.manifest.History) != len(h.manifest.FSLayers) { errs = append(errs, fmt.Errorf("mismatched history and fslayer cardinality %d != %d", len(h.manifest.History), len(h.manifest.FSLayers))) } if _, err := schema1.Verify(h.manifest); err != nil { switch err { case libtrust.ErrMissingSignatureKey, libtrust.ErrInvalidJSONContent, libtrust.ErrMissingSignatureKey: errs = append(errs, distribution.ErrManifestUnverified{}) default: if err.Error() == "invalid signature" { errs = append(errs, distribution.ErrManifestUnverified{}) } else { errs = append(errs, err) } } } if skipDependencyVerification { if len(errs) > 0 { return errs } return nil } for _, fsLayer := range h.manifest.References() { _, err := repo.Blobs(ctx).Stat(ctx, fsLayer.Digest) if err != nil { if err != distribution.ErrBlobUnknown { errs = append(errs, err) continue } // On error here, we always append unknown blob errors. errs = append(errs, distribution.ErrManifestBlobUnknown{Digest: fsLayer.Digest}) } } if len(errs) > 0 { return errs } return nil }