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  }