go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers-sdk/v1/vault/config/vaultconfigstore.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package config
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"runtime"
    10  
    11  	"github.com/aws/aws-sdk-go-v2/config"
    12  	"github.com/cockroachdb/errors"
    13  	"github.com/rs/zerolog/log"
    14  	"go.mondoo.com/cnquery/providers-sdk/v1/vault"
    15  	"go.mondoo.com/cnquery/providers-sdk/v1/vault/awsparameterstore"
    16  	"go.mondoo.com/cnquery/providers-sdk/v1/vault/awssecretsmanager"
    17  	"go.mondoo.com/cnquery/providers-sdk/v1/vault/gcpberglas"
    18  	"go.mondoo.com/cnquery/providers-sdk/v1/vault/gcpsecretmanager"
    19  	"go.mondoo.com/cnquery/providers-sdk/v1/vault/hashivault"
    20  	"go.mondoo.com/cnquery/providers-sdk/v1/vault/inmemory"
    21  	"go.mondoo.com/cnquery/providers-sdk/v1/vault/keyring"
    22  )
    23  
    24  const (
    25  	VaultConfigStoreName = "mondoo-cli-keyring"
    26  	VaultConfigStoreKey  = "user-vaults"
    27  )
    28  
    29  func New(vCfg *vault.VaultConfiguration) (vault.Vault, error) {
    30  	if vCfg == nil {
    31  		return nil, errors.New("vault configuration cannot be empty")
    32  	}
    33  	log.Debug().Str("vault-name", vCfg.Name).Str("vault-type", vCfg.Type.String()).Msg("initialize new vault")
    34  	var v vault.Vault
    35  	switch vCfg.Type {
    36  	case vault.VaultType_Memory:
    37  		v = inmemory.New()
    38  	case vault.VaultType_HashiCorp:
    39  		serverUrl := vCfg.Options["url"]
    40  		token := vCfg.Options["token"]
    41  		v = hashivault.New(serverUrl, token)
    42  	case vault.VaultType_EncryptedFile:
    43  		path := vCfg.Options["path"]
    44  		keyRingName := vCfg.Options["name"]
    45  		password := vCfg.Options["password"]
    46  		v = keyring.NewEncryptedFile(path, keyRingName, password)
    47  	case vault.VaultType_KeyRing:
    48  		keyRingName := vCfg.Options["name"]
    49  		v = keyring.New(keyRingName)
    50  	case vault.VaultType_LinuxKernelKeyring:
    51  		keyRingName := vCfg.Options["name"]
    52  		v = keyring.NewLinuxKernelKeyring(keyRingName)
    53  	case vault.VaultType_GCPSecretsManager:
    54  		projectID := vCfg.Options["project-id"]
    55  		v = gcpsecretmanager.New(projectID)
    56  	case vault.VaultType_AWSSecretsManager:
    57  		// TODO: do we really want to load it from the env?
    58  		cfg, err := config.LoadDefaultConfig(context.Background())
    59  		if err != nil {
    60  			return nil, errors.Wrap(err, "cannot not determine aws environment")
    61  		}
    62  		v = awssecretsmanager.New(cfg)
    63  	case vault.VaultType_AWSParameterStore:
    64  		cfg, err := config.LoadDefaultConfig(context.Background())
    65  		if err != nil {
    66  			return nil, errors.Wrap(err, "cannot not determine aws environment")
    67  		}
    68  		v = awsparameterstore.New(cfg)
    69  	case vault.VaultType_GCPBerglas:
    70  		projectID := vCfg.Options["project-id"]
    71  		kmsKeyID := vCfg.Options["kms-key-id"]
    72  		bucketName := vCfg.Options["bucket-name"]
    73  		opts := []gcpberglas.Option{}
    74  		if kmsKeyID != "" {
    75  			opts = append(opts, gcpberglas.WithKmsKey(kmsKeyID))
    76  		}
    77  		if bucketName != "" {
    78  			opts = append(opts, gcpberglas.WithBucket(bucketName))
    79  		}
    80  		v = gcpberglas.New(projectID, opts...)
    81  
    82  	default:
    83  		return nil, errors.Errorf("could not connect to vault: %s (%s)", vCfg.Name, vCfg.Type.String())
    84  	}
    85  	return v, nil
    86  }
    87  
    88  // GetInternalVault returns the local store that is used in cnquery to store
    89  // Vault configurations eg. Hashicorp Vault access data
    90  func GetInternalVault() vault.Vault {
    91  	// on linux we are going to use kernel key management
    92  	if runtime.GOOS == "linux" {
    93  		log.Debug().Msg("use linux kernel key management to manage vaults")
    94  		return keyring.NewLinuxKernelKeyring(VaultConfigStoreName)
    95  	}
    96  
    97  	return keyring.New(VaultConfigStoreName)
    98  }
    99  
   100  // GetConfiguredVault returns a vault instance based on the configured user vaults.
   101  // It looks up in the internal vault and searches for a configuration for the vaultName
   102  func GetConfiguredVault(vaultName string) (vault.Vault, error) {
   103  	v := GetInternalVault()
   104  
   105  	ctx := context.Background()
   106  	secret, err := v.Get(ctx, &vault.SecretID{
   107  		Key: VaultConfigStoreKey,
   108  	})
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	cfgs, err := NewClientVaultConfig(secret)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	// search for the specified vault
   119  	vCfg, err := cfgs.Get(vaultName)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	log.Debug().Str("vault-name", vCfg.Name).Str("vault-type", vCfg.Type.String()).Msg("found vault config")
   125  	return New(&vCfg)
   126  }
   127  
   128  // ClientVaultConfig is the structured type where we store the client configuration for
   129  // all user configured vaults. We use it to ensure the configuration is stored in structured
   130  // format
   131  type ClientVaultConfig map[string]vault.VaultConfiguration
   132  
   133  func NewClientVaultConfig(secret *vault.Secret) (ClientVaultConfig, error) {
   134  	var vCfg ClientVaultConfig
   135  	err := json.Unmarshal(secret.Data, &vCfg)
   136  	if err != nil {
   137  		return nil, errors.Wrap(err, "corrupt vault configuration")
   138  	}
   139  	return vCfg, nil
   140  }
   141  
   142  func (avc ClientVaultConfig) Delete(key string) {
   143  	delete(avc, key)
   144  }
   145  
   146  func (avc ClientVaultConfig) Set(key string, cfg vault.VaultConfiguration) {
   147  	avc[key] = cfg
   148  }
   149  
   150  func (avc ClientVaultConfig) Get(key string) (vault.VaultConfiguration, error) {
   151  	vCfg, ok := avc[key]
   152  	if !ok {
   153  		return vault.VaultConfiguration{}, errors.New("vault not found")
   154  	}
   155  	return vCfg, nil
   156  }
   157  
   158  // SecretData returns the marshaled data, it is compatible with New()
   159  // In case the data structure cannot be marshalled, the function will panic
   160  func (avc ClientVaultConfig) SecretData() []byte {
   161  	data, err := json.Marshal(avc)
   162  	if err != nil {
   163  		panic(err)
   164  	}
   165  	return data
   166  }