client.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  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. // case 1: push the whole secret
  133. if remoteRef.GetProperty() == "" {
  134. for k, v := range localSecret.Data {
  135. remoteSecret.Data[k] = v
  136. }
  137. return nil
  138. }
  139. // cases 2a + 2b: push into a property.
  140. // if secret key is empty, we will marshal the whole secret and put it into
  141. // the property defined in the remoteRef.
  142. if remoteRef.GetSecretKey() == "" {
  143. value, err := c.marshalData(localSecret)
  144. if err != nil {
  145. return err
  146. }
  147. remoteSecret.Data[remoteRef.GetProperty()] = value
  148. } else {
  149. // if secret key is defined, we will push that key from the local secret
  150. remoteSecret.Data[remoteRef.GetProperty()] = localSecret.Data[remoteRef.GetSecretKey()]
  151. }
  152. return nil
  153. }
  154. func (c *Client) createOrUpdate(ctx context.Context, targetSecret *v1.Secret, f func() error) error {
  155. target, err := c.userSecretClient.Get(ctx, targetSecret.Name, metav1.GetOptions{})
  156. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesGetSecret, err)
  157. if err != nil {
  158. if !apierrors.IsNotFound(err) {
  159. return err
  160. }
  161. if err := f(); err != nil {
  162. return err
  163. }
  164. _, err := c.userSecretClient.Create(ctx, targetSecret, metav1.CreateOptions{})
  165. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesCreateSecret, err)
  166. if err != nil {
  167. return err
  168. }
  169. return nil
  170. }
  171. *targetSecret = *target
  172. existing := targetSecret.DeepCopyObject()
  173. if err := f(); err != nil {
  174. return err
  175. }
  176. if equality.Semantic.DeepEqual(existing, targetSecret) {
  177. return nil
  178. }
  179. _, err = c.userSecretClient.Update(ctx, targetSecret, metav1.UpdateOptions{})
  180. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesUpdateSecret, err)
  181. if err != nil {
  182. return err
  183. }
  184. return nil
  185. }
  186. func (c *Client) marshalData(secret *v1.Secret) ([]byte, error) {
  187. values := make(map[string]string)
  188. for k, v := range secret.Data {
  189. values[k] = string(v)
  190. }
  191. // marshal
  192. value, err := utils.JSONMarshal(values)
  193. if err != nil {
  194. return nil, fmt.Errorf("failed to marshal secrets into a single property: %w", err)
  195. }
  196. return value, nil
  197. }
  198. func (c *Client) GetSecretMap(ctx context.Context, ref esv1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  199. secret, err := c.userSecretClient.Get(ctx, ref.Key, metav1.GetOptions{})
  200. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesGetSecret, err)
  201. if apierrors.IsNotFound(err) {
  202. return nil, esv1.NoSecretError{}
  203. }
  204. if err != nil {
  205. return nil, err
  206. }
  207. var tmpMap map[string][]byte
  208. if ref.MetadataPolicy == esv1.ExternalSecretMetadataPolicyFetch {
  209. tmpMap, err = getSecretMetadata(secret)
  210. if err != nil {
  211. return nil, err
  212. }
  213. } else {
  214. tmpMap = secret.Data
  215. }
  216. if ref.Property != "" {
  217. retMap, err := getPropertyMap(ref.Key, ref.Property, tmpMap)
  218. if err != nil {
  219. return nil, err
  220. }
  221. return retMap, nil
  222. }
  223. return tmpMap, nil
  224. }
  225. func getPropertyMap(key, property string, tmpMap map[string][]byte) (map[string][]byte, error) {
  226. byteArr, err := utils.JSONMarshal(tmpMap)
  227. if err != nil {
  228. return nil, err
  229. }
  230. var retMap map[string][]byte
  231. jsonStr := string(byteArr)
  232. // We need to search if a given key with a . exists before using gjson operations.
  233. idx := strings.Index(property, ".")
  234. if idx > -1 {
  235. refProperty := strings.ReplaceAll(property, ".", "\\.")
  236. retMap, err = getMapFromValues(refProperty, jsonStr)
  237. if err != nil {
  238. return nil, err
  239. }
  240. if retMap != nil {
  241. return retMap, nil
  242. }
  243. }
  244. retMap, err = getMapFromValues(property, jsonStr)
  245. if err != nil {
  246. return nil, err
  247. }
  248. if retMap == nil {
  249. return nil, fmt.Errorf("property %s does not exist in key %s", property, key)
  250. }
  251. return retMap, nil
  252. }
  253. func getMapFromValues(property, jsonStr string) (map[string][]byte, error) {
  254. val := gjson.Get(jsonStr, property)
  255. if val.Exists() {
  256. retMap := make(map[string][]byte)
  257. var tmpMap map[string]any
  258. decoded, err := base64.StdEncoding.DecodeString(val.String())
  259. if err != nil {
  260. return nil, err
  261. }
  262. err = json.Unmarshal(decoded, &tmpMap)
  263. if err != nil {
  264. return nil, err
  265. }
  266. for k, v := range tmpMap {
  267. b, err := utils.JSONMarshal(v)
  268. if err != nil {
  269. return nil, err
  270. }
  271. retMap[k] = b
  272. }
  273. return retMap, nil
  274. }
  275. return nil, nil
  276. }
  277. func getSecretMetadata(secret *v1.Secret) (map[string][]byte, error) {
  278. var err error
  279. tmpMap := make(map[string][]byte)
  280. tmpMap[metaLabels], err = utils.JSONMarshal(secret.ObjectMeta.Labels)
  281. if err != nil {
  282. return nil, err
  283. }
  284. tmpMap[metaAnnotations], err = utils.JSONMarshal(secret.ObjectMeta.Annotations)
  285. if err != nil {
  286. return nil, err
  287. }
  288. return tmpMap, nil
  289. }
  290. func (c *Client) GetAllSecrets(ctx context.Context, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
  291. if ref.Tags != nil {
  292. return c.findByTags(ctx, ref)
  293. }
  294. if ref.Name != nil {
  295. return c.findByName(ctx, ref)
  296. }
  297. return nil, fmt.Errorf("unexpected find operator: %#v", ref)
  298. }
  299. func (c *Client) findByTags(ctx context.Context, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
  300. // empty/nil tags = everything
  301. sel, err := labels.ValidatedSelectorFromSet(ref.Tags)
  302. if err != nil {
  303. return nil, fmt.Errorf("unable to validate selector tags: %w", err)
  304. }
  305. secrets, err := c.userSecretClient.List(ctx, metav1.ListOptions{LabelSelector: sel.String()})
  306. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesListSecrets, err)
  307. if err != nil {
  308. return nil, fmt.Errorf("unable to list secrets: %w", err)
  309. }
  310. data := make(map[string][]byte)
  311. for _, secret := range secrets.Items {
  312. jsonStr, err := utils.JSONMarshal(convertMap(secret.Data))
  313. if err != nil {
  314. return nil, err
  315. }
  316. data[secret.Name] = jsonStr
  317. }
  318. return utils.ConvertKeys(ref.ConversionStrategy, data)
  319. }
  320. func (c *Client) findByName(ctx context.Context, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
  321. secrets, err := c.userSecretClient.List(ctx, metav1.ListOptions{})
  322. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesListSecrets, err)
  323. if err != nil {
  324. return nil, fmt.Errorf("unable to list secrets: %w", err)
  325. }
  326. matcher, err := find.New(*ref.Name)
  327. if err != nil {
  328. return nil, err
  329. }
  330. data := make(map[string][]byte)
  331. for _, secret := range secrets.Items {
  332. if !matcher.MatchName(secret.Name) {
  333. continue
  334. }
  335. jsonStr, err := utils.JSONMarshal(convertMap(secret.Data))
  336. if err != nil {
  337. return nil, err
  338. }
  339. data[secret.Name] = jsonStr
  340. }
  341. return utils.ConvertKeys(ref.ConversionStrategy, data)
  342. }
  343. func (c *Client) Close(_ context.Context) error {
  344. return nil
  345. }
  346. func convertMap(in map[string][]byte) map[string]string {
  347. out := make(map[string]string)
  348. for k, v := range in {
  349. out[k] = string(v)
  350. }
  351. return out
  352. }
  353. // fullDelete removes remote secret completely.
  354. func (c *Client) fullDelete(ctx context.Context, secretName string) error {
  355. err := c.userSecretClient.Delete(ctx, secretName, metav1.DeleteOptions{})
  356. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesDeleteSecret, err)
  357. // gracefully return on not found
  358. if apierrors.IsNotFound(err) {
  359. return nil
  360. }
  361. return err
  362. }
  363. // removeProperty removes single data property from remote secret.
  364. func (c *Client) removeProperty(ctx context.Context, extSecret *v1.Secret, remoteRef esv1.PushSecretRemoteRef) error {
  365. delete(extSecret.Data, remoteRef.GetProperty())
  366. _, err := c.userSecretClient.Update(ctx, extSecret, metav1.UpdateOptions{})
  367. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesUpdateSecret, err)
  368. return err
  369. }
  370. func getSecret(secret *v1.Secret, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
  371. if ref.MetadataPolicy == esv1.ExternalSecretMetadataPolicyFetch {
  372. s, found, err := getFromSecretMetadata(secret, ref)
  373. if err != nil {
  374. return nil, err
  375. }
  376. if !found {
  377. return nil, fmt.Errorf("property %s does not exist in metadata of secret %q", ref.Property, ref.Key)
  378. }
  379. return s, nil
  380. }
  381. s, found := getFromSecretData(secret, ref)
  382. if !found {
  383. return nil, fmt.Errorf("property %s does not exist in data of secret %q", ref.Property, ref.Key)
  384. }
  385. return s, nil
  386. }
  387. func getFromSecretData(secret *v1.Secret, ref esv1.ExternalSecretDataRemoteRef) ([]byte, bool) {
  388. // Check if a property with "." exists first such as file.png
  389. v, ok := secret.Data[ref.Property]
  390. if ok {
  391. return v, true
  392. }
  393. idx := strings.Index(ref.Property, ".")
  394. if idx == -1 || idx == 0 || idx == len(ref.Property)-1 {
  395. return nil, false
  396. }
  397. v, ok = secret.Data[ref.Property[:idx]]
  398. if !ok {
  399. return nil, false
  400. }
  401. val := gjson.Get(string(v), ref.Property[idx+1:])
  402. if !val.Exists() {
  403. return nil, false
  404. }
  405. return []byte(val.String()), true
  406. }
  407. func getFromSecretMetadata(secret *v1.Secret, ref esv1.ExternalSecretDataRemoteRef) ([]byte, bool, error) {
  408. path := strings.Split(ref.Property, ".")
  409. var metadata map[string]string
  410. switch path[0] {
  411. case metaLabels:
  412. metadata = secret.Labels
  413. case metaAnnotations:
  414. metadata = secret.Annotations
  415. default:
  416. return nil, false, nil
  417. }
  418. if len(path) == 1 {
  419. j, err := utils.JSONMarshal(metadata)
  420. if err != nil {
  421. return nil, false, err
  422. }
  423. return j, true, nil
  424. }
  425. v, ok := metadata[path[1]]
  426. if !ok {
  427. return nil, false, nil
  428. }
  429. if len(path) == 2 {
  430. return []byte(v), true, nil
  431. }
  432. val := gjson.Get(v, strings.Join(path[2:], ""))
  433. if !val.Exists() {
  434. return nil, false, nil
  435. }
  436. return []byte(val.String()), true, nil
  437. }