Skip to content

Commit

Permalink
Eliminate dependency on containers/image/docker
Browse files Browse the repository at this point in the history
… to minimize version skew (on strslice, tlsconfig)
  • Loading branch information
mtrmac committed Mar 6, 2017
1 parent ed7bc58 commit 5d42163
Showing 1 changed file with 118 additions and 2 deletions.
120 changes: 118 additions & 2 deletions pkg/cmd/admin/image/verify-signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (
"fmt"
"io"
"io/ioutil"
"strings"

"github.com/containers/image/docker"
"github.com/containers/image/docker/policyconfiguration"
"github.com/containers/image/docker/reference"
"github.com/containers/image/signature"
sigtypes "github.com/containers/image/types"
"github.com/openshift/origin/pkg/client"
Expand Down Expand Up @@ -143,6 +145,120 @@ func (o *VerifyImageSignatureOptions) clearSignatureVerificationStatus(s *imagea
s.IssuedBy = nil
}

// fakeDockerTransport is containers/image/docker.Transport, except that it only provides identity information.
var fakeDockerTransport = dockerTransport{}

type dockerTransport struct{}

func (t dockerTransport) Name() string {
return "docker"
}

// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an ImageReference.
func (t dockerTransport) ParseReference(reference string) (sigtypes.ImageReference, error) {
return parseDockerReference(reference)
}

// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys
// (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value).
// It is acceptable to allow an invalid value which will never be matched, it can "only" cause user confusion.
// scope passed to this function will not be "", that value is always allowed.
func (t dockerTransport) ValidatePolicyConfigurationScope(scope string) error {
// FIXME? We could be verifying the various character set and length restrictions
// from docker/distribution/reference.regexp.go, but other than that there
// are few semantically invalid strings.
return nil
}

// fakeDockerReference is containers/image/docker.Reference, except that only provides identity information.
type fakeDockerReference struct{ ref reference.Named }

// parseReference converts a string, which should not start with the ImageTransport.Name prefix, into an Docker ImageReference.
func parseDockerReference(refString string) (sigtypes.ImageReference, error) {
if !strings.HasPrefix(refString, "//") {
return nil, fmt.Errorf("docker: image reference %s does not start with //", refString)
}
ref, err := reference.ParseNormalizedNamed(strings.TrimPrefix(refString, "//"))
if err != nil {
return nil, err
}
ref = reference.TagNameOnly(ref)

if reference.IsNameOnly(ref) {
return nil, fmt.Errorf("Docker reference %s has neither a tag nor a digest", reference.FamiliarString(ref))
}
// A github.com/distribution/reference value can have a tag and a digest at the same time!
// The docker/distribution API does not really support that (we can’t ask for an image with a specific
// tag and digest), so fail. This MAY be accepted in the future.
// (Even if it were supported, the semantics of policy namespaces are unclear - should we drop
// the tag or the digest first?)
_, isTagged := ref.(reference.NamedTagged)
_, isDigested := ref.(reference.Canonical)
if isTagged && isDigested {
return nil, fmt.Errorf("Docker references with both a tag and digest are currently not supported")
}
return fakeDockerReference{
ref: ref,
}, nil
}

func (ref fakeDockerReference) Transport() sigtypes.ImageTransport {
return fakeDockerTransport
}

// StringWithinTransport returns a string representation of the reference, which MUST be such that
// reference.Transport().ParseReference(reference.StringWithinTransport()) returns an equivalent reference.
// NOTE: The returned string is not promised to be equal to the original input to ParseReference;
// e.g. default attribute values omitted by the user may be filled in in the return value, or vice versa.
// WARNING: Do not use the return value in the UI to describe an image, it does not contain the Transport().Name() prefix.
func (ref fakeDockerReference) StringWithinTransport() string {
return "//" + reference.FamiliarString(ref.ref)
}

// DockerReference returns a Docker reference associated with this reference
// (fully explicit, i.e. !reference.IsNameOnly, but reflecting user intent,
// not e.g. after redirect or alias processing), or nil if unknown/not applicable.
func (ref fakeDockerReference) DockerReference() reference.Named {
return ref.ref
}

// PolicyConfigurationIdentity returns a string representation of the reference, suitable for policy lookup.
// This MUST reflect user intent, not e.g. after processing of third-party redirects or aliases;
// The value SHOULD be fully explicit about its semantics, with no hidden defaults, AND canonical
// (i.e. various references with exactly the same semantics should return the same configuration identity)
// It is fine for the return value to be equal to StringWithinTransport(), and it is desirable but
// not required/guaranteed that it will be a valid input to Transport().ParseReference().
// Returns "" if configuration identities for these references are not supported.
func (ref fakeDockerReference) PolicyConfigurationIdentity() string {
res, err := policyconfiguration.DockerReferenceIdentity(ref.ref)
if res == "" || err != nil { // Coverage: Should never happen, NewReference above should refuse values which could cause a failure.
panic(fmt.Sprintf("Internal inconsistency: policyconfiguration.DockerReferenceIdentity returned %#v, %v", res, err))
}
return res
}

// PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search
// for if explicit configuration for PolicyConfigurationIdentity() is not set. The list will be processed
// in order, terminating on first match, and an implicit "" is always checked at the end.
// It is STRONGLY recommended for the first element, if any, to be a prefix of PolicyConfigurationIdentity(),
// and each following element to be a prefix of the element preceding it.
func (ref fakeDockerReference) PolicyConfigurationNamespaces() []string {
return policyconfiguration.DockerReferenceNamespaces(ref.ref)
}

func (ref fakeDockerReference) NewImage(ctx *sigtypes.SystemContext) (sigtypes.Image, error) {
panic("Unimplemented")
}
func (ref fakeDockerReference) NewImageSource(ctx *sigtypes.SystemContext, requestedManifestMIMETypes []string) (sigtypes.ImageSource, error) {
panic("Unimplemented")
}
func (ref fakeDockerReference) NewImageDestination(ctx *sigtypes.SystemContext) (sigtypes.ImageDestination, error) {
panic("Unimplemented")
}
func (ref fakeDockerReference) DeleteImage(ctx *sigtypes.SystemContext) error {
panic("Unimplemented")
}

// unparsedImage implements sigtypes.UnparsedImage, to allow evaluating the signature policy
// against an image without having to make it pullable by containers/image
type unparsedImage struct {
Expand All @@ -152,7 +268,7 @@ type unparsedImage struct {
}

func newUnparsedImage(expectedIdentity string, img *imageapi.Image, signature []byte) (sigtypes.UnparsedImage, error) {
ref, err := docker.ParseReference("//" + expectedIdentity)
ref, err := parseDockerReference("//" + expectedIdentity)
if err != nil {
return nil, fmt.Errorf("Invalid --expected-identity: %v", err)
}
Expand Down

0 comments on commit 5d42163

Please sign in to comment.