client.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  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 kubernetes
  14. import (
  15. "context"
  16. "encoding/base64"
  17. "encoding/json"
  18. "errors"
  19. "fmt"
  20. "strings"
  21. "github.com/tidwall/gjson"
  22. v1 "k8s.io/api/core/v1"
  23. "k8s.io/apimachinery/pkg/api/equality"
  24. apierrors "k8s.io/apimachinery/pkg/api/errors"
  25. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  26. "k8s.io/apimachinery/pkg/labels"
  27. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  28. "github.com/external-secrets/external-secrets/pkg/constants"
  29. "github.com/external-secrets/external-secrets/pkg/find"
  30. "github.com/external-secrets/external-secrets/pkg/metrics"
  31. "github.com/external-secrets/external-secrets/pkg/utils"
  32. "github.com/external-secrets/external-secrets/pkg/utils/metadata"
  33. )
  34. const (
  35. metaLabels = "labels"
  36. metaAnnotations = "annotations"
  37. )
  38. func (c *Client) GetSecret(ctx context.Context, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
  39. secret, err := c.userSecretClient.Get(ctx, ref.Key, metav1.GetOptions{})
  40. if err != nil {
  41. return nil, err
  42. }
  43. // if property is not defined, we will return the json-serialized secret
  44. if ref.Property == "" {
  45. if ref.MetadataPolicy == esv1.ExternalSecretMetadataPolicyFetch {
  46. m := map[string]map[string]string{}
  47. m[metaLabels] = secret.Labels
  48. m[metaAnnotations] = secret.Annotations
  49. j, err := utils.JSONMarshal(m)
  50. if err != nil {
  51. return nil, err
  52. }
  53. return j, nil
  54. }
  55. m := map[string]string{}
  56. for key, val := range secret.Data {
  57. m[key] = string(val)
  58. }
  59. j, err := utils.JSONMarshal(m)
  60. if err != nil {
  61. return nil, err
  62. }
  63. return j, nil
  64. }
  65. return getSecret(secret, ref)
  66. }
  67. func (c *Client) DeleteSecret(ctx context.Context, remoteRef esv1.PushSecretRemoteRef) error {
  68. if remoteRef.GetProperty() == "" {
  69. return errors.New("requires property in RemoteRef to delete secret value")
  70. }
  71. extSecret, getErr := c.userSecretClient.Get(ctx, remoteRef.GetRemoteKey(), metav1.GetOptions{})
  72. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesGetSecret, getErr)
  73. if getErr != nil {
  74. if apierrors.IsNotFound(getErr) {
  75. // return gracefully if no secret exists
  76. return nil
  77. }
  78. return getErr
  79. }
  80. if _, ok := extSecret.Data[remoteRef.GetProperty()]; !ok {
  81. // return gracefully if specified secret does not contain the given property
  82. return nil
  83. }
  84. if len(extSecret.Data) > 1 {
  85. return c.removeProperty(ctx, extSecret, remoteRef)
  86. }
  87. return c.fullDelete(ctx, remoteRef.GetRemoteKey())
  88. }
  89. func (c *Client) SecretExists(_ context.Context, _ esv1.PushSecretRemoteRef) (bool, error) {
  90. return false, errors.New("not implemented")
  91. }
  92. func (c *Client) PushSecret(ctx context.Context, secret *v1.Secret, data esv1.PushSecretData) error {
  93. if data.GetProperty() == "" && data.GetSecretKey() != "" {
  94. return errors.New("requires property in RemoteRef to push secret value if secret key is defined")
  95. }
  96. remoteSecret := &v1.Secret{
  97. ObjectMeta: metav1.ObjectMeta{
  98. Namespace: c.store.RemoteNamespace,
  99. Name: data.GetRemoteKey(),
  100. },
  101. }
  102. return c.createOrUpdate(ctx, remoteSecret, func() error {
  103. return c.mergePushSecretData(data, remoteSecret, secret)
  104. })
  105. }
  106. func (c *Client) mergePushSecretData(remoteRef esv1.PushSecretData, remoteSecret, localSecret *v1.Secret) error {
  107. // apply secret type
  108. secretType := v1.SecretTypeOpaque
  109. if localSecret.Type != "" {
  110. secretType = localSecret.Type
  111. }
  112. remoteSecret.Type = secretType
  113. // merge secret data with existing secret data
  114. if remoteSecret.Data == nil {
  115. remoteSecret.Data = make(map[string][]byte)
  116. }
  117. pushMeta, err := metadata.ParseMetadataParameters[PushSecretMetadataSpec](remoteRef.GetMetadata())
  118. if err != nil {
  119. return fmt.Errorf("unable to parse metadata parameters: %w", err)
  120. }
  121. // merge metadata based on the policy
  122. var targetLabels, targetAnnotations map[string]string
  123. sourceLabels, sourceAnnotations, err := mergeSourceMetadata(localSecret, pushMeta)
  124. if err != nil {
  125. return fmt.Errorf("failed to merge source metadata: %w", err)
  126. }
  127. targetLabels, targetAnnotations, err = mergeTargetMetadata(remoteSecret, pushMeta, sourceLabels, sourceAnnotations)
  128. if err != nil {
  129. return fmt.Errorf("failed to merge target metadata: %w", err)
  130. }
  131. remoteSecret.ObjectMeta.Labels = targetLabels
  132. remoteSecret.ObjectMeta.Annotations = targetAnnotations
  133. if pushMeta != nil && pushMeta.Spec.RemoteNamespace != "" {
  134. remoteSecret.ObjectMeta.Namespace = pushMeta.Spec.RemoteNamespace
  135. }
  136. // case 1: push the whole secret
  137. if remoteRef.GetProperty() == "" {
  138. for k, v := range localSecret.Data {
  139. remoteSecret.Data[k] = v
  140. }
  141. return nil
  142. }
  143. // cases 2a + 2b: push into a property.
  144. // if secret key is empty, we will marshal the whole secret and put it into
  145. // the property defined in the remoteRef.
  146. if remoteRef.GetSecretKey() == "" {
  147. value, err := c.marshalData(localSecret)
  148. if err != nil {
  149. return err
  150. }
  151. remoteSecret.Data[remoteRef.GetProperty()] = value
  152. } else {
  153. // if secret key is defined, we will push that key from the local secret
  154. remoteSecret.Data[remoteRef.GetProperty()] = localSecret.Data[remoteRef.GetSecretKey()]
  155. }
  156. return nil
  157. }
  158. func (c *Client) createOrUpdate(ctx context.Context, targetSecret *v1.Secret, f func() error) error {
  159. target, err := c.userSecretClient.Get(ctx, targetSecret.Name, metav1.GetOptions{})
  160. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesGetSecret, err)
  161. if err != nil {
  162. if !apierrors.IsNotFound(err) {
  163. return err
  164. }
  165. if err := f(); err != nil {
  166. return err
  167. }
  168. _, err := c.userSecretClient.Create(ctx, targetSecret, metav1.CreateOptions{})
  169. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesCreateSecret, err)
  170. if err != nil {
  171. return err
  172. }
  173. return nil
  174. }
  175. *targetSecret = *target
  176. existing := targetSecret.DeepCopyObject()
  177. if err := f(); err != nil {
  178. return err
  179. }
  180. if equality.Semantic.DeepEqual(existing, targetSecret) {
  181. return nil
  182. }
  183. _, err = c.userSecretClient.Update(ctx, targetSecret, metav1.UpdateOptions{})
  184. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesUpdateSecret, err)
  185. if err != nil {
  186. return err
  187. }
  188. return nil
  189. }
  190. func (c *Client) marshalData(secret *v1.Secret) ([]byte, error) {
  191. values := make(map[string]string)
  192. for k, v := range secret.Data {
  193. values[k] = string(v)
  194. }
  195. // marshal
  196. value, err := utils.JSONMarshal(values)
  197. if err != nil {
  198. return nil, fmt.Errorf("failed to marshal secrets into a single property: %w", err)
  199. }
  200. return value, nil
  201. }
  202. func (c *Client) GetSecretMap(ctx context.Context, ref esv1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  203. secret, err := c.userSecretClient.Get(ctx, ref.Key, metav1.GetOptions{})
  204. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesGetSecret, err)
  205. if apierrors.IsNotFound(err) {
  206. return nil, esv1.NoSecretError{}
  207. }
  208. if err != nil {
  209. return nil, err
  210. }
  211. var tmpMap map[string][]byte
  212. if ref.MetadataPolicy == esv1.ExternalSecretMetadataPolicyFetch {
  213. tmpMap, err = getSecretMetadata(secret)
  214. if err != nil {
  215. return nil, err
  216. }
  217. } else {
  218. tmpMap = secret.Data
  219. }
  220. if ref.Property != "" {
  221. retMap, err := getPropertyMap(ref.Key, ref.Property, tmpMap)
  222. if err != nil {
  223. return nil, err
  224. }
  225. return retMap, nil
  226. }
  227. return tmpMap, nil
  228. }
  229. func getPropertyMap(key, property string, tmpMap map[string][]byte) (map[string][]byte, error) {
  230. byteArr, err := utils.JSONMarshal(tmpMap)
  231. if err != nil {
  232. return nil, err
  233. }
  234. var retMap map[string][]byte
  235. jsonStr := string(byteArr)
  236. // We need to search if a given key with a . exists before using gjson operations.
  237. idx := strings.Index(property, ".")
  238. if idx > -1 {
  239. refProperty := strings.ReplaceAll(property, ".", "\\.")
  240. retMap, err = getMapFromValues(refProperty, jsonStr)
  241. if err != nil {
  242. return nil, err
  243. }
  244. if retMap != nil {
  245. return retMap, nil
  246. }
  247. }
  248. retMap, err = getMapFromValues(property, jsonStr)
  249. if err != nil {
  250. return nil, err
  251. }
  252. if retMap == nil {
  253. return nil, fmt.Errorf("property %s does not exist in key %s", property, key)
  254. }
  255. return retMap, nil
  256. }
  257. func getMapFromValues(property, jsonStr string) (map[string][]byte, error) {
  258. val := gjson.Get(jsonStr, property)
  259. if val.Exists() {
  260. retMap := make(map[string][]byte)
  261. var tmpMap map[string]any
  262. decoded, err := base64.StdEncoding.DecodeString(val.String())
  263. if err != nil {
  264. return nil, err
  265. }
  266. err = json.Unmarshal(decoded, &tmpMap)
  267. if err != nil {
  268. return nil, err
  269. }
  270. for k, v := range tmpMap {
  271. b, err := utils.JSONMarshal(v)
  272. if err != nil {
  273. return nil, err
  274. }
  275. retMap[k] = b
  276. }
  277. return retMap, nil
  278. }
  279. return nil, nil
  280. }
  281. func getSecretMetadata(secret *v1.Secret) (map[string][]byte, error) {
  282. var err error
  283. tmpMap := make(map[string][]byte)
  284. tmpMap[metaLabels], err = utils.JSONMarshal(secret.ObjectMeta.Labels)
  285. if err != nil {
  286. return nil, err
  287. }
  288. tmpMap[metaAnnotations], err = utils.JSONMarshal(secret.ObjectMeta.Annotations)
  289. if err != nil {
  290. return nil, err
  291. }
  292. return tmpMap, nil
  293. }
  294. func (c *Client) GetAllSecrets(ctx context.Context, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
  295. if ref.Tags != nil {
  296. return c.findByTags(ctx, ref)
  297. }
  298. if ref.Name != nil {
  299. return c.findByName(ctx, ref)
  300. }
  301. return nil, fmt.Errorf("unexpected find operator: %#v", ref)
  302. }
  303. func (c *Client) findByTags(ctx context.Context, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
  304. // empty/nil tags = everything
  305. sel, err := labels.ValidatedSelectorFromSet(ref.Tags)
  306. if err != nil {
  307. return nil, fmt.Errorf("unable to validate selector tags: %w", err)
  308. }
  309. secrets, err := c.userSecretClient.List(ctx, metav1.ListOptions{LabelSelector: sel.String()})
  310. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesListSecrets, err)
  311. if err != nil {
  312. return nil, fmt.Errorf("unable to list secrets: %w", err)
  313. }
  314. data := make(map[string][]byte)
  315. for _, secret := range secrets.Items {
  316. jsonStr, err := utils.JSONMarshal(convertMap(secret.Data))
  317. if err != nil {
  318. return nil, err
  319. }
  320. data[secret.Name] = jsonStr
  321. }
  322. return utils.ConvertKeys(ref.ConversionStrategy, data)
  323. }
  324. func (c *Client) findByName(ctx context.Context, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
  325. secrets, err := c.userSecretClient.List(ctx, metav1.ListOptions{})
  326. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesListSecrets, err)
  327. if err != nil {
  328. return nil, fmt.Errorf("unable to list secrets: %w", err)
  329. }
  330. matcher, err := find.New(*ref.Name)
  331. if err != nil {
  332. return nil, err
  333. }
  334. data := make(map[string][]byte)
  335. for _, secret := range secrets.Items {
  336. if !matcher.MatchName(secret.Name) {
  337. continue
  338. }
  339. jsonStr, err := utils.JSONMarshal(convertMap(secret.Data))
  340. if err != nil {
  341. return nil, err
  342. }
  343. data[secret.Name] = jsonStr
  344. }
  345. return utils.ConvertKeys(ref.ConversionStrategy, data)
  346. }
  347. func (c *Client) Close(_ context.Context) error {
  348. return nil
  349. }
  350. func convertMap(in map[string][]byte) map[string]string {
  351. out := make(map[string]string)
  352. for k, v := range in {
  353. out[k] = string(v)
  354. }
  355. return out
  356. }
  357. // fullDelete removes remote secret completely.
  358. func (c *Client) fullDelete(ctx context.Context, secretName string) error {
  359. err := c.userSecretClient.Delete(ctx, secretName, metav1.DeleteOptions{})
  360. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesDeleteSecret, err)
  361. // gracefully return on not found
  362. if apierrors.IsNotFound(err) {
  363. return nil
  364. }
  365. return err
  366. }
  367. // removeProperty removes single data property from remote secret.
  368. func (c *Client) removeProperty(ctx context.Context, extSecret *v1.Secret, remoteRef esv1.PushSecretRemoteRef) error {
  369. delete(extSecret.Data, remoteRef.GetProperty())
  370. _, err := c.userSecretClient.Update(ctx, extSecret, metav1.UpdateOptions{})
  371. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesUpdateSecret, err)
  372. return err
  373. }
  374. func getSecret(secret *v1.Secret, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
  375. if ref.MetadataPolicy == esv1.ExternalSecretMetadataPolicyFetch {
  376. s, found, err := getFromSecretMetadata(secret, ref)
  377. if err != nil {
  378. return nil, err
  379. }
  380. if !found {
  381. return nil, fmt.Errorf("property %s does not exist in metadata of secret %q", ref.Property, ref.Key)
  382. }
  383. return s, nil
  384. }
  385. s, found := getFromSecretData(secret, ref)
  386. if !found {
  387. return nil, fmt.Errorf("property %s does not exist in data of secret %q", ref.Property, ref.Key)
  388. }
  389. return s, nil
  390. }
  391. func getFromSecretData(secret *v1.Secret, ref esv1.ExternalSecretDataRemoteRef) ([]byte, bool) {
  392. // Check if a property with "." exists first such as file.png
  393. v, ok := secret.Data[ref.Property]
  394. if ok {
  395. return v, true
  396. }
  397. idx := strings.Index(ref.Property, ".")
  398. if idx == -1 || idx == 0 || idx == len(ref.Property)-1 {
  399. return nil, false
  400. }
  401. v, ok = secret.Data[ref.Property[:idx]]
  402. if !ok {
  403. return nil, false
  404. }
  405. val := gjson.Get(string(v), ref.Property[idx+1:])
  406. if !val.Exists() {
  407. return nil, false
  408. }
  409. return []byte(val.String()), true
  410. }
  411. func getFromSecretMetadata(secret *v1.Secret, ref esv1.ExternalSecretDataRemoteRef) ([]byte, bool, error) {
  412. path := strings.Split(ref.Property, ".")
  413. var metadata map[string]string
  414. switch path[0] {
  415. case metaLabels:
  416. metadata = secret.Labels
  417. case metaAnnotations:
  418. metadata = secret.Annotations
  419. default:
  420. return nil, false, nil
  421. }
  422. if len(path) == 1 {
  423. j, err := utils.JSONMarshal(metadata)
  424. if err != nil {
  425. return nil, false, err
  426. }
  427. return j, true, nil
  428. }
  429. v, ok := metadata[path[1]]
  430. if !ok {
  431. return nil, false, nil
  432. }
  433. if len(path) == 2 {
  434. return []byte(v), true, nil
  435. }
  436. val := gjson.Get(v, strings.Join(path[2:], ""))
  437. if !val.Exists() {
  438. return nil, false, nil
  439. }
  440. return []byte(val.String()), true, nil
  441. }