diff --git a/hack/vendor.sh b/hack/vendor.sh index 1caa6b3f4e833..105a565e8d9f9 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -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 diff --git a/integration-cli/docker_cli_pull_local_test.go b/integration-cli/docker_cli_pull_local_test.go index a8ddf93a598f5..5021c11c6e17c 100644 --- a/integration-cli/docker_cli_pull_local_test.go +++ b/integration-cli/docker_cli_pull_local_test.go @@ -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(`{ diff --git a/vendor/src/github.com/containers/image/directory/directory_src.go b/vendor/src/github.com/containers/image/directory/directory_src.go index c448a02ccd764..597fe033e1221 100644 --- a/vendor/src/github.com/containers/image/directory/directory_src.go +++ b/vendor/src/github.com/containers/image/directory/directory_src.go @@ -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 { diff --git a/vendor/src/github.com/containers/image/docker/daemon/daemon_src.go b/vendor/src/github.com/containers/image/docker/daemon/daemon_src.go index 7bdcf3ee54d5c..8d9d5692a15f6 100644 --- a/vendor/src/github.com/containers/image/docker/daemon/daemon_src.go +++ b/vendor/src/github.com/containers/image/docker/daemon/daemon_src.go @@ -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 { diff --git a/vendor/src/github.com/containers/image/docker/docker_image_src.go b/vendor/src/github.com/containers/image/docker/docker_image_src.go index 3a97dcb0de4ab..10acd14c4c9dd 100644 --- a/vendor/src/github.com/containers/image/docker/docker_image_src.go +++ b/vendor/src/github.com/containers/image/docker/docker_image_src.go @@ -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 { diff --git a/vendor/src/github.com/containers/image/image/manifest.go b/vendor/src/github.com/containers/image/image/manifest.go index 2f3220b449198..7f2320e1c0500 100644 --- a/vendor/src/github.com/containers/image/image/manifest.go +++ b/vendor/src/github.com/containers/image/image/manifest.go @@ -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 { @@ -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 diff --git a/vendor/src/github.com/containers/image/image/sourced.go b/vendor/src/github.com/containers/image/image/sourced.go index c286d8f165687..a7c25ab13c494 100644 --- a/vendor/src/github.com/containers/image/image/sourced.go +++ b/vendor/src/github.com/containers/image/image/sourced.go @@ -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 } diff --git a/vendor/src/github.com/containers/image/oci/layout/oci_src.go b/vendor/src/github.com/containers/image/oci/layout/oci_src.go new file mode 100644 index 0000000000000..ecc228ac7d8ea --- /dev/null +++ b/vendor/src/github.com/containers/image/oci/layout/oci_src.go @@ -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 +} diff --git a/vendor/src/github.com/containers/image/oci/layout/oci_transport.go b/vendor/src/github.com/containers/image/oci/layout/oci_transport.go index 4124684b52348..940d8169b7620 100644 --- a/vendor/src/github.com/containers/image/oci/layout/oci_transport.go +++ b/vendor/src/github.com/containers/image/oci/layout/oci_transport.go @@ -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" ) @@ -169,7 +170,8 @@ 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, @@ -177,7 +179,7 @@ func (ref ociReference) NewImage(ctx *types.SystemContext) (types.Image, error) // 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. diff --git a/vendor/src/github.com/containers/image/openshift/openshift.go b/vendor/src/github.com/containers/image/openshift/openshift.go index e77a175bb5614..981a70a79da4b 100644 --- a/vendor/src/github.com/containers/image/openshift/openshift.go +++ b/vendor/src/github.com/containers/image/openshift/openshift.go @@ -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 diff --git a/vendor/src/github.com/containers/image/signature/policy_config.go b/vendor/src/github.com/containers/image/signature/policy_config.go index d5e20489bd1da..2bd95569009a2 100644 --- a/vendor/src/github.com/containers/image/signature/policy_config.go +++ b/vendor/src/github.com/containers/image/signature/policy_config.go @@ -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 { @@ -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 { @@ -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: @@ -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}} diff --git a/vendor/src/github.com/containers/image/signature/policy_reference_match.go b/vendor/src/github.com/containers/image/signature/policy_reference_match.go index aedda8d09b6fa..ced51e6e070af 100644 --- a/vendor/src/github.com/containers/image/signature/policy_reference_match.go +++ b/vendor/src/github.com/containers/image/signature/policy_reference_match.go @@ -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 { diff --git a/vendor/src/github.com/containers/image/signature/policy_types.go b/vendor/src/github.com/containers/image/signature/policy_types.go index 5775ab21dd549..4cd770f11c849 100644 --- a/vendor/src/github.com/containers/image/signature/policy_types.go +++ b/vendor/src/github.com/containers/image/signature/policy_types.go @@ -116,10 +116,11 @@ type prmCommon struct { type prmTypeIdentifier string const ( - prmTypeMatchExact prmTypeIdentifier = "matchExact" - prmTypeMatchRepository prmTypeIdentifier = "matchRepository" - prmTypeExactReference prmTypeIdentifier = "exactReference" - prmTypeExactRepository prmTypeIdentifier = "exactRepository" + prmTypeMatchExact prmTypeIdentifier = "matchExact" + prmTypeMatchRepoDigestOrExact prmTypeIdentifier = "matchRepoDigestOrExact" + prmTypeMatchRepository prmTypeIdentifier = "matchRepository" + prmTypeExactReference prmTypeIdentifier = "exactReference" + prmTypeExactRepository prmTypeIdentifier = "exactRepository" ) // prmMatchExact is a PolicyReferenceMatch with type = prmMatchExact: the two references must match exactly. @@ -127,6 +128,12 @@ type prmMatchExact struct { prmCommon } +// prmMatchRepoDigestOrExact is a PolicyReferenceMatch with type = prmMatchExactOrDigest: the two references must match exactly, +// except that digest references are also accepted if the repository name matches (regardless of tag/digest) and the signature applies to the referenced digest +type prmMatchRepoDigestOrExact struct { + prmCommon +} + // prmMatchRepository is a PolicyReferenceMatch with type = prmMatchRepository: the two references must use the same repository, may differ in the tag. type prmMatchRepository struct { prmCommon diff --git a/vendor/src/github.com/containers/image/types/types.go b/vendor/src/github.com/containers/image/types/types.go index a893c421616cd..d675bece06a65 100644 --- a/vendor/src/github.com/containers/image/types/types.go +++ b/vendor/src/github.com/containers/image/types/types.go @@ -108,7 +108,7 @@ type ImageSource interface { Reference() ImageReference // Close removes resources associated with an initialized ImageSource, if any. Close() - // 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. GetManifest() ([]byte, string, error) // GetTargetManifest returns an image's manifest given a digest. This is mainly used to retrieve a single image's manifest