oracle.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  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 oracle implements a provider for Oracle Cloud Infrastructure Vault.
  14. // It allows fetching and managing secrets stored in OCI Vault using the OCI SDK.
  15. package oracle
  16. import (
  17. "bytes"
  18. "context"
  19. "encoding/base64"
  20. "encoding/json"
  21. "errors"
  22. "fmt"
  23. "os"
  24. "regexp"
  25. "sync"
  26. "time"
  27. "github.com/oracle/oci-go-sdk/v65/common"
  28. "github.com/oracle/oci-go-sdk/v65/common/auth"
  29. "github.com/oracle/oci-go-sdk/v65/keymanagement"
  30. "github.com/oracle/oci-go-sdk/v65/secrets"
  31. "github.com/oracle/oci-go-sdk/v65/vault"
  32. "github.com/tidwall/gjson"
  33. corev1 "k8s.io/api/core/v1"
  34. "k8s.io/client-go/kubernetes"
  35. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  36. ctrlcfg "sigs.k8s.io/controller-runtime/pkg/client/config"
  37. "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
  38. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  39. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  40. "github.com/external-secrets/external-secrets/pkg/utils"
  41. "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
  42. )
  43. const (
  44. errOracleClient = "cannot setup new oracle client: %w"
  45. errORACLECredSecretName = "invalid oracle SecretStore resource: missing oracle APIKey"
  46. errUninitalizedOracleProvider = "provider oracle is not initialized"
  47. errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w"
  48. errMissingPK = "missing PrivateKey"
  49. errMissingUser = "missing User ID"
  50. errMissingTenancy = "missing Tenancy ID"
  51. errMissingRegion = "missing Region"
  52. errMissingFingerprint = "missing Fingerprint"
  53. errMissingVault = "missing Vault"
  54. errJSONSecretUnmarshal = "unable to unmarshal secret from JSON: %w"
  55. errMissingKey = "missing Key in secret: %s"
  56. errUnexpectedContent = "unexpected secret bundle content"
  57. errSettingOCIEnvVariables = "unable to set OCI SDK environment variable %s: %w"
  58. )
  59. // https://github.com/external-secrets/external-secrets/issues/644
  60. var _ esv1.SecretsClient = &VaultManagementService{}
  61. var _ esv1.Provider = &VaultManagementService{}
  62. // VaultManagementService implements the External Secrets provider interface for Oracle Cloud Infrastructure Vault.
  63. type VaultManagementService struct {
  64. Client VMInterface
  65. KmsVaultClient KmsVCInterface
  66. VaultClient VaultInterface
  67. vault string
  68. compartment string
  69. encryptionKey string
  70. workloadIdentityMutex sync.Mutex
  71. }
  72. // VMInterface defines the interface for OCI Secrets Management Client operations.
  73. type VMInterface interface {
  74. GetSecretBundleByName(ctx context.Context, request secrets.GetSecretBundleByNameRequest) (secrets.GetSecretBundleByNameResponse, error)
  75. }
  76. // KmsVCInterface defines the interface for OCI Key Management Service Vault Client operations.
  77. type KmsVCInterface interface {
  78. GetVault(ctx context.Context, request keymanagement.GetVaultRequest) (response keymanagement.GetVaultResponse, err error)
  79. }
  80. // VaultInterface defines the interface for OCI Vault operations.
  81. type VaultInterface interface {
  82. ListSecrets(ctx context.Context, request vault.ListSecretsRequest) (response vault.ListSecretsResponse, err error)
  83. CreateSecret(ctx context.Context, request vault.CreateSecretRequest) (response vault.CreateSecretResponse, err error)
  84. UpdateSecret(ctx context.Context, request vault.UpdateSecretRequest) (response vault.UpdateSecretResponse, err error)
  85. ScheduleSecretDeletion(ctx context.Context, request vault.ScheduleSecretDeletionRequest) (response vault.ScheduleSecretDeletionResponse, err error)
  86. }
  87. const (
  88. // SecretNotFound indicates that the requested secret was not found in the vault.
  89. SecretNotFound = iota
  90. // SecretExists indicates that the secret exists in the vault.
  91. SecretExists
  92. // SecretAPIError indicates that an API error occurred while accessing the secret.
  93. SecretAPIError
  94. )
  95. // PushSecret creates or updates a secret in the Oracle Cloud Infrastructure Vault.
  96. func (vms *VaultManagementService) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1.PushSecretData) error {
  97. if vms.encryptionKey == "" {
  98. return errors.New("SecretStore must reference encryption key")
  99. }
  100. value := secret.Data[data.GetSecretKey()]
  101. if data.GetSecretKey() == "" {
  102. secretData := map[string]string{}
  103. for k, v := range secret.Data {
  104. secretData[k] = string(v)
  105. }
  106. jsonSecret, err := json.Marshal(secretData)
  107. if err != nil {
  108. return fmt.Errorf("unable to create json %v from value: %v", value, secretData)
  109. }
  110. value = jsonSecret
  111. }
  112. secretName := data.GetRemoteKey()
  113. encodedValue := base64.StdEncoding.EncodeToString(value)
  114. sec, action, err := vms.getSecretBundleWithCode(ctx, secretName)
  115. switch action {
  116. case SecretNotFound:
  117. _, err = vms.VaultClient.CreateSecret(ctx, vault.CreateSecretRequest{
  118. CreateSecretDetails: vault.CreateSecretDetails{
  119. CompartmentId: &vms.compartment,
  120. KeyId: &vms.encryptionKey,
  121. SecretContent: vault.Base64SecretContentDetails{
  122. Content: &encodedValue,
  123. },
  124. SecretName: &secretName,
  125. VaultId: &vms.vault,
  126. },
  127. })
  128. return sanitizeOCISDKErr(err)
  129. case SecretExists:
  130. payload, err := decodeBundle(sec)
  131. if err != nil {
  132. return err
  133. }
  134. if bytes.Equal(payload, value) {
  135. return nil
  136. }
  137. _, err = vms.VaultClient.UpdateSecret(ctx, vault.UpdateSecretRequest{
  138. SecretId: sec.SecretId,
  139. UpdateSecretDetails: vault.UpdateSecretDetails{
  140. SecretContent: vault.Base64SecretContentDetails{
  141. Content: &encodedValue,
  142. },
  143. },
  144. })
  145. return sanitizeOCISDKErr(err)
  146. default:
  147. return sanitizeOCISDKErr(err)
  148. }
  149. }
  150. // DeleteSecret removes a secret from the Oracle Cloud Infrastructure Vault.
  151. func (vms *VaultManagementService) DeleteSecret(ctx context.Context, remoteRef esv1.PushSecretRemoteRef) error {
  152. secretName := remoteRef.GetRemoteKey()
  153. resp, action, err := vms.getSecretBundleWithCode(ctx, secretName)
  154. switch action {
  155. case SecretNotFound:
  156. return nil
  157. case SecretExists:
  158. if resp.TimeOfDeletion != nil {
  159. return nil
  160. }
  161. _, err = vms.VaultClient.ScheduleSecretDeletion(ctx, vault.ScheduleSecretDeletionRequest{
  162. SecretId: resp.SecretId,
  163. })
  164. return sanitizeOCISDKErr(err)
  165. default:
  166. return sanitizeOCISDKErr(err)
  167. }
  168. }
  169. // SecretExists checks if a secret exists in the Oracle Cloud Infrastructure Vault.
  170. func (vms *VaultManagementService) SecretExists(_ context.Context, _ esv1.PushSecretRemoteRef) (bool, error) {
  171. return false, errors.New("not implemented")
  172. }
  173. // GetAllSecrets retrieves all secrets from the Oracle Cloud Infrastructure Vault that match the given criteria.
  174. func (vms *VaultManagementService) GetAllSecrets(ctx context.Context, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
  175. var page *string
  176. var summaries []vault.SecretSummary
  177. for {
  178. resp, err := vms.VaultClient.ListSecrets(ctx, vault.ListSecretsRequest{
  179. CompartmentId: &vms.compartment,
  180. Page: page,
  181. VaultId: &vms.vault,
  182. })
  183. if err != nil {
  184. return nil, sanitizeOCISDKErr(err)
  185. }
  186. summaries = append(summaries, resp.Items...)
  187. if page = resp.OpcNextPage; resp.OpcNextPage == nil {
  188. break
  189. }
  190. }
  191. return vms.filteredSummaryResult(ctx, summaries, ref)
  192. }
  193. // GetSecret retrieves a specific secret from the Oracle Cloud Infrastructure Vault.
  194. func (vms *VaultManagementService) GetSecret(ctx context.Context, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
  195. if utils.IsNil(vms.Client) {
  196. return nil, errors.New(errUninitalizedOracleProvider)
  197. }
  198. sec, err := vms.Client.GetSecretBundleByName(ctx, secrets.GetSecretBundleByNameRequest{
  199. VaultId: &vms.vault,
  200. SecretName: &ref.Key,
  201. Stage: secrets.GetSecretBundleByNameStageEnum(ref.Version),
  202. })
  203. if err != nil {
  204. return nil, sanitizeOCISDKErr(err)
  205. }
  206. payload, err := decodeBundle(sec)
  207. if err != nil {
  208. return nil, err
  209. }
  210. if ref.Property == "" {
  211. return payload, nil
  212. }
  213. val := gjson.Get(string(payload), ref.Property)
  214. if !val.Exists() {
  215. return nil, fmt.Errorf(errMissingKey, ref.Key)
  216. }
  217. return []byte(val.String()), nil
  218. }
  219. func decodeBundle(sec secrets.GetSecretBundleByNameResponse) ([]byte, error) {
  220. bt, ok := sec.SecretBundleContent.(secrets.Base64SecretBundleContentDetails)
  221. if !ok {
  222. return nil, errors.New(errUnexpectedContent)
  223. }
  224. payload, err := base64.StdEncoding.DecodeString(*bt.Content)
  225. if err != nil {
  226. return nil, err
  227. }
  228. return payload, nil
  229. }
  230. // GetSecretMap retrieves a secret and returns it as a map of key/value pairs.
  231. func (vms *VaultManagementService) GetSecretMap(ctx context.Context, ref esv1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  232. data, err := vms.GetSecret(ctx, ref)
  233. if err != nil {
  234. return nil, sanitizeOCISDKErr(err)
  235. }
  236. kv := make(map[string]string)
  237. err = json.Unmarshal(data, &kv)
  238. if err != nil {
  239. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  240. }
  241. secretData := make(map[string][]byte)
  242. for k, v := range kv {
  243. secretData[k] = []byte(v)
  244. }
  245. return secretData, nil
  246. }
  247. // Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
  248. func (vms *VaultManagementService) Capabilities() esv1.SecretStoreCapabilities {
  249. return esv1.SecretStoreReadOnly
  250. }
  251. // NewClient constructs a new secrets client based on the provided store.
  252. func (vms *VaultManagementService) NewClient(ctx context.Context, store esv1.GenericStore, kube kclient.Client, namespace string) (esv1.SecretsClient, error) {
  253. storeSpec := store.GetSpec()
  254. oracleSpec := storeSpec.Provider.Oracle
  255. if oracleSpec.Vault == "" {
  256. return nil, errors.New(errMissingVault)
  257. }
  258. if oracleSpec.Region == "" {
  259. return nil, errors.New(errMissingRegion)
  260. }
  261. configurationProvider, err := vms.constructProvider(ctx, store, oracleSpec, kube, namespace)
  262. if err != nil {
  263. return nil, err
  264. }
  265. secretManagementService, err := secrets.NewSecretsClientWithConfigurationProvider(configurationProvider)
  266. if err != nil {
  267. return nil, fmt.Errorf(errOracleClient, err)
  268. }
  269. secretManagementService.SetRegion(oracleSpec.Region)
  270. kmsVaultClient, err := keymanagement.NewKmsVaultClientWithConfigurationProvider(configurationProvider)
  271. if err != nil {
  272. return nil, fmt.Errorf(errOracleClient, err)
  273. }
  274. kmsVaultClient.SetRegion(oracleSpec.Region)
  275. vaultClient, err := vault.NewVaultsClientWithConfigurationProvider(configurationProvider)
  276. if err != nil {
  277. return nil, fmt.Errorf(errOracleClient, err)
  278. }
  279. vaultClient.SetRegion(oracleSpec.Region)
  280. if storeSpec.RetrySettings != nil {
  281. if err := vms.configureRetryPolicy(storeSpec, secretManagementService, kmsVaultClient, vaultClient); err != nil {
  282. return nil, fmt.Errorf(errOracleClient, err)
  283. }
  284. }
  285. return &VaultManagementService{
  286. Client: secretManagementService,
  287. KmsVaultClient: kmsVaultClient,
  288. VaultClient: vaultClient,
  289. vault: oracleSpec.Vault,
  290. compartment: oracleSpec.Compartment,
  291. encryptionKey: oracleSpec.EncryptionKey,
  292. }, nil
  293. }
  294. func (vms *VaultManagementService) constructOptions(storeSpec *esv1.SecretStoreSpec) ([]common.RetryPolicyOption, error) {
  295. opts := []common.RetryPolicyOption{common.WithShouldRetryOperation(common.DefaultShouldRetryOperation)}
  296. if mr := storeSpec.RetrySettings.MaxRetries; mr != nil {
  297. attempts := safeConvert(*mr)
  298. opts = append(opts, common.WithMaximumNumberAttempts(attempts))
  299. }
  300. if ri := storeSpec.RetrySettings.RetryInterval; ri != nil {
  301. i, err := time.ParseDuration(*storeSpec.RetrySettings.RetryInterval)
  302. if err != nil {
  303. return nil, fmt.Errorf(errOracleClient, err)
  304. }
  305. opts = append(opts, common.WithFixedBackoff(i))
  306. }
  307. return opts, nil
  308. }
  309. func safeConvert(i int32) uint {
  310. if i < 0 {
  311. return 0
  312. }
  313. return uint(i)
  314. }
  315. func (vms *VaultManagementService) getSecretBundleWithCode(ctx context.Context, secretName string) (secrets.GetSecretBundleByNameResponse, int, error) {
  316. // Try to look up the secret, which will determine if we should create or update the secret.
  317. resp, err := vms.Client.GetSecretBundleByName(ctx, secrets.GetSecretBundleByNameRequest{
  318. SecretName: &secretName,
  319. VaultId: &vms.vault,
  320. })
  321. // Get a PushSecret action depending on the ListSecrets response.
  322. action := getSecretBundleCode(err)
  323. return resp, action, err
  324. }
  325. func getSecretBundleCode(err error) int {
  326. if err != nil {
  327. // If we got a 404 service error, try to create the secret.
  328. if serviceErr, ok := err.(common.ServiceError); ok && serviceErr.GetHTTPStatusCode() == 404 {
  329. return SecretNotFound
  330. }
  331. return SecretAPIError
  332. }
  333. // Otherwise, update the existing secret.
  334. return SecretExists
  335. }
  336. func (vms *VaultManagementService) filteredSummaryResult(ctx context.Context, secretSummaries []vault.SecretSummary, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
  337. secretMap := map[string][]byte{}
  338. for _, summary := range secretSummaries {
  339. matches, err := matchesRef(summary, ref)
  340. if err != nil {
  341. return nil, err
  342. }
  343. if !matches || summary.TimeOfDeletion != nil {
  344. continue
  345. }
  346. secret, err := vms.GetSecret(ctx, esv1.ExternalSecretDataRemoteRef{
  347. Key: *summary.SecretName,
  348. })
  349. if err != nil {
  350. return nil, err
  351. }
  352. secretMap[*summary.SecretName] = secret
  353. }
  354. return secretMap, nil
  355. }
  356. func matchesRef(secretSummary vault.SecretSummary, ref esv1.ExternalSecretFind) (bool, error) {
  357. if ref.Name != nil {
  358. matchString, err := regexp.MatchString(ref.Name.RegExp, *secretSummary.SecretName)
  359. if err != nil {
  360. return false, err
  361. }
  362. return matchString, nil
  363. }
  364. for k, v := range ref.Tags {
  365. if val, ok := secretSummary.FreeformTags[k]; ok {
  366. if val == v {
  367. return true, nil
  368. }
  369. }
  370. }
  371. return false, nil
  372. }
  373. func getSecretData(ctx context.Context, kube kclient.Client, namespace, storeKind string, secretRef esmeta.SecretKeySelector) (string, error) {
  374. if secretRef.Name == "" {
  375. return "", errors.New(errORACLECredSecretName)
  376. }
  377. secret, err := resolvers.SecretKeyRef(
  378. ctx,
  379. kube,
  380. storeKind,
  381. namespace,
  382. &secretRef,
  383. )
  384. if err != nil {
  385. return "", fmt.Errorf(errFetchSAKSecret, err)
  386. }
  387. return secret, nil
  388. }
  389. func getUserAuthConfigurationProvider(ctx context.Context, kube kclient.Client, store *esv1.OracleProvider, namespace, storeKind, region string) (common.ConfigurationProvider, error) {
  390. privateKey, err := getSecretData(ctx, kube, namespace, storeKind, store.Auth.SecretRef.PrivateKey)
  391. if err != nil {
  392. return nil, err
  393. }
  394. if privateKey == "" {
  395. return nil, errors.New(errMissingPK)
  396. }
  397. fingerprint, err := getSecretData(ctx, kube, namespace, storeKind, store.Auth.SecretRef.Fingerprint)
  398. if err != nil {
  399. return nil, err
  400. }
  401. if fingerprint == "" {
  402. return nil, errors.New(errMissingFingerprint)
  403. }
  404. if store.Auth.User == "" {
  405. return nil, errors.New(errMissingUser)
  406. }
  407. if store.Auth.Tenancy == "" {
  408. return nil, errors.New(errMissingTenancy)
  409. }
  410. return common.NewRawConfigurationProvider(store.Auth.Tenancy, store.Auth.User, region, fingerprint, privateKey, nil), nil
  411. }
  412. // Close releases any resources used by the VaultManagementService.
  413. func (vms *VaultManagementService) Close(_ context.Context) error {
  414. return nil
  415. }
  416. // Validate performs validation of the Oracle Cloud Infrastructure provider configuration.
  417. func (vms *VaultManagementService) Validate() (esv1.ValidationResult, error) {
  418. _, err := vms.KmsVaultClient.GetVault(
  419. context.Background(), keymanagement.GetVaultRequest{
  420. VaultId: &vms.vault,
  421. },
  422. )
  423. if err != nil {
  424. failure, ok := common.IsServiceError(err)
  425. if ok {
  426. code := failure.GetCode()
  427. switch code {
  428. case "NotAuthenticated":
  429. return esv1.ValidationResultError, sanitizeOCISDKErr(err)
  430. case "NotAuthorizedOrNotFound":
  431. // User authentication was successful, but user might not have a permission like:
  432. //
  433. // Allow group external_secrets to read vaults in tenancy
  434. //
  435. // Which is fine, because to read secrets we only need:
  436. //
  437. // Allow group external_secrets to read secret-family in tenancy
  438. //
  439. // But we can't test for this permission without knowing the name of a secret
  440. return esv1.ValidationResultUnknown, sanitizeOCISDKErr(err)
  441. default:
  442. return esv1.ValidationResultError, sanitizeOCISDKErr(err)
  443. }
  444. } else {
  445. return esv1.ValidationResultError, err
  446. }
  447. }
  448. return esv1.ValidationResultReady, nil
  449. }
  450. // ValidateStore validates the Oracle Cloud Infrastructure SecretStore resource configuration.
  451. func (vms *VaultManagementService) ValidateStore(store esv1.GenericStore) (admission.Warnings, error) {
  452. storeSpec := store.GetSpec()
  453. oracleSpec := storeSpec.Provider.Oracle
  454. vault := oracleSpec.Vault
  455. if vault == "" {
  456. return nil, errors.New("vault cannot be empty")
  457. }
  458. region := oracleSpec.Region
  459. if region == "" {
  460. return nil, errors.New("region cannot be empty")
  461. }
  462. auth := oracleSpec.Auth
  463. if auth == nil {
  464. return nil, nil
  465. }
  466. user := oracleSpec.Auth.User
  467. if user == "" {
  468. return nil, errors.New("user cannot be empty")
  469. }
  470. tenant := oracleSpec.Auth.Tenancy
  471. if tenant == "" {
  472. return nil, errors.New("tenant cannot be empty")
  473. }
  474. privateKey := oracleSpec.Auth.SecretRef.PrivateKey
  475. if privateKey.Name == "" {
  476. return nil, errors.New("privateKey.name cannot be empty")
  477. }
  478. if privateKey.Key == "" {
  479. return nil, errors.New("privateKey.key cannot be empty")
  480. }
  481. err := utils.ValidateSecretSelector(store, privateKey)
  482. if err != nil {
  483. return nil, err
  484. }
  485. fingerprint := oracleSpec.Auth.SecretRef.Fingerprint
  486. if fingerprint.Name == "" {
  487. return nil, errors.New("fingerprint.name cannot be empty")
  488. }
  489. if fingerprint.Key == "" {
  490. return nil, errors.New("fingerprint.key cannot be empty")
  491. }
  492. err = utils.ValidateSecretSelector(store, fingerprint)
  493. if err != nil {
  494. return nil, err
  495. }
  496. if oracleSpec.ServiceAccountRef != nil {
  497. if err := utils.ValidateReferentServiceAccountSelector(store, *oracleSpec.ServiceAccountRef); err != nil {
  498. return nil, fmt.Errorf("invalid ServiceAccountRef: %w", err)
  499. }
  500. }
  501. return nil, nil
  502. }
  503. func (vms *VaultManagementService) getWorkloadIdentityProvider(store esv1.GenericStore, serviceAcccountRef *esmeta.ServiceAccountSelector, region, namespace string) (configurationProvider common.ConfigurationProvider, err error) {
  504. defer func() {
  505. if uerr := os.Unsetenv(auth.ResourcePrincipalVersionEnvVar); uerr != nil {
  506. err = errors.Join(err, fmt.Errorf(errSettingOCIEnvVariables, auth.ResourcePrincipalRegionEnvVar, uerr))
  507. }
  508. if uerr := os.Unsetenv(auth.ResourcePrincipalRegionEnvVar); uerr != nil {
  509. err = errors.Join(err, fmt.Errorf("unabled to unset OCI SDK environment variable %s: %w", auth.ResourcePrincipalVersionEnvVar, uerr))
  510. }
  511. vms.workloadIdentityMutex.Unlock()
  512. }()
  513. vms.workloadIdentityMutex.Lock()
  514. // OCI SDK requires specific environment variables for workload identity.
  515. if err := os.Setenv(auth.ResourcePrincipalVersionEnvVar, auth.ResourcePrincipalVersion2_2); err != nil {
  516. return nil, fmt.Errorf(errSettingOCIEnvVariables, auth.ResourcePrincipalVersionEnvVar, err)
  517. }
  518. if err := os.Setenv(auth.ResourcePrincipalRegionEnvVar, region); err != nil {
  519. return nil, fmt.Errorf(errSettingOCIEnvVariables, auth.ResourcePrincipalRegionEnvVar, err)
  520. }
  521. // If no service account is specified, use the pod service account to create the Workload Identity provider.
  522. if serviceAcccountRef == nil {
  523. return auth.OkeWorkloadIdentityConfigurationProvider()
  524. }
  525. // Ensure the service account ref is being used appropriately, so arbitrary tokens are not minted by the provider.
  526. if err = utils.ValidateServiceAccountSelector(store, *serviceAcccountRef); err != nil {
  527. return nil, fmt.Errorf("invalid ServiceAccountRef: %w", err)
  528. }
  529. cfg, err := ctrlcfg.GetConfig()
  530. if err != nil {
  531. return nil, err
  532. }
  533. clientset, err := kubernetes.NewForConfig(cfg)
  534. if err != nil {
  535. return nil, err
  536. }
  537. tokenProvider := NewTokenProvider(clientset, serviceAcccountRef, namespace)
  538. return auth.OkeWorkloadIdentityConfigurationProviderWithServiceAccountTokenProvider(tokenProvider)
  539. }
  540. func (vms *VaultManagementService) constructProvider(ctx context.Context, store esv1.GenericStore, oracleSpec *esv1.OracleProvider, kube kclient.Client, namespace string) (common.ConfigurationProvider, error) {
  541. var (
  542. configurationProvider common.ConfigurationProvider
  543. err error
  544. )
  545. if oracleSpec.PrincipalType == esv1.WorkloadPrincipal {
  546. configurationProvider, err = vms.getWorkloadIdentityProvider(store, oracleSpec.ServiceAccountRef, oracleSpec.Region, namespace)
  547. } else if oracleSpec.PrincipalType == esv1.InstancePrincipal || oracleSpec.Auth == nil {
  548. configurationProvider, err = auth.InstancePrincipalConfigurationProvider()
  549. } else {
  550. configurationProvider, err = getUserAuthConfigurationProvider(ctx, kube, oracleSpec, namespace, store.GetObjectKind().GroupVersionKind().Kind, oracleSpec.Region)
  551. }
  552. if err != nil {
  553. return nil, fmt.Errorf(errOracleClient, err)
  554. }
  555. return configurationProvider, nil
  556. }
  557. func (vms *VaultManagementService) configureRetryPolicy(
  558. storeSpec *esv1.SecretStoreSpec,
  559. secretManagementService secrets.SecretsClient,
  560. kmsVaultClient keymanagement.KmsVaultClient,
  561. vaultClient vault.VaultsClient,
  562. ) error {
  563. opts, err := vms.constructOptions(storeSpec)
  564. if err != nil {
  565. return err
  566. }
  567. customRetryPolicy := common.NewRetryPolicyWithOptions(opts...)
  568. secretManagementService.SetCustomClientConfiguration(common.CustomClientConfiguration{
  569. RetryPolicy: &customRetryPolicy,
  570. })
  571. kmsVaultClient.SetCustomClientConfiguration(common.CustomClientConfiguration{
  572. RetryPolicy: &customRetryPolicy,
  573. })
  574. vaultClient.SetCustomClientConfiguration(common.CustomClientConfiguration{
  575. RetryPolicy: &customRetryPolicy,
  576. })
  577. return err
  578. }
  579. func sanitizeOCISDKErr(err error) error {
  580. if err == nil {
  581. return nil
  582. }
  583. // If we have a ServiceError from the OCI SDK, strip only the message from the verbose error
  584. if serviceError, ok := err.(common.ServiceErrorRichInfo); ok {
  585. return fmt.Errorf("%s service failed to %s, HTTP status code %d: %s", serviceError.GetTargetService(), serviceError.GetOperationName(), serviceError.GetHTTPStatusCode(), serviceError.GetMessage())
  586. }
  587. return err
  588. }
  589. func init() {
  590. esv1.Register(&VaultManagementService{}, &esv1.SecretStoreProvider{
  591. Oracle: &esv1.OracleProvider{},
  592. }, esv1.MaintenanceStatusMaintained)
  593. }