mirror of
https://github.com/containers/skopeo.git
synced 2025-06-25 14:22:29 +00:00
Add option to preserve digests on copy
When enabled, if digests can't be preserved an error will be raised. Signed-off-by: James Hewitt <james.hewitt@uk.ibm.com>
This commit is contained in:
parent
25868f17c0
commit
2046bfdaaa
@ -33,6 +33,7 @@ type copyOptions struct {
|
|||||||
quiet bool // Suppress output information when copying images
|
quiet bool // Suppress output information when copying images
|
||||||
all bool // Copy all of the images if the source is a list
|
all bool // Copy all of the images if the source is a list
|
||||||
multiArch commonFlag.OptionalString // How to handle multi architecture images
|
multiArch commonFlag.OptionalString // How to handle multi architecture images
|
||||||
|
preserveDigests bool // Preserve digests during copy
|
||||||
encryptLayer []int // The list of layers to encrypt
|
encryptLayer []int // The list of layers to encrypt
|
||||||
encryptionKeys []string // Keys needed to encrypt the image
|
encryptionKeys []string // Keys needed to encrypt the image
|
||||||
decryptionKeys []string // Keys needed to decrypt the image
|
decryptionKeys []string // Keys needed to decrypt the image
|
||||||
@ -74,6 +75,7 @@ See skopeo(1) section "IMAGE NAMES" for the expected format
|
|||||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress output information when copying images")
|
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress output information when copying images")
|
||||||
flags.BoolVarP(&opts.all, "all", "a", false, "Copy all images if SOURCE-IMAGE is a list")
|
flags.BoolVarP(&opts.all, "all", "a", false, "Copy all images if SOURCE-IMAGE is a list")
|
||||||
flags.Var(commonFlag.NewOptionalStringValue(&opts.multiArch), "multi-arch", `How to handle multi-architecture images (system, all, or index-only)`)
|
flags.Var(commonFlag.NewOptionalStringValue(&opts.multiArch), "multi-arch", `How to handle multi-architecture images (system, all, or index-only)`)
|
||||||
|
flags.BoolVar(&opts.preserveDigests, "preserve-digests", false, "Preserve digests of images and lists")
|
||||||
flags.BoolVar(&opts.removeSignatures, "remove-signatures", false, "Do not copy signatures from SOURCE-IMAGE")
|
flags.BoolVar(&opts.removeSignatures, "remove-signatures", false, "Do not copy signatures from SOURCE-IMAGE")
|
||||||
flags.StringVar(&opts.signByFingerprint, "sign-by", "", "Sign the image using a GPG key with the specified `FINGERPRINT`")
|
flags.StringVar(&opts.signByFingerprint, "sign-by", "", "Sign the image using a GPG key with the specified `FINGERPRINT`")
|
||||||
flags.StringVar(&opts.digestFile, "digestfile", "", "Write the digest of the pushed image to the specified file")
|
flags.StringVar(&opts.digestFile, "digestfile", "", "Write the digest of the pushed image to the specified file")
|
||||||
@ -226,6 +228,7 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) error {
|
|||||||
DestinationCtx: destinationCtx,
|
DestinationCtx: destinationCtx,
|
||||||
ForceManifestMIMEType: manifestType,
|
ForceManifestMIMEType: manifestType,
|
||||||
ImageListSelection: imageListSelection,
|
ImageListSelection: imageListSelection,
|
||||||
|
PreserveDigests: opts.preserveDigests,
|
||||||
OciDecryptConfig: decConfig,
|
OciDecryptConfig: decConfig,
|
||||||
OciEncryptLayers: encLayers,
|
OciEncryptLayers: encLayers,
|
||||||
OciEncryptConfig: encConfig,
|
OciEncryptConfig: encConfig,
|
||||||
|
@ -67,6 +67,7 @@ _skopeo_copy() {
|
|||||||
--dest-no-creds
|
--dest-no-creds
|
||||||
--dest-oci-accept-uncompressed-layers
|
--dest-oci-accept-uncompressed-layers
|
||||||
--dest-precompute-digests
|
--dest-precompute-digests
|
||||||
|
--preserve-digests
|
||||||
"
|
"
|
||||||
|
|
||||||
local transports
|
local transports
|
||||||
|
@ -54,6 +54,10 @@ Directory to use to share blobs across OCI repositories.
|
|||||||
|
|
||||||
After copying the image, write the digest of the resulting image to the file.
|
After copying the image, write the digest of the resulting image to the file.
|
||||||
|
|
||||||
|
**--preserve-digests**
|
||||||
|
|
||||||
|
Preserve the digests during copying. Fail if the digest cannot be preserved.
|
||||||
|
|
||||||
**--encrypt-layer** _ints_
|
**--encrypt-layer** _ints_
|
||||||
|
|
||||||
*Experimental* the 0-indexed layer indices, with support for negative indexing (e.g. 0 is the first layer, -1 is the last layer)
|
*Experimental* the 0-indexed layer indices, with support for negative indexing (e.g. 0 is the first layer, -1 is the last layer)
|
||||||
|
2
go.mod
2
go.mod
@ -5,7 +5,7 @@ go 1.12
|
|||||||
require (
|
require (
|
||||||
github.com/containerd/containerd v1.5.8 // indirect
|
github.com/containerd/containerd v1.5.8 // indirect
|
||||||
github.com/containers/common v0.46.1-0.20211026130826-7abfd453c86f
|
github.com/containers/common v0.46.1-0.20211026130826-7abfd453c86f
|
||||||
github.com/containers/image/v5 v5.17.0
|
github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c
|
||||||
github.com/containers/ocicrypt v1.1.2
|
github.com/containers/ocicrypt v1.1.2
|
||||||
github.com/containers/storage v1.37.0
|
github.com/containers/storage v1.37.0
|
||||||
github.com/docker/docker v20.10.11+incompatible
|
github.com/docker/docker v20.10.11+incompatible
|
||||||
|
7
go.sum
7
go.sum
@ -232,8 +232,8 @@ github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRD
|
|||||||
github.com/containers/common v0.46.1-0.20211026130826-7abfd453c86f h1:jFFIV8QvsPgwkJHh3tjfREFRwSeMq5M8lt8vklkZaOk=
|
github.com/containers/common v0.46.1-0.20211026130826-7abfd453c86f h1:jFFIV8QvsPgwkJHh3tjfREFRwSeMq5M8lt8vklkZaOk=
|
||||||
github.com/containers/common v0.46.1-0.20211026130826-7abfd453c86f/go.mod h1:pVvmLTLCOZE300e4rex/QDmpnRmEM/5aZ/YfCkkjgZo=
|
github.com/containers/common v0.46.1-0.20211026130826-7abfd453c86f/go.mod h1:pVvmLTLCOZE300e4rex/QDmpnRmEM/5aZ/YfCkkjgZo=
|
||||||
github.com/containers/image/v5 v5.16.1/go.mod h1:mCvIFdzyyP1B0NBcZ80OIuaYqFn/OpFpaOMOMn1kU2M=
|
github.com/containers/image/v5 v5.16.1/go.mod h1:mCvIFdzyyP1B0NBcZ80OIuaYqFn/OpFpaOMOMn1kU2M=
|
||||||
github.com/containers/image/v5 v5.17.0 h1:KS5pro80CCsSp5qDBTMmSAWQo+xcBX19zUPExmYX2OQ=
|
github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c h1:WfMOQlq3CDvVe5ONUGfj9/MajskqUHnbo24j24Xg2ZM=
|
||||||
github.com/containers/image/v5 v5.17.0/go.mod h1:GnYVusVRFPMMTAAUkrcS8NNSpBp8oyrjOUe04AAmRr4=
|
github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c/go.mod h1:boW5ckkT0wu9obDEiOIxrtWQmz1znMuHiVMQPcpHnk0=
|
||||||
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
|
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
|
||||||
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
|
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
|
||||||
github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
|
github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
|
||||||
@ -651,8 +651,9 @@ github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqi
|
|||||||
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
|
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
|
||||||
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
|
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
|
||||||
github.com/opencontainers/selinux v1.8.5/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo=
|
github.com/opencontainers/selinux v1.8.5/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo=
|
||||||
github.com/opencontainers/selinux v1.9.1 h1:b4VPEF3O5JLZgdTDBmGepaaIbAo0GqoF6EBRq5f/g3Y=
|
|
||||||
github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||||
|
github.com/opencontainers/selinux v1.10.0 h1:rAiKF8hTcgLI3w0DHm6i0ylVVcOrlgR1kK99DRLDhyU=
|
||||||
|
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||||
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw=
|
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw=
|
||||||
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc=
|
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
@ -1248,6 +1248,15 @@ func (s *CopySuite) TestCopyManifestConversion(c *check.C) {
|
|||||||
verifyManifestMIMEType(c, destDir2, manifest.DockerV2Schema2MediaType)
|
verifyManifestMIMEType(c, destDir2, manifest.DockerV2Schema2MediaType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *CopySuite) TestCopyPreserveDigests(c *check.C) {
|
||||||
|
topDir, err := ioutil.TempDir("", "preserve-digests")
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
defer os.RemoveAll(topDir)
|
||||||
|
|
||||||
|
assertSkopeoSucceeds(c, "", "copy", knownListImage, "--multi-arch=all", "--preserve-digests", "dir:"+topDir)
|
||||||
|
assertSkopeoFails(c, ".*Instructed to preserve digests.*", "copy", knownListImage, "--multi-arch=all", "--preserve-digests", "--format=oci", "dir:"+topDir)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *CopySuite) testCopySchemaConversionRegistries(c *check.C, schema1Registry, schema2Registry string) {
|
func (s *CopySuite) testCopySchemaConversionRegistries(c *check.C, schema1Registry, schema2Registry string) {
|
||||||
topDir, err := ioutil.TempDir("", "schema-conversion")
|
topDir, err := ioutil.TempDir("", "schema-conversion")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
101
vendor/github.com/containers/image/v5/copy/copy.go
generated
vendored
101
vendor/github.com/containers/image/v5/copy/copy.go
generated
vendored
@ -80,13 +80,13 @@ type copier struct {
|
|||||||
|
|
||||||
// imageCopier tracks state specific to a single image (possibly an item of a manifest list)
|
// imageCopier tracks state specific to a single image (possibly an item of a manifest list)
|
||||||
type imageCopier struct {
|
type imageCopier struct {
|
||||||
c *copier
|
c *copier
|
||||||
manifestUpdates *types.ManifestUpdateOptions
|
manifestUpdates *types.ManifestUpdateOptions
|
||||||
src types.Image
|
src types.Image
|
||||||
diffIDsAreNeeded bool
|
diffIDsAreNeeded bool
|
||||||
canModifyManifest bool
|
cannotModifyManifestReason string // The reason the manifest cannot be modified, or an empty string if it can
|
||||||
canSubstituteBlobs bool
|
canSubstituteBlobs bool
|
||||||
ociEncryptLayers *[]int
|
ociEncryptLayers *[]int
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -129,10 +129,14 @@ type Options struct {
|
|||||||
DestinationCtx *types.SystemContext
|
DestinationCtx *types.SystemContext
|
||||||
ProgressInterval time.Duration // time to wait between reports to signal the progress channel
|
ProgressInterval time.Duration // time to wait between reports to signal the progress channel
|
||||||
Progress chan types.ProgressProperties // Reported to when ProgressInterval has arrived for a single artifact+offset.
|
Progress chan types.ProgressProperties // Reported to when ProgressInterval has arrived for a single artifact+offset.
|
||||||
|
|
||||||
|
// Preserve digests, and fail if we cannot.
|
||||||
|
PreserveDigests bool
|
||||||
// manifest MIME type of image set by user. "" is default and means use the autodetection to the the manifest MIME type
|
// manifest MIME type of image set by user. "" is default and means use the autodetection to the the manifest MIME type
|
||||||
ForceManifestMIMEType string
|
ForceManifestMIMEType string
|
||||||
ImageListSelection ImageListSelection // set to either CopySystemImage (the default), CopyAllImages, or CopySpecificImages to control which instances we copy when the source reference is a list; ignored if the source reference is not a list
|
ImageListSelection ImageListSelection // set to either CopySystemImage (the default), CopyAllImages, or CopySpecificImages to control which instances we copy when the source reference is a list; ignored if the source reference is not a list
|
||||||
Instances []digest.Digest // if ImageListSelection is CopySpecificImages, copy only these instances and the list itself
|
Instances []digest.Digest // if ImageListSelection is CopySpecificImages, copy only these instances and the list itself
|
||||||
|
|
||||||
// If OciEncryptConfig is non-nil, it indicates that an image should be encrypted.
|
// If OciEncryptConfig is non-nil, it indicates that an image should be encrypted.
|
||||||
// The encryption options is derived from the construction of EncryptConfig object.
|
// The encryption options is derived from the construction of EncryptConfig object.
|
||||||
// Note: During initial encryption process of a layer, the resultant digest is not known
|
// Note: During initial encryption process of a layer, the resultant digest is not known
|
||||||
@ -410,7 +414,36 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
|
|||||||
return nil, errors.Wrapf(err, "Can not copy signatures to %s", transports.ImageName(c.dest.Reference()))
|
return nil, errors.Wrapf(err, "Can not copy signatures to %s", transports.ImageName(c.dest.Reference()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
canModifyManifestList := (len(sigs) == 0)
|
|
||||||
|
// If the destination is a digested reference, make a note of that, determine what digest value we're
|
||||||
|
// expecting, and check that the source manifest matches it.
|
||||||
|
destIsDigestedReference := false
|
||||||
|
if named := c.dest.Reference().DockerReference(); named != nil {
|
||||||
|
if digested, ok := named.(reference.Digested); ok {
|
||||||
|
destIsDigestedReference = true
|
||||||
|
matches, err := manifest.MatchesDigest(manifestList, digested.Digest())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "computing digest of source image's manifest")
|
||||||
|
}
|
||||||
|
if !matches {
|
||||||
|
return nil, errors.New("Digest of source image's manifest would not match destination reference")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if we're allowed to modify the manifest list.
|
||||||
|
// If we can, set to the empty string. If we can't, set to the reason why.
|
||||||
|
// Compare, and perhaps keep in sync with, the version in copyOneImage.
|
||||||
|
cannotModifyManifestListReason := ""
|
||||||
|
if len(sigs) > 0 {
|
||||||
|
cannotModifyManifestListReason = "Would invalidate signatures"
|
||||||
|
}
|
||||||
|
if destIsDigestedReference {
|
||||||
|
cannotModifyManifestListReason = "Destination specifies a digest"
|
||||||
|
}
|
||||||
|
if options.PreserveDigests {
|
||||||
|
cannotModifyManifestListReason = "Instructed to preserve digests"
|
||||||
|
}
|
||||||
|
|
||||||
// Determine if we'll need to convert the manifest list to a different format.
|
// Determine if we'll need to convert the manifest list to a different format.
|
||||||
forceListMIMEType := options.ForceManifestMIMEType
|
forceListMIMEType := options.ForceManifestMIMEType
|
||||||
@ -425,8 +458,8 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
|
|||||||
return nil, errors.Wrapf(err, "determining manifest list type to write to destination")
|
return nil, errors.Wrapf(err, "determining manifest list type to write to destination")
|
||||||
}
|
}
|
||||||
if selectedListType != originalList.MIMEType() {
|
if selectedListType != originalList.MIMEType() {
|
||||||
if !canModifyManifestList {
|
if cannotModifyManifestListReason != "" {
|
||||||
return nil, errors.Errorf("manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", selectedListType)
|
return nil, errors.Errorf("Manifest list must be converted to type %q to be written to destination, but we cannot modify it: %q", selectedListType, cannotModifyManifestListReason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,8 +543,8 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
|
|||||||
|
|
||||||
// If we can't just use the original value, but we have to change it, flag an error.
|
// If we can't just use the original value, but we have to change it, flag an error.
|
||||||
if !bytes.Equal(attemptedManifestList, originalManifestList) {
|
if !bytes.Equal(attemptedManifestList, originalManifestList) {
|
||||||
if !canModifyManifestList {
|
if cannotModifyManifestListReason != "" {
|
||||||
return nil, errors.Errorf(" manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", thisListType)
|
return nil, errors.Errorf("Manifest list must be converted to type %q to be written to destination, but we cannot modify it: %q", thisListType, cannotModifyManifestListReason)
|
||||||
}
|
}
|
||||||
logrus.Debugf("Manifest list has been updated")
|
logrus.Debugf("Manifest list has been updated")
|
||||||
} else {
|
} else {
|
||||||
@ -629,13 +662,27 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine if we're allowed to modify the manifest.
|
||||||
|
// If we can, set to the empty string. If we can't, set to the reason why.
|
||||||
|
// Compare, and perhaps keep in sync with, the version in copyMultipleImages.
|
||||||
|
cannotModifyManifestReason := ""
|
||||||
|
if len(sigs) > 0 {
|
||||||
|
cannotModifyManifestReason = "Would invalidate signatures"
|
||||||
|
}
|
||||||
|
if destIsDigestedReference {
|
||||||
|
cannotModifyManifestReason = "Destination specifies a digest"
|
||||||
|
}
|
||||||
|
if options.PreserveDigests {
|
||||||
|
cannotModifyManifestReason = "Instructed to preserve digests"
|
||||||
|
}
|
||||||
|
|
||||||
ic := imageCopier{
|
ic := imageCopier{
|
||||||
c: c,
|
c: c,
|
||||||
manifestUpdates: &types.ManifestUpdateOptions{InformationOnly: types.ManifestUpdateInformation{Destination: c.dest}},
|
manifestUpdates: &types.ManifestUpdateOptions{InformationOnly: types.ManifestUpdateInformation{Destination: c.dest}},
|
||||||
src: src,
|
src: src,
|
||||||
// diffIDsAreNeeded is computed later
|
// diffIDsAreNeeded is computed later
|
||||||
canModifyManifest: len(sigs) == 0 && !destIsDigestedReference,
|
cannotModifyManifestReason: cannotModifyManifestReason,
|
||||||
ociEncryptLayers: options.OciEncryptLayers,
|
ociEncryptLayers: options.OciEncryptLayers,
|
||||||
}
|
}
|
||||||
// Ensure _this_ copy sees exactly the intended data when either processing a signed image or signing it.
|
// Ensure _this_ copy sees exactly the intended data when either processing a signed image or signing it.
|
||||||
// This may be too conservative, but for now, better safe than sorry, _especially_ on the SignBy path:
|
// This may be too conservative, but for now, better safe than sorry, _especially_ on the SignBy path:
|
||||||
@ -643,7 +690,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
|
|||||||
// We do intend the RecordDigestUncompressedPair calls to only work with reliable data, but at least there’s a risk
|
// We do intend the RecordDigestUncompressedPair calls to only work with reliable data, but at least there’s a risk
|
||||||
// that the compressed version coming from a third party may be designed to attack some other decompressor implementation,
|
// that the compressed version coming from a third party may be designed to attack some other decompressor implementation,
|
||||||
// and we would reuse and sign it.
|
// and we would reuse and sign it.
|
||||||
ic.canSubstituteBlobs = ic.canModifyManifest && options.SignBy == ""
|
ic.canSubstituteBlobs = ic.cannotModifyManifestReason == "" && options.SignBy == ""
|
||||||
|
|
||||||
if err := ic.updateEmbeddedDockerReference(); err != nil {
|
if err := ic.updateEmbeddedDockerReference(); err != nil {
|
||||||
return nil, "", "", err
|
return nil, "", "", err
|
||||||
@ -710,10 +757,10 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
|
|||||||
}
|
}
|
||||||
// If the original MIME type is acceptable, determineManifestConversion always uses it as preferredManifestMIMEType.
|
// If the original MIME type is acceptable, determineManifestConversion always uses it as preferredManifestMIMEType.
|
||||||
// So if we are here, we will definitely be trying to convert the manifest.
|
// So if we are here, we will definitely be trying to convert the manifest.
|
||||||
// With !ic.canModifyManifest, that would just be a string of repeated failures for the same reason,
|
// With ic.cannotModifyManifestReason != "", that would just be a string of repeated failures for the same reason,
|
||||||
// so let’s bail out early and with a better error message.
|
// so let’s bail out early and with a better error message.
|
||||||
if !ic.canModifyManifest {
|
if ic.cannotModifyManifestReason != "" {
|
||||||
return nil, "", "", errors.Wrap(err, "Writing manifest failed (and converting it is not possible, image is signed or the destination specifies a digest)")
|
return nil, "", "", errors.Wrapf(err, "Writing manifest failed and we cannot try conversions: %q", cannotModifyManifestReason)
|
||||||
}
|
}
|
||||||
|
|
||||||
// errs is a list of errors when trying various manifest types. Also serves as an "upload succeeded" flag when set to nil.
|
// errs is a list of errors when trying various manifest types. Also serves as an "upload succeeded" flag when set to nil.
|
||||||
@ -813,9 +860,9 @@ func (ic *imageCopier) updateEmbeddedDockerReference() error {
|
|||||||
return nil // No reference embedded in the manifest, or it matches destRef already.
|
return nil // No reference embedded in the manifest, or it matches destRef already.
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ic.canModifyManifest {
|
if ic.cannotModifyManifestReason != "" {
|
||||||
return errors.Errorf("Copying a schema1 image with an embedded Docker reference to %s (Docker reference %s) would change the manifest, which is not possible (image is signed or the destination specifies a digest)",
|
return errors.Errorf("Copying a schema1 image with an embedded Docker reference to %s (Docker reference %s) would change the manifest, which we cannot do: %q",
|
||||||
transports.ImageName(ic.c.dest.Reference()), destRef.String())
|
transports.ImageName(ic.c.dest.Reference()), destRef.String(), ic.cannotModifyManifestReason)
|
||||||
}
|
}
|
||||||
ic.manifestUpdates.EmbeddedDockerReference = destRef
|
ic.manifestUpdates.EmbeddedDockerReference = destRef
|
||||||
return nil
|
return nil
|
||||||
@ -833,7 +880,7 @@ func isTTY(w io.Writer) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyLayers copies layers from ic.src/ic.c.rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.canModifyManifest.
|
// copyLayers copies layers from ic.src/ic.c.rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.cannotModifyManifestReason == "".
|
||||||
func (ic *imageCopier) copyLayers(ctx context.Context) error {
|
func (ic *imageCopier) copyLayers(ctx context.Context) error {
|
||||||
srcInfos := ic.src.LayerInfos()
|
srcInfos := ic.src.LayerInfos()
|
||||||
numLayers := len(srcInfos)
|
numLayers := len(srcInfos)
|
||||||
@ -844,8 +891,8 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error {
|
|||||||
srcInfosUpdated := false
|
srcInfosUpdated := false
|
||||||
// If we only need to check authorization, no updates required.
|
// If we only need to check authorization, no updates required.
|
||||||
if updatedSrcInfos != nil && !reflect.DeepEqual(srcInfos, updatedSrcInfos) {
|
if updatedSrcInfos != nil && !reflect.DeepEqual(srcInfos, updatedSrcInfos) {
|
||||||
if !ic.canModifyManifest {
|
if ic.cannotModifyManifestReason != "" {
|
||||||
return errors.Errorf("Copying this image requires changing layer representation, which is not possible (image is signed or the destination specifies a digest)")
|
return errors.Errorf("Copying this image would require changing layer representation, which we cannot do: %q", ic.cannotModifyManifestReason)
|
||||||
}
|
}
|
||||||
srcInfos = updatedSrcInfos
|
srcInfos = updatedSrcInfos
|
||||||
srcInfosUpdated = true
|
srcInfosUpdated = true
|
||||||
@ -975,8 +1022,8 @@ func layerDigestsDiffer(a, b []types.BlobInfo) bool {
|
|||||||
func (ic *imageCopier) copyUpdatedConfigAndManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, digest.Digest, error) {
|
func (ic *imageCopier) copyUpdatedConfigAndManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, digest.Digest, error) {
|
||||||
pendingImage := ic.src
|
pendingImage := ic.src
|
||||||
if !ic.noPendingManifestUpdates() {
|
if !ic.noPendingManifestUpdates() {
|
||||||
if !ic.canModifyManifest {
|
if ic.cannotModifyManifestReason != "" {
|
||||||
return nil, "", errors.Errorf("Internal error: copy needs an updated manifest but that was known to be forbidden")
|
return nil, "", errors.Errorf("Internal error: copy needs an updated manifest but that was known to be forbidden: %q", ic.cannotModifyManifestReason)
|
||||||
}
|
}
|
||||||
if !ic.diffIDsAreNeeded && ic.src.UpdatedImageNeedsLayerDiffIDs(*ic.manifestUpdates) {
|
if !ic.diffIDsAreNeeded && ic.src.UpdatedImageNeedsLayerDiffIDs(*ic.manifestUpdates) {
|
||||||
// We have set ic.diffIDsAreNeeded based on the preferred MIME type returned by determineManifestConversion.
|
// We have set ic.diffIDsAreNeeded based on the preferred MIME type returned by determineManifestConversion.
|
||||||
@ -1359,7 +1406,7 @@ func (ic *imageCopier) copyLayerFromStream(ctx context.Context, srcStream io.Rea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blobInfo, err := ic.c.copyBlobFromStream(ctx, srcStream, srcInfo, getDiffIDRecorder, ic.canModifyManifest, false, toEncrypt, bar, layerIndex, emptyLayer) // Sets err to nil on success
|
blobInfo, err := ic.c.copyBlobFromStream(ctx, srcStream, srcInfo, getDiffIDRecorder, ic.cannotModifyManifestReason == "", false, toEncrypt, bar, layerIndex, emptyLayer) // Sets err to nil on success
|
||||||
return blobInfo, diffIDChan, err
|
return blobInfo, diffIDChan, err
|
||||||
// We need the defer … pipeWriter.CloseWithError() to happen HERE so that the caller can block on reading from diffIDChan
|
// We need the defer … pipeWriter.CloseWithError() to happen HERE so that the caller can block on reading from diffIDChan
|
||||||
}
|
}
|
||||||
|
6
vendor/github.com/containers/image/v5/copy/manifest.go
generated
vendored
6
vendor/github.com/containers/image/v5/copy/manifest.go
generated
vendored
@ -79,10 +79,10 @@ func (ic *imageCopier) determineManifestConversion(ctx context.Context, destSupp
|
|||||||
if _, ok := supportedByDest[srcType]; ok {
|
if _, ok := supportedByDest[srcType]; ok {
|
||||||
prioritizedTypes.append(srcType)
|
prioritizedTypes.append(srcType)
|
||||||
}
|
}
|
||||||
if !ic.canModifyManifest {
|
if ic.cannotModifyManifestReason != "" {
|
||||||
// We could also drop the !ic.canModifyManifest check and have the caller
|
// We could also drop this check and have the caller
|
||||||
// make the choice; it is already doing that to an extent, to improve error
|
// make the choice; it is already doing that to an extent, to improve error
|
||||||
// messages. But it is nice to hide the “if !ic.canModifyManifest, do no conversion”
|
// messages. But it is nice to hide the “if we can't modify, do no conversion”
|
||||||
// special case in here; the caller can then worry (or not) only about a good UI.
|
// special case in here; the caller can then worry (or not) only about a good UI.
|
||||||
logrus.Debugf("We can't modify the manifest, hoping for the best...")
|
logrus.Debugf("We can't modify the manifest, hoping for the best...")
|
||||||
return srcType, []string{}, nil // Take our chances - FIXME? Or should we fail without trying?
|
return srcType, []string{}, nil // Take our chances - FIXME? Or should we fail without trying?
|
||||||
|
5
vendor/github.com/containers/image/v5/types/types.go
generated
vendored
5
vendor/github.com/containers/image/v5/types/types.go
generated
vendored
@ -561,6 +561,11 @@ type SystemContext struct {
|
|||||||
UserShortNameAliasConfPath string
|
UserShortNameAliasConfPath string
|
||||||
// If set, short-name resolution in pkg/shortnames must follow the specified mode
|
// If set, short-name resolution in pkg/shortnames must follow the specified mode
|
||||||
ShortNameMode *ShortNameMode
|
ShortNameMode *ShortNameMode
|
||||||
|
// If set, short names will resolve in pkg/shortnames to docker.io only, and unqualified-search registries and
|
||||||
|
// short-name aliases in registries.conf are ignored. Note that this field is only intended to help enforce
|
||||||
|
// resolving to Docker Hub in the Docker-compatible REST API of Podman; it should never be used outside this
|
||||||
|
// specific context.
|
||||||
|
PodmanOnlyShortNamesIgnoreRegistriesConfAndForceDockerHub bool
|
||||||
// If not "", overrides the default path for the authentication file, but only new format files
|
// If not "", overrides the default path for the authentication file, but only new format files
|
||||||
AuthFilePath string
|
AuthFilePath string
|
||||||
// if not "", overrides the default path for the authentication file, but with the legacy format;
|
// if not "", overrides the default path for the authentication file, but with the legacy format;
|
||||||
|
4
vendor/github.com/containers/image/v5/version/version.go
generated
vendored
4
vendor/github.com/containers/image/v5/version/version.go
generated
vendored
@ -8,10 +8,10 @@ const (
|
|||||||
// VersionMinor is for functionality in a backwards-compatible manner
|
// VersionMinor is for functionality in a backwards-compatible manner
|
||||||
VersionMinor = 17
|
VersionMinor = 17
|
||||||
// VersionPatch is for backwards-compatible bug fixes
|
// VersionPatch is for backwards-compatible bug fixes
|
||||||
VersionPatch = 0
|
VersionPatch = 1
|
||||||
|
|
||||||
// VersionDev indicates development branch. Releases will be empty string.
|
// VersionDev indicates development branch. Releases will be empty string.
|
||||||
VersionDev = ""
|
VersionDev = "-dev"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is the specification version that the package types support.
|
// Version is the specification version that the package types support.
|
||||||
|
18
vendor/github.com/opencontainers/selinux/go-selinux/selinux.go
generated
vendored
18
vendor/github.com/opencontainers/selinux/go-selinux/selinux.go
generated
vendored
@ -61,16 +61,30 @@ func ClassIndex(class string) (int, error) {
|
|||||||
return classIndex(class)
|
return classIndex(class)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFileLabel sets the SELinux label for this path or returns an error.
|
// SetFileLabel sets the SELinux label for this path, following symlinks,
|
||||||
|
// or returns an error.
|
||||||
func SetFileLabel(fpath string, label string) error {
|
func SetFileLabel(fpath string, label string) error {
|
||||||
return setFileLabel(fpath, label)
|
return setFileLabel(fpath, label)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileLabel returns the SELinux label for this path or returns an error.
|
// LsetFileLabel sets the SELinux label for this path, not following symlinks,
|
||||||
|
// or returns an error.
|
||||||
|
func LsetFileLabel(fpath string, label string) error {
|
||||||
|
return lSetFileLabel(fpath, label)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileLabel returns the SELinux label for this path, following symlinks,
|
||||||
|
// or returns an error.
|
||||||
func FileLabel(fpath string) (string, error) {
|
func FileLabel(fpath string) (string, error) {
|
||||||
return fileLabel(fpath)
|
return fileLabel(fpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LfileLabel returns the SELinux label for this path, not following symlinks,
|
||||||
|
// or returns an error.
|
||||||
|
func LfileLabel(fpath string) (string, error) {
|
||||||
|
return lFileLabel(fpath)
|
||||||
|
}
|
||||||
|
|
||||||
// SetFSCreateLabel tells the kernel what label to use for all file system objects
|
// SetFSCreateLabel tells the kernel what label to use for all file system objects
|
||||||
// created by this task.
|
// created by this task.
|
||||||
// Set the label to an empty string to return to the default label. Calls to SetFSCreateLabel
|
// Set the label to an empty string to return to the default label. Calls to SetFSCreateLabel
|
||||||
|
45
vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
generated
vendored
45
vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
generated
vendored
@ -316,8 +316,9 @@ func classIndex(class string) (int, error) {
|
|||||||
return index, nil
|
return index, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setFileLabel sets the SELinux label for this path or returns an error.
|
// lSetFileLabel sets the SELinux label for this path, not following symlinks,
|
||||||
func setFileLabel(fpath string, label string) error {
|
// or returns an error.
|
||||||
|
func lSetFileLabel(fpath string, label string) error {
|
||||||
if fpath == "" {
|
if fpath == "" {
|
||||||
return ErrEmptyPath
|
return ErrEmptyPath
|
||||||
}
|
}
|
||||||
@ -334,12 +335,50 @@ func setFileLabel(fpath string, label string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fileLabel returns the SELinux label for this path or returns an error.
|
// setFileLabel sets the SELinux label for this path, following symlinks,
|
||||||
|
// or returns an error.
|
||||||
|
func setFileLabel(fpath string, label string) error {
|
||||||
|
if fpath == "" {
|
||||||
|
return ErrEmptyPath
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
err := unix.Setxattr(fpath, xattrNameSelinux, []byte(label), 0)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != unix.EINTR { //nolint:errorlint // unix errors are bare
|
||||||
|
return &os.PathError{Op: "setxattr", Path: fpath, Err: err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileLabel returns the SELinux label for this path, following symlinks,
|
||||||
|
// or returns an error.
|
||||||
func fileLabel(fpath string) (string, error) {
|
func fileLabel(fpath string) (string, error) {
|
||||||
if fpath == "" {
|
if fpath == "" {
|
||||||
return "", ErrEmptyPath
|
return "", ErrEmptyPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label, err := getxattr(fpath, xattrNameSelinux)
|
||||||
|
if err != nil {
|
||||||
|
return "", &os.PathError{Op: "getxattr", Path: fpath, Err: err}
|
||||||
|
}
|
||||||
|
// Trim the NUL byte at the end of the byte buffer, if present.
|
||||||
|
if len(label) > 0 && label[len(label)-1] == '\x00' {
|
||||||
|
label = label[:len(label)-1]
|
||||||
|
}
|
||||||
|
return string(label), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// lFileLabel returns the SELinux label for this path, not following symlinks,
|
||||||
|
// or returns an error.
|
||||||
|
func lFileLabel(fpath string) (string, error) {
|
||||||
|
if fpath == "" {
|
||||||
|
return "", ErrEmptyPath
|
||||||
|
}
|
||||||
|
|
||||||
label, err := lgetxattr(fpath, xattrNameSelinux)
|
label, err := lgetxattr(fpath, xattrNameSelinux)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", &os.PathError{Op: "lgetxattr", Path: fpath, Err: err}
|
return "", &os.PathError{Op: "lgetxattr", Path: fpath, Err: err}
|
||||||
|
8
vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go
generated
vendored
8
vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go
generated
vendored
@ -17,10 +17,18 @@ func setFileLabel(fpath string, label string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lSetFileLabel(fpath string, label string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func fileLabel(fpath string) (string, error) {
|
func fileLabel(fpath string) (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lFileLabel(fpath string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
func setFSCreateLabel(label string) error {
|
func setFSCreateLabel(label string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
33
vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go
generated
vendored
33
vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go
generated
vendored
@ -36,3 +36,36 @@ func doLgetxattr(path, attr string, dest []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getxattr returns a []byte slice containing the value of
|
||||||
|
// an extended attribute attr set for path.
|
||||||
|
func getxattr(path, attr string) ([]byte, error) {
|
||||||
|
// Start with a 128 length byte array
|
||||||
|
dest := make([]byte, 128)
|
||||||
|
sz, errno := dogetxattr(path, attr, dest)
|
||||||
|
for errno == unix.ERANGE { //nolint:errorlint // unix errors are bare
|
||||||
|
// Buffer too small, use zero-sized buffer to get the actual size
|
||||||
|
sz, errno = dogetxattr(path, attr, []byte{})
|
||||||
|
if errno != nil {
|
||||||
|
return nil, errno
|
||||||
|
}
|
||||||
|
|
||||||
|
dest = make([]byte, sz)
|
||||||
|
sz, errno = dogetxattr(path, attr, dest)
|
||||||
|
}
|
||||||
|
if errno != nil {
|
||||||
|
return nil, errno
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest[:sz], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dogetxattr is a wrapper that retries on EINTR
|
||||||
|
func dogetxattr(path, attr string, dest []byte) (int, error) {
|
||||||
|
for {
|
||||||
|
sz, err := unix.Getxattr(path, attr, dest)
|
||||||
|
if err != unix.EINTR { //nolint:errorlint // unix errors are bare
|
||||||
|
return sz, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@ -53,7 +53,7 @@ github.com/containers/common/pkg/flag
|
|||||||
github.com/containers/common/pkg/report
|
github.com/containers/common/pkg/report
|
||||||
github.com/containers/common/pkg/report/camelcase
|
github.com/containers/common/pkg/report/camelcase
|
||||||
github.com/containers/common/pkg/retry
|
github.com/containers/common/pkg/retry
|
||||||
# github.com/containers/image/v5 v5.17.0
|
# github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c
|
||||||
github.com/containers/image/v5/copy
|
github.com/containers/image/v5/copy
|
||||||
github.com/containers/image/v5/directory
|
github.com/containers/image/v5/directory
|
||||||
github.com/containers/image/v5/directory/explicitfilepath
|
github.com/containers/image/v5/directory/explicitfilepath
|
||||||
@ -283,7 +283,7 @@ github.com/opencontainers/runc/libcontainer/user
|
|||||||
github.com/opencontainers/runc/libcontainer/userns
|
github.com/opencontainers/runc/libcontainer/userns
|
||||||
# github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
|
# github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
|
||||||
github.com/opencontainers/runtime-spec/specs-go
|
github.com/opencontainers/runtime-spec/specs-go
|
||||||
# github.com/opencontainers/selinux v1.9.1
|
# github.com/opencontainers/selinux v1.10.0
|
||||||
github.com/opencontainers/selinux/go-selinux
|
github.com/opencontainers/selinux/go-selinux
|
||||||
github.com/opencontainers/selinux/go-selinux/label
|
github.com/opencontainers/selinux/go-selinux/label
|
||||||
github.com/opencontainers/selinux/pkg/pwalk
|
github.com/opencontainers/selinux/pkg/pwalk
|
||||||
|
Loading…
Reference in New Issue
Block a user