Skip to content

Commit

Permalink
Update embedded Docker reference, if any, in copy.Image
Browse files Browse the repository at this point in the history
Add EmbbeddedDockerReference to types.ManifestUpdateOptions, and use it
in copy.Image.  Pushing signed schema1 images to a different location
is impossible (skopeo users can use --remove-signatures), but signing an
unsigned schema1 image while pushing it does work.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
  • Loading branch information
mtrmac committed Sep 27, 2016
1 parent 8715980 commit d5ca90f
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 3 deletions.
35 changes: 33 additions & 2 deletions copy/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,13 @@ func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, des
return fmt.Errorf("Can not copy signatures: %v", err)
}
}

canModifyManifest := len(sigs) == 0
manifestUpdates := types.ManifestUpdateOptions{}

if err := updateEmbeddedDockerReference(&manifestUpdates, dest, src, canModifyManifest); err != nil {
return err
}

writeReport("Getting image source configuration\n")
srcConfigInfo, err := src.ConfigInfo()
Expand Down Expand Up @@ -176,8 +182,6 @@ func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, des
}
destLayerInfos = append(destLayerInfos, destLayer)
}

manifestUpdates := types.ManifestUpdateOptions{}
if layerDigestsDiffer(srcLayerInfos, destLayerInfos) {
manifestUpdates.LayerInfos = destLayerInfos
}
Expand Down Expand Up @@ -227,6 +231,33 @@ func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, des
return nil
}

// updateEmbeddedDockerReference handles the Docker reference embedded in Docker schema1 manifests.
func updateEmbeddedDockerReference(manifestUpdates *types.ManifestUpdateOptions, dest types.ImageDestination, src types.Image, canModifyManifest bool) error {
manifestRef, err := src.EmbeddedDockerReference()
if err != nil {
return fmt.Errorf("Error parsing source image for embedded Docker reference: %v", err)
}
if manifestRef == nil {
return nil // No embedded manifest
}

destRef := dest.Reference().DockerReference()
if destRef == nil {
return nil // Destination does not care about Docker references
}

if destRef.String() == manifestRef.String() {
return nil // The reference matches
}

if !canModifyManifest {
return fmt.Errorf("Copying a schema1 image with embedded Docker reference %s to %s (Docker reference %s) would invalidate existing signatures. Explicitly enable signature removal to proceed anyway",
manifestRef.String(), transports.ImageName(dest.Reference()), destRef.String())
}
manifestUpdates.EmbeddedDockerReference = destRef
return nil
}

// layerDigestsDiffer return true iff the digests in a and b differ (ignoring sizes and possible other fields)
func layerDigestsDiffer(a, b []types.BlobInfo) bool {
if len(a) != len(b) {
Expand Down
8 changes: 8 additions & 0 deletions image/docker_schema1.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ func (m *manifestSchema1) UpdatedManifest(options types.ManifestUpdateOptions) (
copy.FSLayers[(len(options.LayerInfos)-1)-i].BlobSum = info.Digest
}
}
if options.EmbeddedDockerReference != nil {
copy.Name = options.EmbeddedDockerReference.Name()
if tagged, isTagged := options.EmbeddedDockerReference.(reference.NamedTagged); isTagged {
copy.Tag = tagged.Tag()
} else {
copy.Tag = ""
}
}
// docker/distribution requires a signature even if the incoming data uses the nominally unsigned DockerV2Schema1MediaType.
unsigned, err := json.Marshal(copy)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions image/docker_schema2.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package image

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"

Expand Down Expand Up @@ -86,5 +87,8 @@ func (m *manifestSchema2) UpdatedManifest(options types.ManifestUpdateOptions) (
copy.LayersDescriptors[i].Size = info.Size
}
}
if options.EmbeddedDockerReference != nil {
return nil, errors.New("ManifestUpdateOptions.EmbeddedDockerReference is not supported for schema2 images")
}
return json.Marshal(copy)
}
6 changes: 6 additions & 0 deletions signature/policy_reference_match_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ func (ref refImageMock) ConfigInfo() (types.BlobInfo, error) {
func (ref refImageMock) LayerInfos() ([]types.BlobInfo, error) {
panic("unexpected call to a mock function")
}
func (ref refImageMock) EmbeddedDockerReference() (reference.Named, error) {
panic("unexpected call to a mock function")
}
func (ref refImageMock) Inspect() (*types.ImageInspectInfo, error) {
panic("unexpected call to a mock function")
}
Expand Down Expand Up @@ -288,6 +291,9 @@ func (ref forbiddenImageMock) ConfigInfo() (types.BlobInfo, error) {
func (ref forbiddenImageMock) LayerInfos() ([]types.BlobInfo, error) {
panic("unexpected call to a mock function")
}
func (ref forbiddenImageMock) EmbeddedDockerReference() (reference.Named, error) {
panic("unexpected call to a mock function")
}
func (ref forbiddenImageMock) Inspect() (*types.ImageInspectInfo, error) {
panic("unexpected call to a mock function")
}
Expand Down
3 changes: 2 additions & 1 deletion types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ type Image interface {

// ManifestUpdateOptions is a way to pass named optional arguments to Image.UpdatedManifest
type ManifestUpdateOptions struct {
LayerInfos []BlobInfo // Complete BlobInfos (size+digest) which should replace the originals, in order (the root layer first, and then successive layered layers)
LayerInfos []BlobInfo // Complete BlobInfos (size+digest) which should replace the originals, in order (the root layer first, and then successive layered layers)
EmbeddedDockerReference reference.Named
}

// ImageInspectInfo is a set of metadata describing Docker images, primarily their manifest and configuration.
Expand Down

0 comments on commit d5ca90f

Please sign in to comment.