go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers-sdk/v1/vault/keyring/keyring.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package keyring 5 6 import ( 7 "context" 8 "errors" 9 10 "github.com/99designs/keyring" 11 "github.com/rs/zerolog/log" 12 "go.mondoo.com/cnquery/providers-sdk/v1/vault" 13 ) 14 15 var notImplemented = errors.New("not implemented") 16 17 func New(serviceName string) *Vault { 18 return &Vault{ 19 ServiceName: serviceName, 20 fileDir: "~/.mondoo/", 21 filePasswordFunc: func(s string) (string, error) { 22 // TODO: this only applies to cases where we have no real keychain available 23 return "", errors.New("file-fallback is not supported") 24 }, 25 // by default we do not allow a fallback to encrypted keys 26 allowedBackends: []keyring.BackendType{ 27 // Windows 28 keyring.WinCredBackend, 29 // macOS 30 keyring.KeychainBackend, 31 // Linux 32 keyring.SecretServiceBackend, 33 keyring.KWalletBackend, 34 // General 35 keyring.PassBackend, 36 }, 37 } 38 } 39 40 func NewEncryptedFile(path string, serviceName string, password string) *Vault { 41 return &Vault{ 42 ServiceName: serviceName, 43 fileDir: path, 44 filePasswordFunc: func(s string) (string, error) { 45 return password, nil 46 }, 47 allowedBackends: []keyring.BackendType{ 48 keyring.FileBackend, 49 }, 50 } 51 } 52 53 func NewLinuxKernelKeyring(serviceName string) *Vault { 54 return &Vault{ 55 ServiceName: serviceName, 56 allowedBackends: []keyring.BackendType{ 57 keyring.KeyCtlBackend, 58 }, 59 keyctlscope: "user", 60 } 61 } 62 63 type Vault struct { 64 ServiceName string 65 allowedBackends []keyring.BackendType 66 fileDir string 67 filePasswordFunc func(s string) (string, error) 68 keyctlscope string 69 } 70 71 func (v *Vault) About(context.Context, *vault.Empty) (*vault.VaultInfo, error) { 72 return &vault.VaultInfo{Name: "Keyring Vault: " + v.ServiceName}, nil 73 } 74 75 func (v *Vault) open() (keyring.Keyring, error) { 76 return keyring.Open(keyring.Config{ 77 ServiceName: v.ServiceName, 78 AllowedBackends: v.allowedBackends, 79 FileDir: v.fileDir, 80 FilePasswordFunc: v.filePasswordFunc, 81 KeyCtlScope: v.keyctlscope, 82 }) 83 } 84 85 func (v *Vault) Set(ctx context.Context, cred *vault.Secret) (*vault.SecretID, error) { 86 ring, err := v.open() 87 if err != nil { 88 return nil, err 89 } 90 91 if cred.Encoding != vault.SecretEncoding_encoding_json && cred.Encoding != vault.SecretEncoding_encoding_undefined { 92 return nil, errors.New("only json encoding is supported") 93 } 94 95 // TODO: store data as json encoding 96 err = ring.Set(keyring.Item{ 97 Key: cred.Key, 98 Label: cred.Label, 99 Data: cred.Data, 100 }) 101 102 return &vault.SecretID{ 103 Key: cred.Key, 104 }, err 105 } 106 107 func (v *Vault) Get(ctx context.Context, id *vault.SecretID) (*vault.Secret, error) { 108 if id == nil { 109 return nil, errors.New("id cannot be nil") 110 } 111 log.Debug().Str("id", id.Key).Msg("get secret from keyring") 112 ring, err := v.open() 113 if err != nil { 114 return nil, err 115 } 116 117 i, err := ring.Get(id.Key) 118 if err != nil { 119 log.Debug().Err(err).Msg("could not retrieve secret from keyring") 120 return nil, vault.NotFoundError 121 } 122 123 return &vault.Secret{ 124 Key: i.Key, 125 Label: i.Label, 126 Data: i.Data, 127 Encoding: vault.SecretEncoding_encoding_json, 128 }, nil 129 }