lockbox.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package lockbox
  13. import (
  14. "context"
  15. "encoding/json"
  16. "fmt"
  17. "github.com/yandex-cloud/go-genproto/yandex/cloud/lockbox/v1"
  18. "github.com/yandex-cloud/go-sdk/iamkey"
  19. corev1 "k8s.io/api/core/v1"
  20. "k8s.io/apimachinery/pkg/types"
  21. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  22. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  23. "github.com/external-secrets/external-secrets/pkg/provider"
  24. "github.com/external-secrets/external-secrets/pkg/provider/schema"
  25. "github.com/external-secrets/external-secrets/pkg/provider/yandex/lockbox/client"
  26. "github.com/external-secrets/external-secrets/pkg/provider/yandex/lockbox/client/grpc"
  27. )
  28. // lockboxProvider is a provider for Yandex Lockbox.
  29. type lockboxProvider struct {
  30. lockboxClientCreator client.LockboxClientCreator
  31. }
  32. // NewClient constructs a Yandex Lockbox Provider.
  33. func (p *lockboxProvider) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
  34. storeSpec := store.GetSpec()
  35. if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.YandexLockbox == nil {
  36. return nil, fmt.Errorf("received invalid Yandex Lockbox SecretStore resource")
  37. }
  38. storeSpecYandexLockbox := storeSpec.Provider.YandexLockbox
  39. authorizedKeySecretName := storeSpecYandexLockbox.Auth.AuthorizedKey.Name
  40. if authorizedKeySecretName == "" {
  41. return nil, fmt.Errorf("invalid Yandex Lockbox SecretStore resource: missing AuthorizedKey Name")
  42. }
  43. objectKey := types.NamespacedName{
  44. Name: authorizedKeySecretName,
  45. Namespace: namespace,
  46. }
  47. // only ClusterStore is allowed to set namespace (and then it's required)
  48. if store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
  49. if storeSpecYandexLockbox.Auth.AuthorizedKey.Namespace == nil {
  50. return nil, fmt.Errorf("invalid ClusterSecretStore: missing AuthorizedKey Namespace")
  51. }
  52. objectKey.Namespace = *storeSpecYandexLockbox.Auth.AuthorizedKey.Namespace
  53. }
  54. authorizedKeySecret := &corev1.Secret{}
  55. err := kube.Get(ctx, objectKey, authorizedKeySecret)
  56. if err != nil {
  57. return nil, fmt.Errorf("could not fetch AuthorizedKey secret: %w", err)
  58. }
  59. authorizedKeySecretData := authorizedKeySecret.Data[storeSpecYandexLockbox.Auth.AuthorizedKey.Key]
  60. if (authorizedKeySecretData == nil) || (len(authorizedKeySecretData) == 0) {
  61. return nil, fmt.Errorf("missing AuthorizedKey")
  62. }
  63. var authorizedKey iamkey.Key
  64. err = json.Unmarshal(authorizedKeySecretData, &authorizedKey)
  65. if err != nil {
  66. return nil, fmt.Errorf("unable to unmarshal authorized key: %w", err)
  67. }
  68. lb, err := p.lockboxClientCreator.Create(ctx, &authorizedKey)
  69. if err != nil {
  70. return nil, fmt.Errorf("failed to create Yandex.Cloud SDK: %w", err)
  71. }
  72. return &lockboxSecretsClient{lb}, nil
  73. }
  74. // lockboxSecretsClient is a secrets client for Yandex Lockbox.
  75. type lockboxSecretsClient struct {
  76. lockboxClient client.LockboxClient
  77. }
  78. // GetSecret returns a single secret from the provider.
  79. func (p *lockboxSecretsClient) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
  80. entries, err := p.lockboxClient.GetPayloadEntries(ctx, ref.Key, ref.Version)
  81. if err != nil {
  82. return nil, fmt.Errorf("unable to request secret payload to get secret: %w", err)
  83. }
  84. if ref.Property == "" {
  85. keyToValue := make(map[string]interface{}, len(entries))
  86. for _, entry := range entries {
  87. value, err := getValueAsIs(entry)
  88. if err != nil {
  89. return nil, err
  90. }
  91. keyToValue[entry.Key] = value
  92. }
  93. out, err := json.Marshal(keyToValue)
  94. if err != nil {
  95. return nil, fmt.Errorf("failed to marshal secret: %w", err)
  96. }
  97. return out, nil
  98. }
  99. entry, err := findEntryByKey(entries, ref.Property)
  100. if err != nil {
  101. return nil, err
  102. }
  103. return getValueAsBinary(entry)
  104. }
  105. // GetSecretMap returns multiple k/v pairs from the provider.
  106. func (p *lockboxSecretsClient) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  107. entries, err := p.lockboxClient.GetPayloadEntries(ctx, ref.Key, ref.Version)
  108. if err != nil {
  109. return nil, fmt.Errorf("unable to request secret payload to get secret map: %w", err)
  110. }
  111. secretMap := make(map[string][]byte, len(entries))
  112. for _, entry := range entries {
  113. value, err := getValueAsBinary(entry)
  114. if err != nil {
  115. return nil, err
  116. }
  117. secretMap[entry.Key] = value
  118. }
  119. return secretMap, nil
  120. }
  121. func (p *lockboxSecretsClient) Close(ctx context.Context) error {
  122. return p.lockboxClient.Close(ctx)
  123. }
  124. func getValueAsIs(entry *lockbox.Payload_Entry) (interface{}, error) {
  125. switch entry.Value.(type) {
  126. case *lockbox.Payload_Entry_TextValue:
  127. return entry.GetTextValue(), nil
  128. case *lockbox.Payload_Entry_BinaryValue:
  129. return entry.GetBinaryValue(), nil
  130. default:
  131. return nil, fmt.Errorf("unsupported payload value type, key: %v", entry.Key)
  132. }
  133. }
  134. func getValueAsBinary(entry *lockbox.Payload_Entry) ([]byte, error) {
  135. switch entry.Value.(type) {
  136. case *lockbox.Payload_Entry_TextValue:
  137. return []byte(entry.GetTextValue()), nil
  138. case *lockbox.Payload_Entry_BinaryValue:
  139. return entry.GetBinaryValue(), nil
  140. default:
  141. return nil, fmt.Errorf("unsupported payload value type, key: %v", entry.Key)
  142. }
  143. }
  144. func findEntryByKey(entries []*lockbox.Payload_Entry, key string) (*lockbox.Payload_Entry, error) {
  145. for i := range entries {
  146. if entries[i].Key == key {
  147. return entries[i], nil
  148. }
  149. }
  150. return nil, fmt.Errorf("payload entry with key '%s' not found", key)
  151. }
  152. func init() {
  153. schema.Register(
  154. &lockboxProvider{
  155. lockboxClientCreator: &grpc.LockboxClientCreator{},
  156. },
  157. &esv1alpha1.SecretStoreProvider{
  158. YandexLockbox: &esv1alpha1.YandexLockboxProvider{},
  159. },
  160. )
  161. }