client.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  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. "bytes"
  15. "context"
  16. "encoding/base64"
  17. "encoding/json"
  18. "fmt"
  19. "strings"
  20. "github.com/tidwall/gjson"
  21. v1 "k8s.io/api/core/v1"
  22. apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  23. apierrors "k8s.io/apimachinery/pkg/api/errors"
  24. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  25. "k8s.io/apimachinery/pkg/labels"
  26. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  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. )
  32. const (
  33. metaLabels = "labels"
  34. metaAnnotations = "annotations"
  35. )
  36. func (c *Client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  37. secret, err := c.userSecretClient.Get(ctx, ref.Key, metav1.GetOptions{})
  38. if err != nil {
  39. return nil, err
  40. }
  41. // if property is not defined, we will return the json-serialized secret
  42. if ref.Property == "" {
  43. if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
  44. m := map[string]map[string]string{}
  45. m[metaLabels] = secret.Labels
  46. m[metaAnnotations] = secret.Annotations
  47. j, err := jsonMarshal(m)
  48. if err != nil {
  49. return nil, err
  50. }
  51. return j, nil
  52. }
  53. m := map[string]string{}
  54. for key, val := range secret.Data {
  55. m[key] = string(val)
  56. }
  57. j, err := jsonMarshal(m)
  58. if err != nil {
  59. return nil, err
  60. }
  61. return j, nil
  62. }
  63. return getSecret(secret, ref)
  64. }
  65. func jsonMarshal(t interface{}) ([]byte, error) {
  66. buffer := &bytes.Buffer{}
  67. encoder := json.NewEncoder(buffer)
  68. encoder.SetEscapeHTML(false)
  69. err := encoder.Encode(t)
  70. return bytes.TrimRight(buffer.Bytes(), "\n"), err
  71. }
  72. func (c *Client) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushRemoteRef) error {
  73. if remoteRef.GetProperty() == "" {
  74. return fmt.Errorf("requires property in RemoteRef to delete secret value")
  75. }
  76. extSecret, getErr := c.userSecretClient.Get(ctx, remoteRef.GetRemoteKey(), metav1.GetOptions{})
  77. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesGetSecret, getErr)
  78. if getErr != nil {
  79. if apierrors.IsNotFound(getErr) {
  80. // return gracefully if no secret exists
  81. return nil
  82. }
  83. return getErr
  84. }
  85. if _, ok := extSecret.Data[remoteRef.GetProperty()]; !ok {
  86. // return gracefully if specified secret does not contain the given property
  87. return nil
  88. }
  89. if len(extSecret.Data) > 1 {
  90. return c.removeProperty(ctx, extSecret, remoteRef)
  91. }
  92. return c.fullDelete(ctx, remoteRef.GetRemoteKey())
  93. }
  94. func (c *Client) PushSecret(ctx context.Context, value []byte, _ *apiextensionsv1.JSON, remoteRef esv1beta1.PushRemoteRef) error {
  95. if remoteRef.GetProperty() == "" {
  96. return fmt.Errorf("requires property in RemoteRef to push secret value")
  97. }
  98. extSecret, getErr := c.userSecretClient.Get(ctx, remoteRef.GetRemoteKey(), metav1.GetOptions{})
  99. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesGetSecret, getErr)
  100. if getErr != nil {
  101. // create if it not exists
  102. if apierrors.IsNotFound(getErr) {
  103. return c.createSecret(ctx, value, remoteRef)
  104. }
  105. return getErr
  106. }
  107. // return gracefully if data is already in sync
  108. if v, ok := extSecret.Data[remoteRef.GetProperty()]; ok && bytes.Equal(v, value) {
  109. return nil
  110. }
  111. // otherwise update remote property
  112. return c.updateProperty(ctx, extSecret, remoteRef, value)
  113. }
  114. func (c *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  115. secret, err := c.userSecretClient.Get(ctx, ref.Key, metav1.GetOptions{})
  116. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesGetSecret, err)
  117. if apierrors.IsNotFound(err) {
  118. return nil, esv1beta1.NoSecretError{}
  119. }
  120. if err != nil {
  121. return nil, err
  122. }
  123. var tmpMap map[string][]byte
  124. if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
  125. tmpMap, err = getSecretMetadata(secret)
  126. if err != nil {
  127. return nil, err
  128. }
  129. } else {
  130. tmpMap = secret.Data
  131. }
  132. if ref.Property != "" {
  133. retMap, err := getPropertyMap(ref.Key, ref.Property, tmpMap)
  134. if err != nil {
  135. return nil, err
  136. }
  137. return retMap, nil
  138. }
  139. return tmpMap, nil
  140. }
  141. func getPropertyMap(key, property string, tmpMap map[string][]byte) (map[string][]byte, error) {
  142. byteArr, err := jsonMarshal(tmpMap)
  143. if err != nil {
  144. return nil, err
  145. }
  146. var retMap map[string][]byte
  147. jsonStr := string(byteArr)
  148. // We need to search if a given key with a . exists before using gjson operations.
  149. idx := strings.Index(property, ".")
  150. if idx > -1 {
  151. refProperty := strings.ReplaceAll(property, ".", "\\.")
  152. retMap, err = getMapFromValues(refProperty, jsonStr)
  153. if err != nil {
  154. return nil, err
  155. }
  156. if retMap != nil {
  157. return retMap, nil
  158. }
  159. }
  160. retMap, err = getMapFromValues(property, jsonStr)
  161. if err != nil {
  162. return nil, err
  163. }
  164. if retMap == nil {
  165. return nil, fmt.Errorf("property %s does not exist in key %s", property, key)
  166. }
  167. return retMap, nil
  168. }
  169. func getMapFromValues(property, jsonStr string) (map[string][]byte, error) {
  170. val := gjson.Get(jsonStr, property)
  171. if val.Exists() {
  172. retMap := make(map[string][]byte)
  173. var tmpMap map[string]interface{}
  174. decoded, err := base64.StdEncoding.DecodeString(val.String())
  175. if err != nil {
  176. return nil, err
  177. }
  178. err = json.Unmarshal(decoded, &tmpMap)
  179. if err != nil {
  180. return nil, err
  181. }
  182. for k, v := range tmpMap {
  183. b, err := jsonMarshal(v)
  184. if err != nil {
  185. return nil, err
  186. }
  187. retMap[k] = b
  188. }
  189. return retMap, nil
  190. }
  191. return nil, nil
  192. }
  193. func getSecretMetadata(secret *v1.Secret) (map[string][]byte, error) {
  194. var err error
  195. tmpMap := make(map[string][]byte)
  196. tmpMap[metaLabels], err = jsonMarshal(secret.ObjectMeta.Labels)
  197. if err != nil {
  198. return nil, err
  199. }
  200. tmpMap[metaAnnotations], err = jsonMarshal(secret.ObjectMeta.Annotations)
  201. if err != nil {
  202. return nil, err
  203. }
  204. return tmpMap, nil
  205. }
  206. func (c *Client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  207. if ref.Tags != nil {
  208. return c.findByTags(ctx, ref)
  209. }
  210. if ref.Name != nil {
  211. return c.findByName(ctx, ref)
  212. }
  213. return nil, fmt.Errorf("unexpected find operator: %#v", ref)
  214. }
  215. func (c *Client) findByTags(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  216. // empty/nil tags = everything
  217. sel, err := labels.ValidatedSelectorFromSet(ref.Tags)
  218. if err != nil {
  219. return nil, fmt.Errorf("unable to validate selector tags: %w", err)
  220. }
  221. secrets, err := c.userSecretClient.List(ctx, metav1.ListOptions{LabelSelector: sel.String()})
  222. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesListSecrets, err)
  223. if err != nil {
  224. return nil, fmt.Errorf("unable to list secrets: %w", err)
  225. }
  226. data := make(map[string][]byte)
  227. for _, secret := range secrets.Items {
  228. jsonStr, err := jsonMarshal(convertMap(secret.Data))
  229. if err != nil {
  230. return nil, err
  231. }
  232. data[secret.Name] = jsonStr
  233. }
  234. return utils.ConvertKeys(ref.ConversionStrategy, data)
  235. }
  236. func (c *Client) findByName(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  237. secrets, err := c.userSecretClient.List(ctx, metav1.ListOptions{})
  238. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesListSecrets, err)
  239. if err != nil {
  240. return nil, fmt.Errorf("unable to list secrets: %w", err)
  241. }
  242. matcher, err := find.New(*ref.Name)
  243. if err != nil {
  244. return nil, err
  245. }
  246. data := make(map[string][]byte)
  247. for _, secret := range secrets.Items {
  248. if !matcher.MatchName(secret.Name) {
  249. continue
  250. }
  251. jsonStr, err := jsonMarshal(convertMap(secret.Data))
  252. if err != nil {
  253. return nil, err
  254. }
  255. data[secret.Name] = jsonStr
  256. }
  257. return utils.ConvertKeys(ref.ConversionStrategy, data)
  258. }
  259. func (c Client) Close(_ context.Context) error {
  260. return nil
  261. }
  262. func convertMap(in map[string][]byte) map[string]string {
  263. out := make(map[string]string)
  264. for k, v := range in {
  265. out[k] = string(v)
  266. }
  267. return out
  268. }
  269. func (c *Client) createSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
  270. s := v1.Secret{
  271. ObjectMeta: metav1.ObjectMeta{
  272. Name: remoteRef.GetRemoteKey(),
  273. Namespace: c.store.RemoteNamespace,
  274. },
  275. Data: map[string][]byte{remoteRef.GetProperty(): value},
  276. Type: "Opaque",
  277. }
  278. _, err := c.userSecretClient.Create(ctx, &s, metav1.CreateOptions{})
  279. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesCreateSecret, err)
  280. return err
  281. }
  282. // fullDelete removes remote secret completely.
  283. func (c *Client) fullDelete(ctx context.Context, secretName string) error {
  284. err := c.userSecretClient.Delete(ctx, secretName, metav1.DeleteOptions{})
  285. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesDeleteSecret, err)
  286. // gracefully return on not found
  287. if apierrors.IsNotFound(err) {
  288. return nil
  289. }
  290. return err
  291. }
  292. // removeProperty removes single data property from remote secret.
  293. func (c *Client) removeProperty(ctx context.Context, extSecret *v1.Secret, remoteRef esv1beta1.PushRemoteRef) error {
  294. delete(extSecret.Data, remoteRef.GetProperty())
  295. _, err := c.userSecretClient.Update(ctx, extSecret, metav1.UpdateOptions{})
  296. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesUpdateSecret, err)
  297. return err
  298. }
  299. func (c *Client) updateProperty(ctx context.Context, extSecret *v1.Secret, remoteRef esv1beta1.PushRemoteRef, value []byte) error {
  300. if extSecret.Data == nil {
  301. extSecret.Data = make(map[string][]byte)
  302. }
  303. // otherwise update remote secret
  304. extSecret.Data[remoteRef.GetProperty()] = value
  305. _, uErr := c.userSecretClient.Update(ctx, extSecret, metav1.UpdateOptions{})
  306. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesUpdateSecret, uErr)
  307. return uErr
  308. }
  309. func getSecret(secret *v1.Secret, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  310. if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
  311. s, found, err := getFromSecretMetadata(secret, ref)
  312. if err != nil {
  313. return nil, err
  314. }
  315. if !found {
  316. return nil, fmt.Errorf("property %s does not exist in metadata of secret %q", ref.Property, ref.Key)
  317. }
  318. return s, nil
  319. }
  320. s, found := getFromSecretData(secret, ref)
  321. if !found {
  322. return nil, fmt.Errorf("property %s does not exist in data of secret %q", ref.Property, ref.Key)
  323. }
  324. return s, nil
  325. }
  326. func getFromSecretData(secret *v1.Secret, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, bool) {
  327. // Check if a property with "." exists first such as file.png
  328. v, ok := secret.Data[ref.Property]
  329. if ok {
  330. return v, true
  331. }
  332. idx := strings.Index(ref.Property, ".")
  333. if idx == -1 || idx == 0 || idx == len(ref.Property)-1 {
  334. return nil, false
  335. }
  336. v, ok = secret.Data[ref.Property[:idx]]
  337. if !ok {
  338. return nil, false
  339. }
  340. val := gjson.Get(string(v), ref.Property[idx+1:])
  341. if !val.Exists() {
  342. return nil, false
  343. }
  344. return []byte(val.String()), true
  345. }
  346. func getFromSecretMetadata(secret *v1.Secret, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, bool, error) {
  347. path := strings.Split(ref.Property, ".")
  348. var metadata map[string]string
  349. switch path[0] {
  350. case metaLabels:
  351. metadata = secret.Labels
  352. case metaAnnotations:
  353. metadata = secret.Annotations
  354. default:
  355. return nil, false, nil
  356. }
  357. if len(path) == 1 {
  358. j, err := jsonMarshal(metadata)
  359. if err != nil {
  360. return nil, false, err
  361. }
  362. return j, true, nil
  363. }
  364. v, ok := metadata[path[1]]
  365. if !ok {
  366. return nil, false, nil
  367. }
  368. if len(path) == 2 {
  369. return []byte(v), true, nil
  370. }
  371. val := gjson.Get(v, strings.Join(path[2:], ""))
  372. if !val.Exists() {
  373. return nil, false, nil
  374. }
  375. return []byte(val.String()), true, nil
  376. }