client.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  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, typed v1.SecretType, _ *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. newType := v1.SecretTypeOpaque
  104. if typed != "" {
  105. newType = typed
  106. }
  107. return c.createSecret(ctx, value, newType, remoteRef)
  108. }
  109. return getErr
  110. }
  111. // return gracefully if data is already in sync
  112. if v, ok := extSecret.Data[remoteRef.GetProperty()]; ok && bytes.Equal(v, value) {
  113. return nil
  114. }
  115. // otherwise update remote property
  116. return c.updateProperty(ctx, extSecret, remoteRef, value)
  117. }
  118. func (c *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  119. secret, err := c.userSecretClient.Get(ctx, ref.Key, metav1.GetOptions{})
  120. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesGetSecret, err)
  121. if apierrors.IsNotFound(err) {
  122. return nil, esv1beta1.NoSecretError{}
  123. }
  124. if err != nil {
  125. return nil, err
  126. }
  127. var tmpMap map[string][]byte
  128. if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
  129. tmpMap, err = getSecretMetadata(secret)
  130. if err != nil {
  131. return nil, err
  132. }
  133. } else {
  134. tmpMap = secret.Data
  135. }
  136. if ref.Property != "" {
  137. retMap, err := getPropertyMap(ref.Key, ref.Property, tmpMap)
  138. if err != nil {
  139. return nil, err
  140. }
  141. return retMap, nil
  142. }
  143. return tmpMap, nil
  144. }
  145. func getPropertyMap(key, property string, tmpMap map[string][]byte) (map[string][]byte, error) {
  146. byteArr, err := jsonMarshal(tmpMap)
  147. if err != nil {
  148. return nil, err
  149. }
  150. var retMap map[string][]byte
  151. jsonStr := string(byteArr)
  152. // We need to search if a given key with a . exists before using gjson operations.
  153. idx := strings.Index(property, ".")
  154. if idx > -1 {
  155. refProperty := strings.ReplaceAll(property, ".", "\\.")
  156. retMap, err = getMapFromValues(refProperty, jsonStr)
  157. if err != nil {
  158. return nil, err
  159. }
  160. if retMap != nil {
  161. return retMap, nil
  162. }
  163. }
  164. retMap, err = getMapFromValues(property, jsonStr)
  165. if err != nil {
  166. return nil, err
  167. }
  168. if retMap == nil {
  169. return nil, fmt.Errorf("property %s does not exist in key %s", property, key)
  170. }
  171. return retMap, nil
  172. }
  173. func getMapFromValues(property, jsonStr string) (map[string][]byte, error) {
  174. val := gjson.Get(jsonStr, property)
  175. if val.Exists() {
  176. retMap := make(map[string][]byte)
  177. var tmpMap map[string]interface{}
  178. decoded, err := base64.StdEncoding.DecodeString(val.String())
  179. if err != nil {
  180. return nil, err
  181. }
  182. err = json.Unmarshal(decoded, &tmpMap)
  183. if err != nil {
  184. return nil, err
  185. }
  186. for k, v := range tmpMap {
  187. b, err := jsonMarshal(v)
  188. if err != nil {
  189. return nil, err
  190. }
  191. retMap[k] = b
  192. }
  193. return retMap, nil
  194. }
  195. return nil, nil
  196. }
  197. func getSecretMetadata(secret *v1.Secret) (map[string][]byte, error) {
  198. var err error
  199. tmpMap := make(map[string][]byte)
  200. tmpMap[metaLabels], err = jsonMarshal(secret.ObjectMeta.Labels)
  201. if err != nil {
  202. return nil, err
  203. }
  204. tmpMap[metaAnnotations], err = jsonMarshal(secret.ObjectMeta.Annotations)
  205. if err != nil {
  206. return nil, err
  207. }
  208. return tmpMap, nil
  209. }
  210. func (c *Client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  211. if ref.Tags != nil {
  212. return c.findByTags(ctx, ref)
  213. }
  214. if ref.Name != nil {
  215. return c.findByName(ctx, ref)
  216. }
  217. return nil, fmt.Errorf("unexpected find operator: %#v", ref)
  218. }
  219. func (c *Client) findByTags(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  220. // empty/nil tags = everything
  221. sel, err := labels.ValidatedSelectorFromSet(ref.Tags)
  222. if err != nil {
  223. return nil, fmt.Errorf("unable to validate selector tags: %w", err)
  224. }
  225. secrets, err := c.userSecretClient.List(ctx, metav1.ListOptions{LabelSelector: sel.String()})
  226. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesListSecrets, err)
  227. if err != nil {
  228. return nil, fmt.Errorf("unable to list secrets: %w", err)
  229. }
  230. data := make(map[string][]byte)
  231. for _, secret := range secrets.Items {
  232. jsonStr, err := jsonMarshal(convertMap(secret.Data))
  233. if err != nil {
  234. return nil, err
  235. }
  236. data[secret.Name] = jsonStr
  237. }
  238. return utils.ConvertKeys(ref.ConversionStrategy, data)
  239. }
  240. func (c *Client) findByName(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  241. secrets, err := c.userSecretClient.List(ctx, metav1.ListOptions{})
  242. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesListSecrets, err)
  243. if err != nil {
  244. return nil, fmt.Errorf("unable to list secrets: %w", err)
  245. }
  246. matcher, err := find.New(*ref.Name)
  247. if err != nil {
  248. return nil, err
  249. }
  250. data := make(map[string][]byte)
  251. for _, secret := range secrets.Items {
  252. if !matcher.MatchName(secret.Name) {
  253. continue
  254. }
  255. jsonStr, err := jsonMarshal(convertMap(secret.Data))
  256. if err != nil {
  257. return nil, err
  258. }
  259. data[secret.Name] = jsonStr
  260. }
  261. return utils.ConvertKeys(ref.ConversionStrategy, data)
  262. }
  263. func (c Client) Close(_ context.Context) error {
  264. return nil
  265. }
  266. func convertMap(in map[string][]byte) map[string]string {
  267. out := make(map[string]string)
  268. for k, v := range in {
  269. out[k] = string(v)
  270. }
  271. return out
  272. }
  273. func (c *Client) createSecret(ctx context.Context, value []byte, typed v1.SecretType, remoteRef esv1beta1.PushRemoteRef) error {
  274. s := v1.Secret{
  275. ObjectMeta: metav1.ObjectMeta{
  276. Name: remoteRef.GetRemoteKey(),
  277. Namespace: c.store.RemoteNamespace,
  278. },
  279. Data: map[string][]byte{remoteRef.GetProperty(): value},
  280. Type: typed,
  281. }
  282. _, err := c.userSecretClient.Create(ctx, &s, metav1.CreateOptions{})
  283. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesCreateSecret, err)
  284. return err
  285. }
  286. // fullDelete removes remote secret completely.
  287. func (c *Client) fullDelete(ctx context.Context, secretName string) error {
  288. err := c.userSecretClient.Delete(ctx, secretName, metav1.DeleteOptions{})
  289. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesDeleteSecret, err)
  290. // gracefully return on not found
  291. if apierrors.IsNotFound(err) {
  292. return nil
  293. }
  294. return err
  295. }
  296. // removeProperty removes single data property from remote secret.
  297. func (c *Client) removeProperty(ctx context.Context, extSecret *v1.Secret, remoteRef esv1beta1.PushRemoteRef) error {
  298. delete(extSecret.Data, remoteRef.GetProperty())
  299. _, err := c.userSecretClient.Update(ctx, extSecret, metav1.UpdateOptions{})
  300. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesUpdateSecret, err)
  301. return err
  302. }
  303. func (c *Client) updateProperty(ctx context.Context, extSecret *v1.Secret, remoteRef esv1beta1.PushRemoteRef, value []byte) error {
  304. if extSecret.Data == nil {
  305. extSecret.Data = make(map[string][]byte)
  306. }
  307. // otherwise update remote secret
  308. extSecret.Data[remoteRef.GetProperty()] = value
  309. _, uErr := c.userSecretClient.Update(ctx, extSecret, metav1.UpdateOptions{})
  310. metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesUpdateSecret, uErr)
  311. return uErr
  312. }
  313. func getSecret(secret *v1.Secret, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  314. if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
  315. s, found, err := getFromSecretMetadata(secret, ref)
  316. if err != nil {
  317. return nil, err
  318. }
  319. if !found {
  320. return nil, fmt.Errorf("property %s does not exist in metadata of secret %q", ref.Property, ref.Key)
  321. }
  322. return s, nil
  323. }
  324. s, found := getFromSecretData(secret, ref)
  325. if !found {
  326. return nil, fmt.Errorf("property %s does not exist in data of secret %q", ref.Property, ref.Key)
  327. }
  328. return s, nil
  329. }
  330. func getFromSecretData(secret *v1.Secret, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, bool) {
  331. // Check if a property with "." exists first such as file.png
  332. v, ok := secret.Data[ref.Property]
  333. if ok {
  334. return v, true
  335. }
  336. idx := strings.Index(ref.Property, ".")
  337. if idx == -1 || idx == 0 || idx == len(ref.Property)-1 {
  338. return nil, false
  339. }
  340. v, ok = secret.Data[ref.Property[:idx]]
  341. if !ok {
  342. return nil, false
  343. }
  344. val := gjson.Get(string(v), ref.Property[idx+1:])
  345. if !val.Exists() {
  346. return nil, false
  347. }
  348. return []byte(val.String()), true
  349. }
  350. func getFromSecretMetadata(secret *v1.Secret, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, bool, error) {
  351. path := strings.Split(ref.Property, ".")
  352. var metadata map[string]string
  353. switch path[0] {
  354. case metaLabels:
  355. metadata = secret.Labels
  356. case metaAnnotations:
  357. metadata = secret.Annotations
  358. default:
  359. return nil, false, nil
  360. }
  361. if len(path) == 1 {
  362. j, err := jsonMarshal(metadata)
  363. if err != nil {
  364. return nil, false, err
  365. }
  366. return j, true, nil
  367. }
  368. v, ok := metadata[path[1]]
  369. if !ok {
  370. return nil, false, nil
  371. }
  372. if len(path) == 2 {
  373. return []byte(v), true, nil
  374. }
  375. val := gjson.Get(v, strings.Join(path[2:], ""))
  376. if !val.Exists() {
  377. return nil, false, nil
  378. }
  379. return []byte(val.String()), true, nil
  380. }