Merge pull request #3430 from lumjjb/encgcfix

Made fixes and optimizations to encryption GC
This commit is contained in:
Phil Estes 2019-07-22 19:23:47 -04:00 committed by GitHub
commit 49fdb9e1b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 99 deletions

View File

@ -24,12 +24,10 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
imgenc "github.com/containerd/containerd/images/encryption" imgenc "github.com/containerd/containerd/images/encryption"
"github.com/containerd/containerd/leases"
"github.com/containerd/containerd/pkg/encryption" "github.com/containerd/containerd/pkg/encryption"
encconfig "github.com/containerd/containerd/pkg/encryption/config" encconfig "github.com/containerd/containerd/pkg/encryption/config"
encutils "github.com/containerd/containerd/pkg/encryption/utils" encutils "github.com/containerd/containerd/pkg/encryption/utils"
@ -254,17 +252,16 @@ func cryptImage(client *containerd.Client, ctx gocontext.Context, name, newName
newSpec ocispec.Descriptor newSpec ocispec.Descriptor
) )
ls := client.LeasesService() ctx, done, err := client.WithLease(ctx)
l, err := ls.Create(ctx, leases.WithRandomID(), leases.WithExpiration(5*time.Minute))
if err != nil { if err != nil {
return images.Image{}, err return images.Image{}, err
} }
defer ls.Delete(ctx, l, leases.SynchronousDelete) defer done(ctx)
if encrypt { if encrypt {
newSpec, modified, err = imgenc.EncryptImage(ctx, client.ContentStore(), ls, l, image.Target, cc, lf) newSpec, modified, err = imgenc.EncryptImage(ctx, client.ContentStore(), image.Target, cc, lf)
} else { } else {
newSpec, modified, err = imgenc.DecryptImage(ctx, client.ContentStore(), ls, l, image.Target, cc, lf) newSpec, modified, err = imgenc.DecryptImage(ctx, client.ContentStore(), image.Target, cc, lf)
} }
if err != nil { if err != nil {
return image, err return image, err

View File

@ -75,10 +75,10 @@ The current draft of exposed interfaces we believe will be used by consumers are
/* Functions */ /* Functions */
// EncryptImage encrypts an image; it accepts either an OCI descriptor representing a manifest list or a single manifest // EncryptImage encrypts an image; it accepts either an OCI descriptor representing a manifest list or a single manifest
func EncryptImage(ctx context.Context, cs content.Store, ls leases.Manager, l leases.Lease, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf *LayerFilter) (ocispec.Descriptor, bool, error) func EncryptImage(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf *LayerFilter) (ocispec.Descriptor, bool, error)
// DecryptImage decrypts an image; it accepts either an OCI descriptor representing a manifest list or a single manifest // DecryptImage decrypts an image; it accepts either an OCI descriptor representing a manifest list or a single manifest
func DecryptImage(ctx context.Context, cs content.Store, ls leases.Manager, l leases.Lease, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf *LayerFilter) (ocispec.Descriptor, bool, error) func DecryptImage(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf *LayerFilter) (ocispec.Descriptor, bool, error)
// CheckAuthorization checks whether a user has the right keys to be allowed to access an image (every layer) // CheckAuthorization checks whether a user has the right keys to be allowed to access an image (every layer)
// It takes decrypting of the layers only as far as decrypting the asymmetrically encrypted data // It takes decrypting of the layers only as far as decrypting the asymmetrically encrypted data

View File

@ -20,25 +20,23 @@ import (
"context" "context"
"runtime" "runtime"
"testing" "testing"
"time"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
imgenc "github.com/containerd/containerd/images/encryption" imgenc "github.com/containerd/containerd/images/encryption"
"github.com/containerd/containerd/leases"
encconfig "github.com/containerd/containerd/pkg/encryption/config" encconfig "github.com/containerd/containerd/pkg/encryption/config"
"github.com/containerd/containerd/pkg/encryption/utils" "github.com/containerd/containerd/pkg/encryption/utils"
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
) )
func setupBusyboxImage(t *testing.T) { func setupNginxImage(t *testing.T) {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
t.Skip() t.Skip()
} }
const imageName = "docker.io/library/busybox:latest" const imageName = "docker.io/library/nginx:latest"
ctx, cancel := testContext(t) ctx, cancel := testContext(t)
defer cancel() defer cancel()
@ -67,15 +65,15 @@ func setupBusyboxImage(t *testing.T) {
} }
func TestImageEncryption(t *testing.T) { func TestImageEncryption(t *testing.T) {
setupBusyboxImage(t) setupNginxImage(t)
publicKey, privateKey, err := utils.CreateRSATestKey(2048, nil, true) publicKey, privateKey, err := utils.CreateRSATestKey(2048, nil, true)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
const imageName = "docker.io/library/busybox:latest" const imageName = "docker.io/library/nginx:latest"
const encImageName = "docker.io/library/busybox:enc" const encImageName = "docker.io/library/nginx:enc"
ctx, cancel := testContext(t) ctx, cancel := testContext(t)
defer cancel() defer cancel()
@ -86,7 +84,11 @@ func TestImageEncryption(t *testing.T) {
defer client.Close() defer client.Close()
s := client.ImageService() s := client.ImageService()
ls := client.LeasesService() ctx, done, err := client.WithLease(ctx)
if err != nil {
t.Fatal(err)
}
defer done(ctx)
image, err := s.Get(ctx, imageName) image, err := s.Get(ctx, imageName)
if err != nil { if err != nil {
@ -136,14 +138,8 @@ func TestImageEncryption(t *testing.T) {
}, },
} }
l, err := ls.Create(ctx, leases.WithRandomID(), leases.WithExpiration(5*time.Minute))
if err != nil {
t.Fatal("Unable to create lease for encryption")
}
defer ls.Delete(ctx, l, leases.SynchronousDelete)
// Perform encryption of image // Perform encryption of image
encSpec, modified, err := imgenc.EncryptImage(ctx, client.ContentStore(), ls, l, image.Target, cc, lf) encSpec, modified, err := imgenc.EncryptImage(ctx, client.ContentStore(), image.Target, cc, lf)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -159,29 +155,26 @@ func TestImageEncryption(t *testing.T) {
if _, err := s.Create(ctx, image); err != nil { if _, err := s.Create(ctx, image); err != nil {
t.Fatalf("Unable to create image: %v", err) t.Fatalf("Unable to create image: %v", err)
} }
// Force deletion of lease early to check for proper referencing
ls.Delete(ctx, l, leases.SynchronousDelete)
cc = &encconfig.CryptoConfig{ cc = &encconfig.CryptoConfig{
DecryptConfig: &encconfig.DecryptConfig{ DecryptConfig: &encconfig.DecryptConfig{
Parameters: dcparameters, Parameters: dcparameters,
}, },
} }
// Clean up function cancels lease before deleting the image so the images are
// properly deleted
defer func() {
done(ctx)
client.ImageService().Delete(ctx, imageName)
client.ImageService().Delete(ctx, encImageName, images.SynchronousDelete())
}()
// Perform decryption of image // Perform decryption of image
defer client.ImageService().Delete(ctx, imageName, images.SynchronousDelete())
defer client.ImageService().Delete(ctx, encImageName, images.SynchronousDelete())
lf = func(desc ocispec.Descriptor) bool { lf = func(desc ocispec.Descriptor) bool {
return true return true
} }
l, err = ls.Create(ctx, leases.WithRandomID(), leases.WithExpiration(5*time.Minute)) decSpec, modified, err := imgenc.DecryptImage(ctx, client.ContentStore(), encSpec, cc, lf)
if err != nil {
t.Fatal("Unable to create lease for decryption")
}
defer ls.Delete(ctx, l, leases.SynchronousDelete)
decSpec, modified, err := imgenc.DecryptImage(ctx, client.ContentStore(), ls, l, encSpec, cc, lf)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -30,7 +30,6 @@ import (
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/leases"
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go" specs "github.com/opencontainers/image-spec/specs-go"
@ -149,7 +148,7 @@ func decryptLayer(cc *encconfig.CryptoConfig, dataReader content.ReaderAt, desc
} }
// cryptLayer handles the changes due to encryption or decryption of a layer // cryptLayer handles the changes due to encryption or decryption of a layer
func cryptLayer(ctx context.Context, cs content.Store, ls leases.Manager, l leases.Lease, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, cryptoOp cryptoOp) (ocispec.Descriptor, error) { func cryptLayer(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, cryptoOp cryptoOp) (ocispec.Descriptor, error) {
var ( var (
resultReader io.Reader resultReader io.Reader
newDesc ocispec.Descriptor newDesc ocispec.Descriptor
@ -171,33 +170,13 @@ func cryptLayer(ctx context.Context, cs content.Store, ls leases.Manager, l leas
} }
// some operations, such as changing recipients, may not touch the layer at all // some operations, such as changing recipients, may not touch the layer at all
if resultReader != nil { if resultReader != nil {
if ls == nil {
return ocispec.Descriptor{}, errors.New("Unexpected write to object without lease")
}
var rsrc leases.Resource
var ref string var ref string
// If we have the digest, write blob with checks // If we have the digest, write blob with checks
haveDigest := newDesc.Digest.String() != "" haveDigest := newDesc.Digest.String() != ""
if haveDigest { if haveDigest {
ref = fmt.Sprintf("layer-%s", newDesc.Digest.String()) ref = fmt.Sprintf("layer-%s", newDesc.Digest.String())
rsrc = leases.Resource{
ID: newDesc.Digest.String(),
Type: "content",
}
} else { } else {
ref = fmt.Sprintf("blob-%d-%d", rand.Int(), rand.Int()) ref = fmt.Sprintf("blob-%d-%d", rand.Int(), rand.Int())
rsrc = leases.Resource{
ID: ref,
Type: "ingests",
}
}
// Add resource to lease and write blob
if err := ls.AddResource(ctx, l, rsrc); err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "Unable to add resource to lease")
} }
if haveDigest { if haveDigest {
@ -240,7 +219,7 @@ func ingestReader(ctx context.Context, cs content.Ingester, ref string, r io.Rea
} }
// Encrypt or decrypt all the Children of a given descriptor // Encrypt or decrypt all the Children of a given descriptor
func cryptChildren(ctx context.Context, cs content.Store, ls leases.Manager, l leases.Lease, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp, thisPlatform *ocispec.Platform) (ocispec.Descriptor, bool, error) { func cryptChildren(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp, thisPlatform *ocispec.Platform) (ocispec.Descriptor, bool, error) {
children, err := images.Children(ctx, cs, desc) children, err := images.Children(ctx, cs, desc)
if err != nil { if err != nil {
if errdefs.IsNotFound(err) { if errdefs.IsNotFound(err) {
@ -261,7 +240,7 @@ func cryptChildren(ctx context.Context, cs content.Store, ls leases.Manager, l l
case images.MediaTypeDockerSchema2LayerGzip, images.MediaTypeDockerSchema2Layer, case images.MediaTypeDockerSchema2LayerGzip, images.MediaTypeDockerSchema2Layer,
ocispec.MediaTypeImageLayerGzip, ocispec.MediaTypeImageLayer: ocispec.MediaTypeImageLayerGzip, ocispec.MediaTypeImageLayer:
if cryptoOp == cryptoOpEncrypt && lf(child) { if cryptoOp == cryptoOpEncrypt && lf(child) {
nl, err := cryptLayer(ctx, cs, ls, l, child, cc, cryptoOp) nl, err := cryptLayer(ctx, cs, child, cc, cryptoOp)
if err != nil { if err != nil {
return ocispec.Descriptor{}, false, err return ocispec.Descriptor{}, false, err
} }
@ -273,7 +252,7 @@ func cryptChildren(ctx context.Context, cs content.Store, ls leases.Manager, l l
case images.MediaTypeDockerSchema2LayerGzipEnc, images.MediaTypeDockerSchema2LayerEnc: case images.MediaTypeDockerSchema2LayerGzipEnc, images.MediaTypeDockerSchema2LayerEnc:
// this one can be decrypted but also its recipients list changed // this one can be decrypted but also its recipients list changed
if lf(child) { if lf(child) {
nl, err := cryptLayer(ctx, cs, ls, l, child, cc, cryptoOp) nl, err := cryptLayer(ctx, cs, child, cc, cryptoOp)
if err != nil || cryptoOp == cryptoOpUnwrapOnly { if err != nil || cryptoOp == cryptoOpUnwrapOnly {
return ocispec.Descriptor{}, false, err return ocispec.Descriptor{}, false, err
} }
@ -319,19 +298,6 @@ func cryptChildren(ctx context.Context, cs content.Store, ls leases.Manager, l l
ref := fmt.Sprintf("manifest-%s", newDesc.Digest.String()) ref := fmt.Sprintf("manifest-%s", newDesc.Digest.String())
if ls == nil {
return ocispec.Descriptor{}, false, errors.New("Unexpected write to object without lease")
}
rsrc := leases.Resource{
ID: desc.Digest.String(),
Type: "content",
}
if err := ls.AddResource(ctx, l, rsrc); err != nil {
return ocispec.Descriptor{}, false, errors.Wrap(err, "Unable to add resource to lease")
}
if err := content.WriteBlob(ctx, cs, ref, bytes.NewReader(mb), newDesc, content.WithLabels(labels)); err != nil { if err := content.WriteBlob(ctx, cs, ref, bytes.NewReader(mb), newDesc, content.WithLabels(labels)); err != nil {
return ocispec.Descriptor{}, false, errors.Wrap(err, "failed to write config") return ocispec.Descriptor{}, false, errors.Wrap(err, "failed to write config")
} }
@ -342,7 +308,7 @@ func cryptChildren(ctx context.Context, cs content.Store, ls leases.Manager, l l
} }
// cryptManifest encrypts or decrypts the children of a top level manifest // cryptManifest encrypts or decrypts the children of a top level manifest
func cryptManifest(ctx context.Context, cs content.Store, ls leases.Manager, l leases.Lease, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp) (ocispec.Descriptor, bool, error) { func cryptManifest(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp) (ocispec.Descriptor, bool, error) {
p, err := content.ReadBlob(ctx, cs, desc) p, err := content.ReadBlob(ctx, cs, desc)
if err != nil { if err != nil {
return ocispec.Descriptor{}, false, err return ocispec.Descriptor{}, false, err
@ -352,7 +318,7 @@ func cryptManifest(ctx context.Context, cs content.Store, ls leases.Manager, l l
return ocispec.Descriptor{}, false, err return ocispec.Descriptor{}, false, err
} }
platform := platforms.DefaultSpec() platform := platforms.DefaultSpec()
newDesc, modified, err := cryptChildren(ctx, cs, ls, l, desc, cc, lf, cryptoOp, &platform) newDesc, modified, err := cryptChildren(ctx, cs, desc, cc, lf, cryptoOp, &platform)
if err != nil || cryptoOp == cryptoOpUnwrapOnly { if err != nil || cryptoOp == cryptoOpUnwrapOnly {
return ocispec.Descriptor{}, false, err return ocispec.Descriptor{}, false, err
} }
@ -360,7 +326,7 @@ func cryptManifest(ctx context.Context, cs content.Store, ls leases.Manager, l l
} }
// cryptManifestList encrypts or decrypts the children of a top level manifest list // cryptManifestList encrypts or decrypts the children of a top level manifest list
func cryptManifestList(ctx context.Context, cs content.Store, ls leases.Manager, l leases.Lease, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp) (ocispec.Descriptor, bool, error) { func cryptManifestList(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp) (ocispec.Descriptor, bool, error) {
// read the index; if any layer is encrypted and any manifests change we will need to rewrite it // read the index; if any layer is encrypted and any manifests change we will need to rewrite it
b, err := content.ReadBlob(ctx, cs, desc) b, err := content.ReadBlob(ctx, cs, desc)
if err != nil { if err != nil {
@ -375,7 +341,7 @@ func cryptManifestList(ctx context.Context, cs content.Store, ls leases.Manager,
var newManifests []ocispec.Descriptor var newManifests []ocispec.Descriptor
modified := false modified := false
for _, manifest := range index.Manifests { for _, manifest := range index.Manifests {
newManifest, m, err := cryptChildren(ctx, cs, ls, l, manifest, cc, lf, cryptoOp, manifest.Platform) newManifest, m, err := cryptChildren(ctx, cs, manifest, cc, lf, cryptoOp, manifest.Platform)
if err != nil || cryptoOp == cryptoOpUnwrapOnly { if err != nil || cryptoOp == cryptoOpUnwrapOnly {
return ocispec.Descriptor{}, false, err return ocispec.Descriptor{}, false, err
} }
@ -410,19 +376,6 @@ func cryptManifestList(ctx context.Context, cs content.Store, ls leases.Manager,
ref := fmt.Sprintf("index-%s", newDesc.Digest.String()) ref := fmt.Sprintf("index-%s", newDesc.Digest.String())
if ls == nil {
return ocispec.Descriptor{}, false, errors.New("Unexpected write to object without lease")
}
rsrc := leases.Resource{
ID: desc.Digest.String(),
Type: "content",
}
if err := ls.AddResource(ctx, l, rsrc); err != nil {
return ocispec.Descriptor{}, false, errors.Wrap(err, "Unable to add resource to lease")
}
if err = content.WriteBlob(ctx, cs, ref, bytes.NewReader(mb), newDesc, content.WithLabels(labels)); err != nil { if err = content.WriteBlob(ctx, cs, ref, bytes.NewReader(mb), newDesc, content.WithLabels(labels)); err != nil {
return ocispec.Descriptor{}, false, errors.Wrap(err, "failed to write index") return ocispec.Descriptor{}, false, errors.Wrap(err, "failed to write index")
} }
@ -434,28 +387,28 @@ func cryptManifestList(ctx context.Context, cs content.Store, ls leases.Manager,
// cryptImage is the dispatcher to encrypt/decrypt an image; it accepts either an OCI descriptor // cryptImage is the dispatcher to encrypt/decrypt an image; it accepts either an OCI descriptor
// representing a manifest list or a single manifest // representing a manifest list or a single manifest
func cryptImage(ctx context.Context, cs content.Store, ls leases.Manager, l leases.Lease, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp) (ocispec.Descriptor, bool, error) { func cryptImage(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp) (ocispec.Descriptor, bool, error) {
if cc == nil { if cc == nil {
return ocispec.Descriptor{}, false, errors.Wrapf(errdefs.ErrInvalidArgument, "CryptoConfig must not be nil") return ocispec.Descriptor{}, false, errors.Wrapf(errdefs.ErrInvalidArgument, "CryptoConfig must not be nil")
} }
switch desc.MediaType { switch desc.MediaType {
case ocispec.MediaTypeImageIndex, images.MediaTypeDockerSchema2ManifestList: case ocispec.MediaTypeImageIndex, images.MediaTypeDockerSchema2ManifestList:
return cryptManifestList(ctx, cs, ls, l, desc, cc, lf, cryptoOp) return cryptManifestList(ctx, cs, desc, cc, lf, cryptoOp)
case ocispec.MediaTypeImageManifest, images.MediaTypeDockerSchema2Manifest: case ocispec.MediaTypeImageManifest, images.MediaTypeDockerSchema2Manifest:
return cryptManifest(ctx, cs, ls, l, desc, cc, lf, cryptoOp) return cryptManifest(ctx, cs, desc, cc, lf, cryptoOp)
default: default:
return ocispec.Descriptor{}, false, errors.Errorf("CryptImage: Unhandled media type: %s", desc.MediaType) return ocispec.Descriptor{}, false, errors.Errorf("CryptImage: Unhandled media type: %s", desc.MediaType)
} }
} }
// EncryptImage encrypts an image; it accepts either an OCI descriptor representing a manifest list or a single manifest // EncryptImage encrypts an image; it accepts either an OCI descriptor representing a manifest list or a single manifest
func EncryptImage(ctx context.Context, cs content.Store, ls leases.Manager, l leases.Lease, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter) (ocispec.Descriptor, bool, error) { func EncryptImage(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter) (ocispec.Descriptor, bool, error) {
return cryptImage(ctx, cs, ls, l, desc, cc, lf, cryptoOpEncrypt) return cryptImage(ctx, cs, desc, cc, lf, cryptoOpEncrypt)
} }
// DecryptImage decrypts an image; it accepts either an OCI descriptor representing a manifest list or a single manifest // DecryptImage decrypts an image; it accepts either an OCI descriptor representing a manifest list or a single manifest
func DecryptImage(ctx context.Context, cs content.Store, ls leases.Manager, l leases.Lease, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter) (ocispec.Descriptor, bool, error) { func DecryptImage(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter) (ocispec.Descriptor, bool, error) {
return cryptImage(ctx, cs, ls, l, desc, cc, lf, cryptoOpDecrypt) return cryptImage(ctx, cs, desc, cc, lf, cryptoOpDecrypt)
} }
// CheckAuthorization checks whether a user has the right keys to be allowed to access an image (every layer) // CheckAuthorization checks whether a user has the right keys to be allowed to access an image (every layer)
@ -465,11 +418,12 @@ func CheckAuthorization(ctx context.Context, cs content.Store, desc ocispec.Desc
cc := encconfig.CryptoConfig{ cc := encconfig.CryptoConfig{
DecryptConfig: dc, DecryptConfig: dc,
} }
lf := func(desc ocispec.Descriptor) bool { lf := func(desc ocispec.Descriptor) bool {
return true return true
} }
// We shouldn't need to create any objects in CheckAuthorization, so no lease required.
_, _, err := cryptImage(ctx, cs, nil, leases.Lease{}, desc, &cc, lf, cryptoOpUnwrapOnly) _, _, err := cryptImage(ctx, cs, desc, &cc, lf, cryptoOpUnwrapOnly)
if err != nil { if err != nil {
return errors.Wrapf(err, "you are not authorized to use this image") return errors.Wrapf(err, "you are not authorized to use this image")
} }