go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers-sdk/v1/inventory/manager/manager.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package manager 5 6 import ( 7 "context" 8 9 "github.com/rs/zerolog/log" 10 "go.mondoo.com/cnquery/llx" 11 "go.mondoo.com/cnquery/logger" 12 "go.mondoo.com/cnquery/providers-sdk/v1/inventory" 13 "go.mondoo.com/cnquery/providers-sdk/v1/vault" 14 "go.mondoo.com/cnquery/providers-sdk/v1/vault/config" 15 "go.mondoo.com/cnquery/providers-sdk/v1/vault/credentials_resolver" 16 "go.mondoo.com/cnquery/providers-sdk/v1/vault/inmemory" 17 "go.mondoo.com/cnquery/providers-sdk/v1/vault/multivault" 18 protobuf "google.golang.org/protobuf/proto" 19 ) 20 21 var _ InventoryManager = (*inventoryManager)(nil) 22 23 type InventoryManager interface { 24 // GetAssets returns all assets under management 25 GetAssets() []*inventory.Asset 26 // GetRelatedAssets returns a list of assets related to those under management 27 GetRelatedAssets() []*inventory.Asset 28 // Resolve will iterate over all assets and try to discover all nested assets. After this operation 29 // GetAssets will return the fully resolved list of assets 30 Resolve(ctx context.Context) map[*inventory.Asset]error 31 // GetCredential returns a full credential including the secret from vault 32 GetCredential(*vault.Credential) (*vault.Credential, error) 33 // QuerySecretId runs the credential query to determine the secret id for an asset, the resulting credential 34 // only returns a secret id 35 QuerySecretId(a *inventory.Asset) (*vault.Credential, error) 36 // GetVault returns the configured Vault 37 GetVault() vault.Vault 38 GetCredsResolver() vault.Resolver 39 } 40 41 type imOption func(*inventoryManager) error 42 43 // passes a pre-parsed asset inventory into the Inventory Manager 44 func WithInventory(inventory *inventory.Inventory, runtime llx.Runtime) imOption { 45 return func(im *inventoryManager) error { 46 logger.DebugDumpJSON("inventory-unresolved", inventory) 47 return im.loadInventory(inventory, runtime) 48 } 49 } 50 51 func WithCredentialQuery(query string, runtime llx.Runtime) imOption { 52 return func(im *inventoryManager) error { 53 return im.SetCredentialQuery(query, runtime) 54 } 55 } 56 57 func WithVault(v vault.Vault) imOption { 58 return func(im *inventoryManager) error { 59 im.vault = v 60 return nil 61 } 62 } 63 64 func WithCachedCredsResolver() imOption { 65 return func(im *inventoryManager) error { 66 im.isCached = true 67 return nil 68 } 69 } 70 71 func NewManager(opts ...imOption) (*inventoryManager, error) { 72 im := &inventoryManager{ 73 assetList: []*inventory.Asset{}, 74 } 75 76 for _, option := range opts { 77 if err := option(im); err != nil { 78 return nil, err 79 } 80 } 81 im.resetVault() 82 83 return im, nil 84 } 85 86 type inventoryManager struct { 87 isCached bool 88 assetList []*inventory.Asset 89 relatedAssets []*inventory.Asset 90 // optional vault set by user 91 vault vault.Vault 92 // internal vault used to store embedded credentials 93 inmemoryVault vault.Vault 94 // wrapper vault to access the credentials 95 accessVault vault.Vault 96 credentialQueryRunner *CredentialQueryRunner 97 } 98 99 // TODO: define what happens when we call load multiple times? 100 func (im *inventoryManager) loadInventory(inventory *inventory.Inventory, runtime llx.Runtime) error { 101 err := inventory.PreProcess() 102 if err != nil { 103 return err 104 } 105 106 // all assets are copied 107 im.assetList = append(im.assetList, inventory.Spec.Assets...) 108 109 // palace all credentials in an in-memory vault 110 secrets := map[string]*vault.Secret{} 111 for i := range inventory.Spec.Credentials { 112 cred := inventory.Spec.Credentials[i] 113 114 secret, err := vault.NewSecret(cred, vault.SecretEncoding_encoding_proto) 115 if err != nil { 116 return err 117 } 118 119 secrets[secret.Key] = secret 120 } 121 122 if inventory.Spec.CredentialQuery != "" { 123 err = im.SetCredentialQuery(inventory.Spec.CredentialQuery, runtime) 124 if err != nil { 125 return err 126 } 127 } 128 129 // in-memory vault is used as fall-back store embedded credentials 130 im.inmemoryVault = inmemory.New(inmemory.WithSecretMap(secrets)) 131 if inventory.Spec.Vault != nil { 132 var v vault.Vault 133 // when the type is not provided but a name was given, then look up in our internal vault configuration 134 if inventory.Spec.Vault.Name != "" && inventory.Spec.Vault.Type == vault.VaultType_None { 135 v, err = config.GetConfiguredVault(inventory.Spec.Vault.Name) 136 if err != nil { 137 return err 138 } 139 } else { 140 t, err := vault.NewVaultType(inventory.Spec.Vault.Type.String()) 141 if err != nil { 142 return err 143 } 144 145 // instantiate with full vault config 146 v, err = config.New(&vault.VaultConfiguration{ 147 Name: inventory.Spec.Vault.Name, 148 Type: t, 149 Options: inventory.Spec.Vault.Options, 150 }) 151 if err != nil { 152 return err 153 } 154 } 155 im.vault = v 156 } 157 158 // determine the vault to use for accessing credentials 159 im.resetVault() 160 161 return nil 162 } 163 164 func (im *inventoryManager) SetCredentialQuery(query string, runtime llx.Runtime) error { 165 qr, err := NewCredentialQueryRunner(query, runtime) 166 if err != nil { 167 return err 168 } 169 im.credentialQueryRunner = qr 170 return nil 171 } 172 173 func (im *inventoryManager) GetAssets() []*inventory.Asset { 174 // TODO: do we need additional work to make this thread-safe 175 return im.assetList 176 } 177 178 func (im *inventoryManager) GetRelatedAssets() []*inventory.Asset { 179 return im.relatedAssets 180 } 181 182 // QuerySecretId provides an input and determines the credential information for an asset 183 // The credential will only include the reference to the secret and not include the actual secret 184 func (im *inventoryManager) QuerySecretId(a *inventory.Asset) (*vault.Credential, error) { 185 if im.credentialQueryRunner == nil { 186 log.Debug().Msg("no credential query set") 187 return nil, nil 188 } 189 190 // this is where we get the vault configuration query and evaluate it against the asset data 191 // if vault and secret function is set, run the additional handling 192 return im.credentialQueryRunner.Run(a) 193 } 194 195 func (im *inventoryManager) Resolve(ctx context.Context) map[*inventory.Asset]error { 196 // resolvedAssets := discovery.ResolveAssets(ctx, im.assetList, im.GetCredsResolver(), im.QuerySecretId) 197 panic("NEED TO RESOLVE") 198 199 // // TODO: iterate over all resolved assets and match them with the original list and try to find credentials for each asset 200 // im.assetList = resolvedAssets.Assets 201 // im.relatedAssets = resolvedAssets.RelatedAssets 202 203 // log.Info().Int("resolved-assets", len(im.assetList)).Msg("resolved assets") 204 // logger.DebugDumpJSON("inventory-resolved-assets", im.assetList) 205 // return resolvedAssets.Errors 206 return nil 207 } 208 209 func (im *inventoryManager) ResolveAsset(inventoryAsset *inventory.Asset) (*inventory.Asset, error) { 210 creds := im.GetCredsResolver() 211 212 // we clone the asset to make sure we do not modify the original asset 213 clonedAsset := protobuf.Clone(inventoryAsset).(*inventory.Asset) 214 215 for j := range clonedAsset.Connections { 216 conn := clonedAsset.Connections[j] 217 for k := range conn.Credentials { 218 credential := conn.Credentials[k] 219 if credential.SecretId == "" { 220 continue 221 } 222 223 resolvedCredential, err := creds.GetCredential(credential) 224 if err != nil { 225 log.Debug().Str("secret-id", credential.SecretId).Err(err).Msg("could not fetch secret for motor connection") 226 return nil, err 227 } 228 229 conn.Credentials[k] = resolvedCredential 230 } 231 } 232 233 return clonedAsset, nil 234 } 235 236 func (im *inventoryManager) GetCredsResolver() vault.Resolver { 237 return credentials_resolver.New(im.accessVault, im.isCached) 238 } 239 240 func (im *inventoryManager) GetCredential(cred *vault.Credential) (*vault.Credential, error) { 241 return im.GetCredsResolver().GetCredential(cred) 242 } 243 244 func (im *inventoryManager) resetVault() { 245 if im.vault != nil && im.inmemoryVault != nil { 246 im.accessVault = multivault.New(im.vault, im.inmemoryVault) 247 } else if im.vault != nil { 248 im.accessVault = im.vault 249 } else if im.inmemoryVault != nil { 250 im.accessVault = im.inmemoryVault 251 } else { 252 im.accessVault = nil 253 } 254 } 255 256 func (im *inventoryManager) GetVault() vault.Vault { 257 return im.accessVault 258 }