client_utils.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /*
  2. Copyright © 2025 ESO Maintainer Team
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package ovh
  14. import (
  15. "context"
  16. "encoding/json"
  17. "errors"
  18. "fmt"
  19. "math"
  20. "strconv"
  21. "github.com/google/uuid"
  22. "github.com/ovh/okms-sdk-go"
  23. "github.com/tidwall/gjson"
  24. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  25. )
  26. func getSecretWithOvhSDK(ctx context.Context, kmsClient OkmsClient, okmsID uuid.UUID, ref esv1.ExternalSecretDataRemoteRef) ([]byte, *uint32, error) {
  27. // Check if the remoteRef key is empty.
  28. if ref.Key == "" {
  29. return []byte{}, nil, errors.New("remote key cannot be empty (spec.data.remoteRef.key)")
  30. }
  31. // Check MetaDataPolicy (not supported).
  32. if ref.MetadataPolicy == esv1.ExternalSecretMetadataPolicyFetch {
  33. return []byte{}, nil, errors.New("fetch metadata policy not supported")
  34. }
  35. // Decode the secret version.
  36. versionAddr, err := decodeSecretVersion(ref.Version)
  37. if err != nil {
  38. return []byte{}, nil, err
  39. }
  40. // Retrieve the KMS secret.
  41. includeData := true
  42. secret, err := kmsClient.GetSecretV2(ctx, okmsID, ref.Key, versionAddr, &includeData)
  43. if err != nil {
  44. return []byte{}, nil, handleOkmsError(err)
  45. }
  46. if secret == nil {
  47. return []byte{}, nil, esv1.NoSecretErr
  48. }
  49. if secret.Version == nil || secret.Version.Data == nil || len(*secret.Version.Data) == 0 {
  50. return []byte{}, nil, errors.New("secret version data is missing")
  51. }
  52. // Retrieve KMS Secret property if needed.
  53. var secretData []byte
  54. if ref.Property == "" {
  55. secretData, err = json.Marshal(secret.Version.Data)
  56. } else {
  57. secretData, err = getPropertyValue(*secret.Version.Data, ref.Property)
  58. }
  59. var currentVersion *uint32
  60. if secret.Metadata != nil {
  61. currentVersion = secret.Metadata.CurrentVersion
  62. }
  63. return secretData, currentVersion, err
  64. }
  65. // Decode a secret version.
  66. //
  67. // Returns nil if no version is provided; in that case, the OVH SDK uses the latest version.
  68. func decodeSecretVersion(strVersion string) (*uint32, error) {
  69. var version uint32
  70. if strVersion != "" {
  71. v, err := strconv.Atoi(strVersion)
  72. if err != nil {
  73. return nil, err
  74. }
  75. if v < 0 || v > math.MaxUint32 {
  76. return nil, errors.New("overflow occurred while decoding secret version")
  77. }
  78. version = uint32(v)
  79. } else {
  80. return nil, nil
  81. }
  82. return &version, nil
  83. }
  84. // Retrieve the value of the secret property.
  85. func getPropertyValue(data map[string]any, property string) ([]byte, error) {
  86. // Marshal data into bytes so it can be passed to gjson.Get.
  87. secretData, err := json.Marshal(data)
  88. if err != nil {
  89. return []byte{}, err
  90. }
  91. // Retrieve the property value if it exists.
  92. secretDataResult := gjson.Get(string(secretData), property)
  93. if !secretDataResult.Exists() {
  94. return []byte{}, fmt.Errorf("secret property \"%s\" not found", property)
  95. }
  96. return []byte(secretDataResult.String()), nil
  97. }
  98. // Returns an okms.KmsError struct representing the KMS response
  99. // (error_code, error_id, errors, request_id).
  100. func handleOkmsError(err error) error {
  101. okmsError := okms.AsKmsError(err)
  102. if okmsError == nil {
  103. return fmt.Errorf("failed to parse the following okms error: %w", err)
  104. } else if okmsError.ErrorCode == 17125377 { // 17125377: returned by OKMS when secret was not found
  105. return esv1.NoSecretErr
  106. }
  107. return okmsError
  108. }