go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/connection/container/image/registry.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package image 5 6 import ( 7 "crypto/tls" 8 "fmt" 9 "io" 10 "net" 11 "net/http" 12 "strings" 13 "time" 14 15 ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login" 16 "github.com/google/go-containerregistry/pkg/authn" 17 "github.com/google/go-containerregistry/pkg/name" 18 v1 "github.com/google/go-containerregistry/pkg/v1" 19 "github.com/google/go-containerregistry/pkg/v1/remote" 20 ) 21 22 // Option is a functional option 23 // see https://www.sohamkamani.com/golang/options-pattern/ 24 type Option func(*options) error 25 26 type options struct { 27 insecure bool 28 auth authn.Authenticator 29 } 30 31 func WithInsecure(insecure bool) Option { 32 return func(o *options) error { 33 o.insecure = insecure 34 return nil 35 } 36 } 37 38 func WithAuthenticator(auth authn.Authenticator) Option { 39 return func(o *options) error { 40 o.auth = auth 41 return nil 42 } 43 } 44 45 func GetImageDescriptor(ref name.Reference, opts ...Option) (*remote.Descriptor, error) { 46 o := &options{ 47 insecure: false, 48 } 49 50 for _, option := range opts { 51 if err := option(o); err != nil { 52 return nil, err 53 } 54 } 55 56 if o.auth == nil { 57 kc := authn.NewMultiKeychain( 58 authn.DefaultKeychain, 59 ) 60 if strings.Contains(ref.Name(), ".ecr.") { 61 kc = authn.NewMultiKeychain( 62 authn.DefaultKeychain, 63 authn.NewKeychainFromHelper(ecr.NewECRHelper()), 64 ) 65 } 66 auth, err := kc.Resolve(ref.Context()) 67 if err != nil { 68 fmt.Printf("getting creds for %q: %v", ref, err) 69 return nil, err 70 } 71 o.auth = auth 72 } 73 74 return remote.Get(ref, remote.WithAuth(o.auth)) 75 } 76 77 func LoadImageFromRegistry(ref name.Reference, opts ...Option) (v1.Image, io.ReadCloser, error) { 78 o := &options{ 79 insecure: false, 80 } 81 82 for _, option := range opts { 83 if err := option(o); err != nil { 84 return nil, nil, err 85 } 86 } 87 88 if o.auth == nil { 89 kc := authn.NewMultiKeychain( 90 authn.DefaultKeychain, 91 ) 92 if strings.Contains(ref.Name(), ".ecr.") { 93 kc = authn.NewMultiKeychain( 94 authn.DefaultKeychain, 95 authn.NewKeychainFromHelper(ecr.NewECRHelper()), 96 ) 97 } 98 auth, err := kc.Resolve(ref.Context()) 99 if err != nil { 100 fmt.Printf("getting creds for %q: %v", ref, err) 101 return nil, nil, err 102 } 103 o.auth = auth 104 } 105 106 // mimic http.DefaultTransport 107 tr := &http.Transport{ 108 Proxy: http.ProxyFromEnvironment, 109 DialContext: (&net.Dialer{ 110 Timeout: 30 * time.Second, 111 KeepAlive: 30 * time.Second, 112 DualStack: true, 113 }).DialContext, 114 ForceAttemptHTTP2: true, 115 MaxIdleConns: 100, 116 IdleConnTimeout: 90 * time.Second, 117 TLSHandshakeTimeout: 10 * time.Second, 118 ExpectContinueTimeout: 1 * time.Second, 119 } 120 121 if o.insecure { 122 tr.TLSClientConfig = &tls.Config{ 123 InsecureSkipVerify: true, 124 } 125 } 126 127 img, err := remote.Image(ref, remote.WithAuth(o.auth), remote.WithTransport(tr)) 128 if err != nil { 129 return nil, nil, err 130 } 131 132 // write image to disk (conmpressed, unflattened) 133 // Otherwise we can not later recognize it as a valid image 134 f, err := writeCompressedTarImage(img, ref.String()) 135 if err != nil { 136 return nil, nil, err 137 } 138 139 return img, f, nil 140 }