go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers-sdk/v1/vault/gcpberglas/berglas.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package gcpberglas 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "strings" 11 12 berglas "github.com/GoogleCloudPlatform/berglas/pkg/berglas" 13 "go.mondoo.com/cnquery/providers-sdk/v1/vault" 14 "go.mondoo.com/cnquery/utils/multierr" 15 ) 16 17 type storageType string 18 19 type Option func(*Vault) 20 21 const cloudStorage storageType = "storage" 22 23 // https://github.com/GoogleCloudPlatform/berglas 24 func New(projectID string, opts ...Option) *Vault { 25 v := &Vault{projectID: projectID} 26 for _, opt := range opts { 27 opt(v) 28 } 29 return v 30 } 31 32 func WithBucket(bucket string) Option { 33 return func(v *Vault) { 34 v.bucket = bucket 35 v.storageType = cloudStorage 36 } 37 } 38 39 func WithKmsKey(kmsKeyID string) Option { 40 return func(v *Vault) { 41 v.kmsKeyID = kmsKeyID 42 } 43 } 44 45 type berglasStorageInfo struct { 46 bucket string 47 object string 48 } 49 50 type Vault struct { 51 projectID string 52 storageType storageType 53 kmsKeyID string 54 bucket string 55 } 56 57 func (v *Vault) About(context.Context, *vault.Empty) (*vault.VaultInfo, error) { 58 return &vault.VaultInfo{Name: "GCP Berglas: " + v.projectID}, nil 59 } 60 61 func (v *Vault) client(ctx context.Context) (*berglas.Client, error) { 62 client, err := berglas.New(ctx) 63 if err != nil { 64 return nil, multierr.Wrap(err, "failed to setup gcp berglas client") 65 } 66 return client, nil 67 } 68 69 // expected berglas key format: {storage}/{bucketName}/{objectName} 70 func getBerglasStorageInfo(key string) (berglasStorageInfo, error) { 71 split := strings.Split(key, "/") 72 if len(split) != 3 { 73 return berglasStorageInfo{}, errors.New("invalid berglas key provided") 74 } 75 // we omit storage for now, as berglas secrets manager is not yet supported 76 // however, the key contains the type as to not break any existing keys if we add support in the future 77 return berglasStorageInfo{ 78 bucket: split[1], 79 object: split[2], 80 }, nil 81 } 82 83 func (v *Vault) assembleBerglasKeyId(key string) (string, error) { 84 if v.storageType == cloudStorage { 85 return fmt.Sprintf("%s/%s/%s", v.storageType, v.bucket, key), nil 86 } 87 return "", errors.New("invalid berglas storage type") 88 } 89 90 func (v *Vault) Get(ctx context.Context, id *vault.SecretID) (*vault.Secret, error) { 91 c, err := v.client(ctx) 92 if err != nil { 93 return nil, err 94 } 95 96 berglasReadInfo, err := getBerglasStorageInfo(id.Key) 97 if err != nil { 98 return nil, err 99 } 100 101 result, err := c.Read(ctx, &berglas.StorageReadRequest{ 102 Bucket: berglasReadInfo.bucket, 103 Object: berglasReadInfo.object, 104 }) 105 if err != nil { 106 return nil, vault.NotFoundError 107 } 108 109 return &vault.Secret{ 110 Key: id.Key, 111 Data: result.Plaintext, 112 // we do not know the encoding here, but the default is binary 113 Encoding: vault.SecretEncoding_encoding_binary, 114 }, nil 115 } 116 117 func (v *Vault) Set(ctx context.Context, cred *vault.Secret) (*vault.SecretID, error) { 118 if len(v.kmsKeyID) == 0 { 119 return nil, errors.New("specified KMS key id is empty") 120 } 121 122 if len(v.storageType) == 0 { 123 return nil, errors.New("cannot create vault secret without a storage type") 124 } 125 126 if len(v.bucket) == 0 && v.storageType == cloudStorage { 127 return nil, errors.New("specified bucket name is empty") 128 } 129 130 c, err := v.client(ctx) 131 if err != nil { 132 return nil, err 133 } 134 135 // assemble the berglas key that will be used to get this secret 136 // it uses the storage type and the passed in key to build a key 137 key, err := v.assembleBerglasKeyId(cred.Key) 138 if err != nil { 139 return nil, err 140 } 141 142 _, err = c.Create(ctx, &berglas.StorageCreateRequest{ 143 Bucket: v.bucket, 144 Object: cred.Key, 145 Plaintext: cred.Data, 146 Key: v.kmsKeyID, 147 }) 148 if err != nil { 149 return nil, err 150 } 151 return &vault.SecretID{ 152 Key: key, 153 }, nil 154 }