Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hack/vendor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ clone git github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42
clone git github.com/flynn-archive/go-shlex 3f9db97f856818214da2e1057f8ad84803971cff

# signatures
clone git github.com/containers/image 87c225dc35b864b6003d788c02c16d6db7387d08
clone git github.com/containers/image master
clone git github.com/opencontainers/image-spec 756744a5dcf23a6c8e4b11ef403522ca3ca33fd9
clone git k8s.io/kubernetes 4a3f9c5b19c7ff804cbc1bf37a15c044ca5d2353 https://github.com/openshift/kubernetes
clone git github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed
Expand Down
65 changes: 28 additions & 37 deletions integration-cli/docker_cli_pull_local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -701,50 +701,41 @@ func (s *DockerRegistriesSuite) TestPullWithPolicy(c *check.C) {
c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", out))
dgst := matches[1]

// test that I can pull by a digest and tag when the signature is valid for the tag
// default referencesByDigest == allow
//
// FIXME: requires containers/image#129 to make this test below work
// when that's in, change this test to assert success
//
// test that I can also pull by digest when the signature is valid for the tag
dgstRef := fmt.Sprintf("%s/busybox@%s", s.reg1.url, dgst)
_, _, err = dockerCmdWithError("pull", dgstRef)
c.Assert(err, check.NotNil)
dockerCmd(c, "pull", dgstRef)

dgstRefUnsigned := fmt.Sprintf("%s/busybox@%s", s.reg1.url, unsignedDigest)
out, _, err = dockerCmdWithError("pull", dgstRefUnsigned)
c.Assert(err, check.NotNil)
c.Assert(out, checker.Contains, "no signature exists")

// test that I can pull by tag but NOT by digest when the signature is valid for the tag
//
// FIXME: requires containers/image#129 to make this test below work
// when that's in, change this test to assert success
//
//// when referencesByDigest == reject
//policy2 := fmt.Sprintf(`{
//"default": [
//{
//"type": "reject"
//}
//],
//"transports": {
//"docker": {
//"%s/busybox": [
//{
//"referencesByDigest": "reject",
//"type": "signedBy",
//"keyType": "GPGKeys",
//"keyPath": "/root/personal-pubkey.gpg"
//}
//]
//}
//}
//}`, s.reg1.url)
//c.Assert(ioutil.WriteFile("/etc/containers/policy.json", []byte(policy2), 0755), check.IsNil)
//out, _, err = dockerCmdWithError("pull", dgstRef)
//c.Assert(err, check.NotNil)
//c.Assert(out, checker.Contains, "is not accepted")
// test that I cannot pull by digest when the signature is valid for the tag
policy2 := fmt.Sprintf(`{
"default": [
{
"type": "reject"
}
],
"transports": {
"docker": {
"%s/busybox": [
{
"type": "signedBy",
"signedIdentity": {
"type": "matchExact"
},
"keyType": "GPGKeys",
"keyPath": "/root/personal-pubkey.gpg"
}
]
}
}
}`, s.reg1.url)
c.Assert(ioutil.WriteFile("/etc/containers/policy.json", []byte(policy2), 0755), check.IsNil)
out, _, err = dockerCmdWithError("pull", dgstRef)
c.Assert(err, check.NotNil)
c.Assert(out, checker.Contains, "is not accepted")

// test pull default policy reject + allow anything from reg2
policy3 := fmt.Sprintf(`{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ func (s *dirImageSource) Reference() types.ImageReference {
func (s *dirImageSource) Close() {
}

// it's up to the caller to determine the MIME type of the returned manifest's bytes
// GetManifest returns the image's manifest along with its MIME type (which may be empty when it can't be determined but the manifest is available).
// It may use a remote (= slow) service.
func (s *dirImageSource) GetManifest() ([]byte, string, error) {
m, err := ioutil.ReadFile(s.ref.manifestPath())
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ func (s *daemonImageSource) prepareLayerData(tarManifest *manifestItem, parsedCo
return knownLayers, nil
}

// GetManifest returns the image's manifest along with its MIME type. The empty string is returned if the MIME type is unknown.
// GetManifest returns the image's manifest along with its MIME type (which may be empty when it can't be determined but the manifest is available).
// It may use a remote (= slow) service.
func (s *daemonImageSource) GetManifest() ([]byte, string, error) {
if s.generatedManifest == nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ func simplifyContentType(contentType string) string {
return mimeType
}

// GetManifest returns the image's manifest along with its MIME type (which may be empty when it can't be determined but the manifest is available).
// It may use a remote (= slow) service.
func (s *dockerImageSource) GetManifest() ([]byte, string, error) {
err := s.ensureManifestIsLoaded()
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/containers/image/manifest"
"github.com/containers/image/types"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
)

type config struct {
Expand Down Expand Up @@ -84,16 +85,18 @@ func manifestInstanceFromBlob(src types.ImageSource, manblob []byte, mt string)
// need to happen within the ImageSource.
case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType, "application/json":
return manifestSchema1FromManifest(manblob)
case manifest.DockerV2Schema2MediaType:
case manifest.DockerV2Schema2MediaType, imgspecv1.MediaTypeImageManifest:
// FIXME: OCI v1 is compatible with Docker Schema2, "docker_schema2.go" is good enough for reading images, but this will
// need to be modified for write support due to differing MIME types.
return manifestSchema2FromManifest(src, manblob)
case manifest.DockerV2ListMediaType:
return manifestSchema2FromManifestList(src, manblob)
default:
// if it's not a recognized manifest media type we'll try the last time
// If it's not a recognized manifest media type, or we have failed determining the type, we'll try one last time
// to deserialize using v2s1 as per https://github.com/docker/distribution/blob/master/manifests.go#L108
// and https://github.com/docker/distribution/blob/master/manifest/schema1/manifest.go#L50
//
// Crane registries can return "text/plain" also.
// Crane registries can also return "text/plain", or pretty much anything else depending on a file extension “recognized” in the tag.
// This makes no real sense, but it happens
// because requests for manifests are
// redirected to a content distribution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func FromUnparsedImage(unparsed *UnparsedImage) (types.Image, error) {
}, nil
}

// Manifest overrides the UnparsedImage.Manifest to use the fields which we have already fetched, after guessing and overrides.
// Manifest overrides the UnparsedImage.Manifest to always use the fields which we have already fetched.
func (i *sourcedImage) Manifest() ([]byte, string, error) {
return i.manifestBlob, i.manifestMIMEType, nil
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package layout

import (
"encoding/json"
"io"
"io/ioutil"
"os"

"github.com/containers/image/manifest"
"github.com/containers/image/types"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
)

type ociImageSource struct {
ref ociReference
}

// newImageSource returns an ImageSource for reading from an existing directory.
func newImageSource(ref ociReference) types.ImageSource {
return &ociImageSource{ref: ref}
}

// Reference returns the reference used to set up this source.
func (s *ociImageSource) Reference() types.ImageReference {
return s.ref
}

// Close removes resources associated with an initialized ImageSource, if any.
func (s *ociImageSource) Close() {
}

// GetManifest returns the image's manifest along with its MIME type (which may be empty when it can't be determined but the manifest is available).
// It may use a remote (= slow) service.
func (s *ociImageSource) GetManifest() ([]byte, string, error) {
descriptorPath := s.ref.descriptorPath(s.ref.tag)
data, err := ioutil.ReadFile(descriptorPath)
if err != nil {
return nil, "", err
}

desc := imgspecv1.Descriptor{}
err = json.Unmarshal(data, &desc)
if err != nil {
return nil, "", err
}

manifestPath, err := s.ref.blobPath(desc.Digest)
if err != nil {
return nil, "", err
}
m, err := ioutil.ReadFile(manifestPath)
if err != nil {
return nil, "", err
}

return m, manifest.GuessMIMEType(m), nil
}

func (s *ociImageSource) GetTargetManifest(digest string) ([]byte, string, error) {
manifestPath, err := s.ref.blobPath(digest)
if err != nil {
return nil, "", err
}

m, err := ioutil.ReadFile(manifestPath)
if err != nil {
return nil, "", err
}

return m, manifest.GuessMIMEType(m), nil
}

// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
func (s *ociImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) {
path, err := s.ref.blobPath(digest)
if err != nil {
return nil, 0, err
}

r, err := os.Open(path)
if err != nil {
return nil, 0, nil
}
fi, err := r.Stat()
if err != nil {
return nil, 0, nil
}
return r, fi.Size(), nil
}

func (s *ociImageSource) GetSignatures() ([][]byte, error) {
return [][]byte{}, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/containers/image/directory/explicitfilepath"
"github.com/containers/image/docker/reference"
"github.com/containers/image/image"
"github.com/containers/image/types"
)

Expand Down Expand Up @@ -169,15 +170,16 @@ func (ref ociReference) PolicyConfigurationNamespaces() []string {
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
func (ref ociReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
return nil, errors.New("Full Image support not implemented for oci: image names")
src := newImageSource(ref)
return image.FromSource(src)
}

// NewImageSource returns a types.ImageSource for this reference,
// asking the backend to use a manifest from requestedManifestMIMETypes if possible.
// nil requestedManifestMIMETypes means manifest.DefaultRequestedManifestMIMETypes.
// The caller must call .Close() on the returned ImageSource.
func (ref ociReference) NewImageSource(ctx *types.SystemContext, requestedManifestMIMETypes []string) (types.ImageSource, error) {
return nil, errors.New("Reading images not implemented for oci: image names")
return newImageSource(ref), nil
}

// NewImageDestination returns a types.ImageDestination for this reference.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ func (s *openshiftImageSource) GetTargetManifest(digest string) ([]byte, string,
return s.docker.GetTargetManifest(digest)
}

// GetManifest returns the image's manifest along with its MIME type (which may be empty when it can't be determined but the manifest is available).
// It may use a remote (= slow) service.
func (s *openshiftImageSource) GetManifest() ([]byte, string, error) {
if err := s.ensureImageIsResolved(); err != nil {
return nil, "", err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ func (pr *prSignedBy) UnmarshalJSON(data []byte) error {
return InvalidPolicyFormatError(fmt.Sprintf("Unexpected policy requirement type \"%s\"", tmp.Type))
}
if signedIdentity == nil {
tmp.SignedIdentity = NewPRMMatchExact()
tmp.SignedIdentity = NewPRMMatchRepoDigestOrExact()
} else {
si, err := newPolicyReferenceMatchFromJSON(signedIdentity)
if err != nil {
Expand Down Expand Up @@ -501,7 +501,7 @@ func (pr *prSignedBaseLayer) UnmarshalJSON(data []byte) error {
return nil
}

// newPolicyRequirementFromJSON parses JSON data into a PolicyReferenceMatch implementation.
// newPolicyReferenceMatchFromJSON parses JSON data into a PolicyReferenceMatch implementation.
func newPolicyReferenceMatchFromJSON(data []byte) (PolicyReferenceMatch, error) {
var typeField prmCommon
if err := json.Unmarshal(data, &typeField); err != nil {
Expand All @@ -511,6 +511,8 @@ func newPolicyReferenceMatchFromJSON(data []byte) (PolicyReferenceMatch, error)
switch typeField.Type {
case prmTypeMatchExact:
res = &prmMatchExact{}
case prmTypeMatchRepoDigestOrExact:
res = &prmMatchRepoDigestOrExact{}
case prmTypeMatchRepository:
res = &prmMatchRepository{}
case prmTypeExactReference:
Expand Down Expand Up @@ -561,6 +563,41 @@ func (prm *prmMatchExact) UnmarshalJSON(data []byte) error {
return nil
}

// newPRMMatchRepoDigestOrExact is NewPRMMatchRepoDigestOrExact, except it resturns the private type.
func newPRMMatchRepoDigestOrExact() *prmMatchRepoDigestOrExact {
return &prmMatchRepoDigestOrExact{prmCommon{Type: prmTypeMatchRepoDigestOrExact}}
}

// NewPRMMatchRepoDigestOrExact returns a new "matchRepoDigestOrExact" PolicyReferenceMatch.
func NewPRMMatchRepoDigestOrExact() PolicyReferenceMatch {
return newPRMMatchRepoDigestOrExact()
}

// Compile-time check that prmMatchRepoDigestOrExact implements json.Unmarshaler.
var _ json.Unmarshaler = (*prmMatchRepoDigestOrExact)(nil)

// UnmarshalJSON implements the json.Unmarshaler interface.
func (prm *prmMatchRepoDigestOrExact) UnmarshalJSON(data []byte) error {
*prm = prmMatchRepoDigestOrExact{}
var tmp prmMatchRepoDigestOrExact
if err := paranoidUnmarshalJSONObject(data, func(key string) interface{} {
switch key {
case "type":
return &tmp.Type
default:
return nil
}
}); err != nil {
return err
}

if tmp.Type != prmTypeMatchRepoDigestOrExact {
return InvalidPolicyFormatError(fmt.Sprintf("Unexpected policy requirement type \"%s\"", tmp.Type))
}
*prm = *newPRMMatchRepoDigestOrExact()
return nil
}

// newPRMMatchRepository is NewPRMMatchRepository, except it resturns the private type.
func newPRMMatchRepository() *prmMatchRepository {
return &prmMatchRepository{prmCommon{Type: prmTypeMatchRepository}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,29 @@ func (prm *prmMatchExact) matchesDockerReference(image types.UnparsedImage, sign
return signature.String() == intended.String()
}

func (prm *prmMatchRepoDigestOrExact) matchesDockerReference(image types.UnparsedImage, signatureDockerReference string) bool {
intended, signature, err := parseImageAndDockerReference(image, signatureDockerReference)
if err != nil {
return false
}

// Do not add default tags: image.Reference().DockerReference() should contain it already, and signatureDockerReference should be exact; so, verify that now.
if reference.IsNameOnly(signature) {
return false
}
switch intended.(type) {
case reference.NamedTagged: // Includes the case when intended has both a tag and a digest.
return signature.String() == intended.String()
case reference.Canonical:
// We don’t actually compare the manifest digest against the signature here; that happens prSignedBy.in UnparsedImage.Manifest.
// Becase UnparsedImage.Manifest verifies the intended.Digest() against the manifest, and prSignedBy verifies the signature digest against the manifest,
// we know that signature digest matches intended.Digest() (but intended.Digest() and signature digest may use different algorithms)
return signature.Name() == intended.Name()
default: // !reference.IsNameOnly(intended)
return false
}
}

func (prm *prmMatchRepository) matchesDockerReference(image types.UnparsedImage, signatureDockerReference string) bool {
intended, signature, err := parseImageAndDockerReference(image, signatureDockerReference)
if err != nil {
Expand Down
Loading