github.com/containerd/nerdctl/v2@v2.0.0-beta.5.0.20240520001846-b5758f54fa28/pkg/cmd/image/crypt.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package image 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 24 "github.com/containerd/containerd" 25 "github.com/containerd/containerd/content" 26 "github.com/containerd/containerd/images/converter" 27 "github.com/containerd/imgcrypt/images/encryption" 28 "github.com/containerd/imgcrypt/images/encryption/parsehelpers" 29 "github.com/containerd/nerdctl/v2/pkg/api/types" 30 "github.com/containerd/nerdctl/v2/pkg/platformutil" 31 "github.com/containerd/nerdctl/v2/pkg/referenceutil" 32 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 33 ) 34 35 func Crypt(ctx context.Context, client *containerd.Client, srcRawRef, targetRawRef string, encrypt bool, options types.ImageCryptOptions) error { 36 var convertOpts = []converter.Opt{} 37 if srcRawRef == "" || targetRawRef == "" { 38 return errors.New("src and target image need to be specified") 39 } 40 41 srcNamed, err := referenceutil.ParseAny(srcRawRef) 42 if err != nil { 43 return err 44 } 45 srcRef := srcNamed.String() 46 47 targetNamed, err := referenceutil.ParseDockerRef(targetRawRef) 48 if err != nil { 49 return err 50 } 51 targetRef := targetNamed.String() 52 53 platMC, err := platformutil.NewMatchComparer(options.AllPlatforms, options.Platforms) 54 if err != nil { 55 return err 56 } 57 convertOpts = append(convertOpts, converter.WithPlatform(platMC)) 58 59 imgcryptFlags, err := parseImgcryptFlags(options, encrypt) 60 if err != nil { 61 return err 62 } 63 64 srcImg, err := client.ImageService().Get(ctx, srcRef) 65 if err != nil { 66 return err 67 } 68 layerDescs, err := platformutil.LayerDescs(ctx, client.ContentStore(), srcImg.Target, platMC) 69 if err != nil { 70 return err 71 } 72 layerFilter := func(desc ocispec.Descriptor) bool { 73 return true 74 } 75 var convertFunc converter.ConvertFunc 76 if encrypt { 77 cc, err := parsehelpers.CreateCryptoConfig(imgcryptFlags, layerDescs) 78 if err != nil { 79 return err 80 } 81 convertFunc = encryption.GetImageEncryptConverter(&cc, layerFilter) 82 } else { 83 cc, err := parsehelpers.CreateDecryptCryptoConfig(imgcryptFlags, layerDescs) 84 if err != nil { 85 return err 86 } 87 convertFunc = encryption.GetImageDecryptConverter(&cc, layerFilter) 88 } 89 // we have to compose the DefaultIndexConvertFunc here to match platforms. 90 convertFunc = composeConvertFunc(converter.DefaultIndexConvertFunc(nil, false, platMC), convertFunc) 91 convertOpts = append(convertOpts, converter.WithIndexConvertFunc(convertFunc)) 92 93 // converter.Convert() gains the lease by itself 94 newImg, err := converter.Convert(ctx, client, targetRef, srcRef, convertOpts...) 95 if err != nil { 96 return err 97 } 98 fmt.Fprintln(options.Stdout, newImg.Target.Digest.String()) 99 return nil 100 } 101 102 // parseImgcryptFlags corresponds to https://github.com/containerd/imgcrypt/blob/v1.1.2/cmd/ctr/commands/images/crypt_utils.go#L244-L252 103 func parseImgcryptFlags(options types.ImageCryptOptions, encrypt bool) (parsehelpers.EncArgs, error) { 104 var a parsehelpers.EncArgs 105 106 a.GPGHomedir = options.GpgHomeDir 107 a.GPGVersion = options.GpgVersion 108 a.Key = options.Keys 109 if encrypt { 110 a.Recipient = options.Recipients 111 if len(a.Recipient) == 0 { 112 return a, errors.New("at least one recipient must be specified (e.g., --recipient=jwe:mypubkey.pem)") 113 } 114 } 115 // While --recipient can be specified only for `nerdctl image encrypt`, 116 // --dec-recipient can be specified for both `nerdctl image encrypt` and `nerdctl image decrypt`. 117 a.DecRecipient = options.DecRecipients 118 return a, nil 119 } 120 121 func composeConvertFunc(a, b converter.ConvertFunc) converter.ConvertFunc { 122 return func(ctx context.Context, cs content.Store, desc ocispec.Descriptor) (*ocispec.Descriptor, error) { 123 newDesc, err := a(ctx, cs, desc) 124 if err != nil { 125 return newDesc, err 126 } 127 if newDesc == nil { 128 return b(ctx, cs, desc) 129 } 130 return b(ctx, cs, *newDesc) 131 } 132 }