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  }