client.go 14 KB

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