go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/discovery/container_registry/resolver.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package container_registry 5 6 import ( 7 "context" 8 "errors" 9 10 "github.com/google/go-containerregistry/pkg/authn" 11 "github.com/google/go-containerregistry/pkg/name" 12 "github.com/google/go-containerregistry/pkg/v1/remote" 13 "github.com/rs/zerolog/log" 14 "go.mondoo.com/cnquery/logger" 15 "go.mondoo.com/cnquery/providers-sdk/v1/inventory" 16 "go.mondoo.com/cnquery/providers-sdk/v1/vault" 17 ) 18 19 type Resolver struct { 20 // NoStrictValidation deactivates the strict validation for container registry resolutions 21 // cr://index.docker.io/mondoo/client would be converted index.docker.io/mondoo/client:latest 22 // It is not the default behavior but is used by the docker resolver to resolve images 23 NoStrictValidation bool 24 } 25 26 func (r *Resolver) Name() string { 27 return "Container Registry Discover" 28 } 29 30 func (r *Resolver) AvailableDiscoveryTargets() []string { 31 return []string{"auto", "all"} 32 } 33 34 // func (r *Resolver) Resolve(ctx context.Context, root *inventory.Asset, conf *inventory.Config, credsResolver vault.Resolver, sfn common.QuerySecretFn, userIdDetectors ...providers.PlatformIdDetector) ([]*inventory.Asset, error) { 35 func (r *Resolver) Resolve(ctx context.Context, root *inventory.Asset, conf *inventory.Config, credsResolver vault.Resolver) ([]*inventory.Asset, error) { 36 resolved := []*inventory.Asset{} 37 38 imageFetcher := NewContainerRegistryResolver() 39 // to support self-signed certs 40 imageFetcher.Insecure = conf.Insecure 41 42 // check if the reference is an image 43 // NOTE: we use strict validation here otherwise urls like cr://index.docker.io/mondoo/client are converted 44 // to index.docker.io/mondoo/client:latest 45 opts := name.StrictValidation 46 if r.NoStrictValidation { 47 opts = name.WeakValidation 48 } 49 50 ref, err := name.ParseReference(conf.Host, opts) 51 if err == nil { 52 log.Debug().Str("image", conf.Host).Msg("detected container image in container registry") 53 54 remoteOpts := AuthOption(conf.Credentials, credsResolver) 55 // we need to disable default keychain auth if an auth method was found 56 if len(remoteOpts) > 0 { 57 imageFetcher.DisableKeychainAuth = true 58 } 59 60 a, err := imageFetcher.GetImage(ref, conf.Credentials, remoteOpts...) 61 if err != nil { 62 return nil, err 63 } 64 // keep already set options, i.e. image paths 65 if conf.Options != nil && a.Connections[0].Options == nil { 66 a.Connections[0].Options = conf.Options 67 } 68 69 if conf.Insecure { 70 for i := range a.Connections { 71 c := a.Connections[i] 72 c.Insecure = conf.Insecure 73 c.Credentials = conf.Credentials 74 } 75 } 76 77 return []*inventory.Asset{a}, nil 78 } 79 80 // okay, no image, lets check the repository 81 repository := conf.Host 82 log.Info().Str("registry", repository).Msg("fetch meta information from container registry") 83 84 assetList, err := imageFetcher.ListImages(repository) 85 if err != nil { 86 log.Error().Err(err).Msg("could not fetch container images") 87 return nil, err 88 } 89 90 for i := range assetList { 91 a := assetList[i] 92 log.Info().Str("name", a.Name).Str("image", a.Connections[0].Host+assetList[i].Connections[0].Path).Msg("resolved image") 93 94 if conf.Insecure { 95 for i := range a.Connections { 96 c := a.Connections[i] 97 c.Insecure = conf.Insecure 98 } 99 } 100 resolved = append(resolved, a) 101 } 102 103 if len(resolved) == 0 { 104 return nil, errors.New("could not find repository:" + repository) 105 } 106 107 return resolved, nil 108 } 109 110 func AuthOption(credentials []*vault.Credential, credsResolver vault.Resolver) []remote.Option { 111 remoteOpts := []remote.Option{} 112 for i := range credentials { 113 cred := credentials[i] 114 115 // NOTE: normally the motor connection is resolving the credentials but here we need the credential earlier 116 // we probably want to write some mql resources to support the query of registries itself 117 resolvedCredential, err := credsResolver.GetCredential(cred) 118 if err != nil { 119 log.Warn().Err(err).Msg("could not resolve credential") 120 } 121 switch resolvedCredential.Type { 122 case vault.CredentialType_password: 123 log.Debug().Msg("add password authentication") 124 cfg := authn.AuthConfig{ 125 Username: resolvedCredential.User, 126 Password: string(resolvedCredential.Secret), 127 } 128 remoteOpts = append(remoteOpts, remote.WithAuth(authn.FromConfig(cfg))) 129 case vault.CredentialType_bearer: 130 log.Debug().Str("token", string(resolvedCredential.Secret)).Msg("add bearer authentication") 131 cfg := authn.AuthConfig{ 132 Username: resolvedCredential.User, 133 RegistryToken: string(resolvedCredential.Secret), 134 } 135 remoteOpts = append(remoteOpts, remote.WithAuth(authn.FromConfig(cfg))) 136 default: 137 log.Warn().Msg("unknown credentials for container image") 138 logger.DebugJSON(credentials) 139 } 140 } 141 return remoteOpts 142 }