lockbox_test.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  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 lockbox
  13. import (
  14. "context"
  15. b64 "encoding/base64"
  16. "encoding/json"
  17. "testing"
  18. "time"
  19. "github.com/google/uuid"
  20. tassert "github.com/stretchr/testify/assert"
  21. "github.com/yandex-cloud/go-genproto/yandex/cloud/lockbox/v1"
  22. "github.com/yandex-cloud/go-sdk/iamkey"
  23. corev1 "k8s.io/api/core/v1"
  24. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  25. ctrl "sigs.k8s.io/controller-runtime"
  26. k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
  27. clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
  28. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  29. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  30. "github.com/external-secrets/external-secrets/pkg/provider/yandex/common"
  31. "github.com/external-secrets/external-secrets/pkg/provider/yandex/common/clock"
  32. "github.com/external-secrets/external-secrets/pkg/provider/yandex/lockbox/client"
  33. )
  34. const (
  35. errMissingKey = "invalid Yandex Lockbox SecretStore resource: missing AuthorizedKey Name"
  36. errSecretPayloadPermissionDenied = "unable to request secret payload to get secret: permission denied"
  37. errSecretPayloadNotFound = "unable to request secret payload to get secret: secret not found"
  38. )
  39. func TestNewClient(t *testing.T) {
  40. ctx := context.Background()
  41. const namespace = "namespace"
  42. store := &esv1beta1.SecretStore{
  43. ObjectMeta: metav1.ObjectMeta{
  44. Namespace: namespace,
  45. },
  46. Spec: esv1beta1.SecretStoreSpec{
  47. Provider: &esv1beta1.SecretStoreProvider{
  48. YandexLockbox: &esv1beta1.YandexLockboxProvider{},
  49. },
  50. },
  51. }
  52. provider, err := esv1beta1.GetProvider(store)
  53. tassert.Nil(t, err)
  54. k8sClient := clientfake.NewClientBuilder().Build()
  55. secretClient, err := provider.NewClient(context.Background(), store, k8sClient, namespace)
  56. tassert.EqualError(t, err, errMissingKey)
  57. tassert.Nil(t, secretClient)
  58. store.Spec.Provider.YandexLockbox.Auth = esv1beta1.YandexLockboxAuth{}
  59. secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
  60. tassert.EqualError(t, err, errMissingKey)
  61. tassert.Nil(t, secretClient)
  62. store.Spec.Provider.YandexLockbox.Auth.AuthorizedKey = esmeta.SecretKeySelector{}
  63. secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
  64. tassert.EqualError(t, err, errMissingKey)
  65. tassert.Nil(t, secretClient)
  66. const authorizedKeySecretName = "authorizedKeySecretName"
  67. const authorizedKeySecretKey = "authorizedKeySecretKey"
  68. store.Spec.Provider.YandexLockbox.Auth.AuthorizedKey.Name = authorizedKeySecretName
  69. store.Spec.Provider.YandexLockbox.Auth.AuthorizedKey.Key = authorizedKeySecretKey
  70. secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
  71. tassert.EqualError(t, err, "cannot get Kubernetes secret \"authorizedKeySecretName\" from namespace \"namespace\": secrets \"authorizedKeySecretName\" not found")
  72. tassert.Nil(t, secretClient)
  73. err = createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, newFakeAuthorizedKey()))
  74. tassert.Nil(t, err)
  75. const caCertificateSecretName = "caCertificateSecretName"
  76. const caCertificateSecretKey = "caCertificateSecretKey"
  77. store.Spec.Provider.YandexLockbox.CAProvider = &esv1beta1.YandexLockboxCAProvider{
  78. Certificate: esmeta.SecretKeySelector{
  79. Key: caCertificateSecretKey,
  80. Name: caCertificateSecretName,
  81. },
  82. }
  83. secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
  84. tassert.EqualError(t, err, "cannot get Kubernetes secret \"caCertificateSecretName\" from namespace \"namespace\": secrets \"caCertificateSecretName\" not found")
  85. tassert.Nil(t, secretClient)
  86. err = createK8sSecret(ctx, t, k8sClient, namespace, caCertificateSecretName, caCertificateSecretKey, []byte("it-is-not-a-certificate"))
  87. tassert.Nil(t, err)
  88. secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
  89. tassert.EqualError(t, err, "failed to create Yandex.Cloud client: unable to read trusted CA certificates")
  90. tassert.Nil(t, secretClient)
  91. }
  92. func TestGetSecretForAllEntries(t *testing.T) {
  93. ctx := context.Background()
  94. namespace := uuid.NewString()
  95. authorizedKey := newFakeAuthorizedKey()
  96. fakeClock := clock.NewFakeClock()
  97. fakeLockboxServer := client.NewFakeLockboxServer(fakeClock, time.Hour)
  98. k1, v1 := "k1", "v1"
  99. k2, v2 := "k2", []byte("v2")
  100. secretID, _ := fakeLockboxServer.CreateSecret(authorizedKey,
  101. textEntry(k1, v1),
  102. binaryEntry(k2, v2),
  103. )
  104. k8sClient := clientfake.NewClientBuilder().Build()
  105. const authorizedKeySecretName = "authorizedKeySecretName"
  106. const authorizedKeySecretKey = "authorizedKeySecretKey"
  107. err := createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, authorizedKey))
  108. tassert.Nil(t, err)
  109. store := newYandexLockboxSecretStore("", namespace, authorizedKeySecretName, authorizedKeySecretKey)
  110. provider := newLockboxProvider(fakeClock, fakeLockboxServer)
  111. secretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
  112. tassert.Nil(t, err)
  113. data, err := secretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID})
  114. tassert.Nil(t, err)
  115. tassert.Equal(
  116. t,
  117. map[string]string{
  118. k1: v1,
  119. k2: base64(v2),
  120. },
  121. unmarshalStringMap(t, data),
  122. )
  123. }
  124. func TestGetSecretForTextEntry(t *testing.T) {
  125. ctx := context.Background()
  126. namespace := uuid.NewString()
  127. authorizedKey := newFakeAuthorizedKey()
  128. fakeClock := clock.NewFakeClock()
  129. fakeLockboxServer := client.NewFakeLockboxServer(fakeClock, time.Hour)
  130. k1, v1 := "k1", "v1"
  131. k2, v2 := "k2", []byte("v2")
  132. secretID, _ := fakeLockboxServer.CreateSecret(authorizedKey,
  133. textEntry(k1, v1),
  134. binaryEntry(k2, v2),
  135. )
  136. k8sClient := clientfake.NewClientBuilder().Build()
  137. const authorizedKeySecretName = "authorizedKeySecretName"
  138. const authorizedKeySecretKey = "authorizedKeySecretKey"
  139. err := createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, authorizedKey))
  140. tassert.Nil(t, err)
  141. store := newYandexLockboxSecretStore("", namespace, authorizedKeySecretName, authorizedKeySecretKey)
  142. provider := newLockboxProvider(fakeClock, fakeLockboxServer)
  143. secretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
  144. tassert.Nil(t, err)
  145. data, err := secretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID, Property: k1})
  146. tassert.Nil(t, err)
  147. tassert.Equal(t, v1, string(data))
  148. }
  149. func TestGetSecretForBinaryEntry(t *testing.T) {
  150. ctx := context.Background()
  151. namespace := uuid.NewString()
  152. authorizedKey := newFakeAuthorizedKey()
  153. fakeClock := clock.NewFakeClock()
  154. fakeLockboxServer := client.NewFakeLockboxServer(fakeClock, time.Hour)
  155. k1, v1 := "k1", "v1"
  156. k2, v2 := "k2", []byte("v2")
  157. secretID, _ := fakeLockboxServer.CreateSecret(authorizedKey,
  158. textEntry(k1, v1),
  159. binaryEntry(k2, v2),
  160. )
  161. k8sClient := clientfake.NewClientBuilder().Build()
  162. const authorizedKeySecretName = "authorizedKeySecretName"
  163. const authorizedKeySecretKey = "authorizedKeySecretKey"
  164. err := createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, authorizedKey))
  165. tassert.Nil(t, err)
  166. store := newYandexLockboxSecretStore("", namespace, authorizedKeySecretName, authorizedKeySecretKey)
  167. provider := newLockboxProvider(fakeClock, fakeLockboxServer)
  168. secretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
  169. tassert.Nil(t, err)
  170. data, err := secretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID, Property: k2})
  171. tassert.Nil(t, err)
  172. tassert.Equal(t, v2, data)
  173. }
  174. func TestGetSecretByVersionID(t *testing.T) {
  175. ctx := context.Background()
  176. namespace := uuid.NewString()
  177. authorizedKey := newFakeAuthorizedKey()
  178. fakeClock := clock.NewFakeClock()
  179. fakeLockboxServer := client.NewFakeLockboxServer(fakeClock, time.Hour)
  180. oldKey, oldVal := "oldKey", "oldVal"
  181. secretID, oldVersionID := fakeLockboxServer.CreateSecret(authorizedKey,
  182. textEntry(oldKey, oldVal),
  183. )
  184. k8sClient := clientfake.NewClientBuilder().Build()
  185. const authorizedKeySecretName = "authorizedKeySecretName"
  186. const authorizedKeySecretKey = "authorizedKeySecretKey"
  187. err := createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, authorizedKey))
  188. tassert.Nil(t, err)
  189. store := newYandexLockboxSecretStore("", namespace, authorizedKeySecretName, authorizedKeySecretKey)
  190. provider := newLockboxProvider(fakeClock, fakeLockboxServer)
  191. secretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
  192. tassert.Nil(t, err)
  193. data, err := secretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID, Version: oldVersionID})
  194. tassert.Nil(t, err)
  195. tassert.Equal(t, map[string]string{oldKey: oldVal}, unmarshalStringMap(t, data))
  196. newKey, newVal := "newKey", "newVal"
  197. newVersionID := fakeLockboxServer.AddVersion(secretID,
  198. textEntry(newKey, newVal),
  199. )
  200. data, err = secretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID, Version: oldVersionID})
  201. tassert.Nil(t, err)
  202. tassert.Equal(t, map[string]string{oldKey: oldVal}, unmarshalStringMap(t, data))
  203. data, err = secretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID, Version: newVersionID})
  204. tassert.Nil(t, err)
  205. tassert.Equal(t, map[string]string{newKey: newVal}, unmarshalStringMap(t, data))
  206. }
  207. func TestGetSecretUnauthorized(t *testing.T) {
  208. ctx := context.Background()
  209. namespace := uuid.NewString()
  210. authorizedKeyA := newFakeAuthorizedKey()
  211. authorizedKeyB := newFakeAuthorizedKey()
  212. fakeClock := clock.NewFakeClock()
  213. fakeLockboxServer := client.NewFakeLockboxServer(fakeClock, time.Hour)
  214. secretID, _ := fakeLockboxServer.CreateSecret(authorizedKeyA,
  215. textEntry("k1", "v1"),
  216. )
  217. k8sClient := clientfake.NewClientBuilder().Build()
  218. const authorizedKeySecretName = "authorizedKeySecretName"
  219. const authorizedKeySecretKey = "authorizedKeySecretKey"
  220. err := createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, authorizedKeyB))
  221. tassert.Nil(t, err)
  222. store := newYandexLockboxSecretStore("", namespace, authorizedKeySecretName, authorizedKeySecretKey)
  223. provider := newLockboxProvider(fakeClock, fakeLockboxServer)
  224. secretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
  225. tassert.Nil(t, err)
  226. _, err = secretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID})
  227. tassert.EqualError(t, err, errSecretPayloadPermissionDenied)
  228. }
  229. func TestGetSecretNotFound(t *testing.T) {
  230. ctx := context.Background()
  231. namespace := uuid.NewString()
  232. authorizedKey := newFakeAuthorizedKey()
  233. fakeClock := clock.NewFakeClock()
  234. fakeLockboxServer := client.NewFakeLockboxServer(fakeClock, time.Hour)
  235. k8sClient := clientfake.NewClientBuilder().Build()
  236. const authorizedKeySecretName = "authorizedKeySecretName"
  237. const authorizedKeySecretKey = "authorizedKeySecretKey"
  238. err := createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, authorizedKey))
  239. tassert.Nil(t, err)
  240. store := newYandexLockboxSecretStore("", namespace, authorizedKeySecretName, authorizedKeySecretKey)
  241. provider := newLockboxProvider(fakeClock, fakeLockboxServer)
  242. secretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
  243. tassert.Nil(t, err)
  244. _, err = secretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: "no-secret-with-this-id"})
  245. tassert.EqualError(t, err, errSecretPayloadNotFound)
  246. secretID, _ := fakeLockboxServer.CreateSecret(authorizedKey,
  247. textEntry("k1", "v1"),
  248. )
  249. _, err = secretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID, Version: "no-version-with-this-id"})
  250. tassert.EqualError(t, err, "unable to request secret payload to get secret: version not found")
  251. }
  252. func TestGetSecretWithTwoNamespaces(t *testing.T) {
  253. ctx := context.Background()
  254. namespace1 := uuid.NewString()
  255. namespace2 := uuid.NewString()
  256. authorizedKey1 := newFakeAuthorizedKey()
  257. authorizedKey2 := newFakeAuthorizedKey()
  258. fakeClock := clock.NewFakeClock()
  259. fakeLockboxServer := client.NewFakeLockboxServer(fakeClock, time.Hour)
  260. k1, v1 := "k1", "v1"
  261. secretID1, _ := fakeLockboxServer.CreateSecret(authorizedKey1,
  262. textEntry(k1, v1),
  263. )
  264. k2, v2 := "k2", "v2"
  265. secretID2, _ := fakeLockboxServer.CreateSecret(authorizedKey2,
  266. textEntry(k2, v2),
  267. )
  268. k8sClient := clientfake.NewClientBuilder().Build()
  269. const authorizedKeySecretName = "authorizedKeySecretName"
  270. const authorizedKeySecretKey = "authorizedKeySecretKey"
  271. err := createK8sSecret(ctx, t, k8sClient, namespace1, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, authorizedKey1))
  272. tassert.Nil(t, err)
  273. err = createK8sSecret(ctx, t, k8sClient, namespace2, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, authorizedKey2))
  274. tassert.Nil(t, err)
  275. store1 := newYandexLockboxSecretStore("", namespace1, authorizedKeySecretName, authorizedKeySecretKey)
  276. store2 := newYandexLockboxSecretStore("", namespace2, authorizedKeySecretName, authorizedKeySecretKey)
  277. provider := newLockboxProvider(fakeClock, fakeLockboxServer)
  278. secretsClient1, err := provider.NewClient(ctx, store1, k8sClient, namespace1)
  279. tassert.Nil(t, err)
  280. secretsClient2, err := provider.NewClient(ctx, store2, k8sClient, namespace2)
  281. tassert.Nil(t, err)
  282. data, err := secretsClient1.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID1, Property: k1})
  283. tassert.Equal(t, v1, string(data))
  284. tassert.Nil(t, err)
  285. data, err = secretsClient1.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID2, Property: k2})
  286. tassert.Nil(t, data)
  287. tassert.EqualError(t, err, errSecretPayloadPermissionDenied)
  288. data, err = secretsClient2.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID1, Property: k1})
  289. tassert.Nil(t, data)
  290. tassert.EqualError(t, err, errSecretPayloadPermissionDenied)
  291. data, err = secretsClient2.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID2, Property: k2})
  292. tassert.Equal(t, v2, string(data))
  293. tassert.Nil(t, err)
  294. }
  295. func TestGetSecretWithTwoApiEndpoints(t *testing.T) {
  296. ctx := context.Background()
  297. apiEndpoint1 := uuid.NewString()
  298. apiEndpoint2 := uuid.NewString()
  299. namespace := uuid.NewString()
  300. authorizedKey1 := newFakeAuthorizedKey()
  301. authorizedKey2 := newFakeAuthorizedKey()
  302. fakeClock := clock.NewFakeClock()
  303. fakeLockboxServer1 := client.NewFakeLockboxServer(fakeClock, time.Hour)
  304. k1, v1 := "k1", "v1"
  305. secretID1, _ := fakeLockboxServer1.CreateSecret(authorizedKey1,
  306. textEntry(k1, v1),
  307. )
  308. fakeLockboxServer2 := client.NewFakeLockboxServer(fakeClock, time.Hour)
  309. k2, v2 := "k2", "v2"
  310. secretID2, _ := fakeLockboxServer2.CreateSecret(authorizedKey2,
  311. textEntry(k2, v2),
  312. )
  313. k8sClient := clientfake.NewClientBuilder().Build()
  314. const authorizedKeySecretName1 = "authorizedKeySecretName1"
  315. const authorizedKeySecretKey1 = "authorizedKeySecretKey1"
  316. err := createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName1, authorizedKeySecretKey1, toJSON(t, authorizedKey1))
  317. tassert.Nil(t, err)
  318. const authorizedKeySecretName2 = "authorizedKeySecretName2"
  319. const authorizedKeySecretKey2 = "authorizedKeySecretKey2"
  320. err = createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName2, authorizedKeySecretKey2, toJSON(t, authorizedKey2))
  321. tassert.Nil(t, err)
  322. store1 := newYandexLockboxSecretStore(apiEndpoint1, namespace, authorizedKeySecretName1, authorizedKeySecretKey1)
  323. store2 := newYandexLockboxSecretStore(apiEndpoint2, namespace, authorizedKeySecretName2, authorizedKeySecretKey2)
  324. provider1 := newLockboxProvider(fakeClock, fakeLockboxServer1)
  325. provider2 := newLockboxProvider(fakeClock, fakeLockboxServer2)
  326. secretsClient1, err := provider1.NewClient(ctx, store1, k8sClient, namespace)
  327. tassert.Nil(t, err)
  328. secretsClient2, err := provider2.NewClient(ctx, store2, k8sClient, namespace)
  329. tassert.Nil(t, err)
  330. var data []byte
  331. data, err = secretsClient1.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID1, Property: k1})
  332. tassert.Equal(t, v1, string(data))
  333. tassert.Nil(t, err)
  334. data, err = secretsClient1.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID2, Property: k2})
  335. tassert.Nil(t, data)
  336. tassert.EqualError(t, err, errSecretPayloadNotFound)
  337. data, err = secretsClient2.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID1, Property: k1})
  338. tassert.Nil(t, data)
  339. tassert.EqualError(t, err, errSecretPayloadNotFound)
  340. data, err = secretsClient2.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID2, Property: k2})
  341. tassert.Equal(t, v2, string(data))
  342. tassert.Nil(t, err)
  343. }
  344. func TestGetSecretWithIamTokenExpiration(t *testing.T) {
  345. ctx := context.Background()
  346. namespace := uuid.NewString()
  347. authorizedKey := newFakeAuthorizedKey()
  348. fakeClock := clock.NewFakeClock()
  349. tokenExpirationTime := time.Hour
  350. fakeLockboxServer := client.NewFakeLockboxServer(fakeClock, tokenExpirationTime)
  351. k1, v1 := "k1", "v1"
  352. secretID, _ := fakeLockboxServer.CreateSecret(authorizedKey,
  353. textEntry(k1, v1),
  354. )
  355. k8sClient := clientfake.NewClientBuilder().Build()
  356. const authorizedKeySecretName = "authorizedKeySecretName"
  357. const authorizedKeySecretKey = "authorizedKeySecretKey"
  358. err := createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, authorizedKey))
  359. tassert.Nil(t, err)
  360. store := newYandexLockboxSecretStore("", namespace, authorizedKeySecretName, authorizedKeySecretKey)
  361. provider := newLockboxProvider(fakeClock, fakeLockboxServer)
  362. var data []byte
  363. oldSecretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
  364. tassert.Nil(t, err)
  365. data, err = oldSecretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID, Property: k1})
  366. tassert.Equal(t, v1, string(data))
  367. tassert.Nil(t, err)
  368. fakeClock.AddDuration(2 * tokenExpirationTime)
  369. data, err = oldSecretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID, Property: k1})
  370. tassert.Nil(t, data)
  371. tassert.EqualError(t, err, "unable to request secret payload to get secret: iam token expired")
  372. newSecretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
  373. tassert.Nil(t, err)
  374. data, err = newSecretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID, Property: k1})
  375. tassert.Equal(t, v1, string(data))
  376. tassert.Nil(t, err)
  377. }
  378. func TestGetSecretWithIamTokenCleanup(t *testing.T) {
  379. ctx := context.Background()
  380. namespace := uuid.NewString()
  381. authorizedKey1 := newFakeAuthorizedKey()
  382. authorizedKey2 := newFakeAuthorizedKey()
  383. fakeClock := clock.NewFakeClock()
  384. tokenExpirationDuration := time.Hour
  385. fakeLockboxServer := client.NewFakeLockboxServer(fakeClock, tokenExpirationDuration)
  386. secretID1, _ := fakeLockboxServer.CreateSecret(authorizedKey1,
  387. textEntry("k1", "v1"),
  388. )
  389. secretID2, _ := fakeLockboxServer.CreateSecret(authorizedKey2,
  390. textEntry("k2", "v2"),
  391. )
  392. var err error
  393. k8sClient := clientfake.NewClientBuilder().Build()
  394. const authorizedKeySecretName1 = "authorizedKeySecretName1"
  395. const authorizedKeySecretKey1 = "authorizedKeySecretKey1"
  396. err = createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName1, authorizedKeySecretKey1, toJSON(t, authorizedKey1))
  397. tassert.Nil(t, err)
  398. const authorizedKeySecretName2 = "authorizedKeySecretName2"
  399. const authorizedKeySecretKey2 = "authorizedKeySecretKey2"
  400. err = createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName2, authorizedKeySecretKey2, toJSON(t, authorizedKey2))
  401. tassert.Nil(t, err)
  402. store1 := newYandexLockboxSecretStore("", namespace, authorizedKeySecretName1, authorizedKeySecretKey1)
  403. store2 := newYandexLockboxSecretStore("", namespace, authorizedKeySecretName2, authorizedKeySecretKey2)
  404. provider := newLockboxProvider(fakeClock, fakeLockboxServer)
  405. tassert.False(t, provider.IsIamTokenCached(authorizedKey1))
  406. tassert.False(t, provider.IsIamTokenCached(authorizedKey2))
  407. // Access secretID1 with authorizedKey1, IAM token for authorizedKey1 should be cached
  408. secretsClient, err := provider.NewClient(ctx, store1, k8sClient, namespace)
  409. tassert.Nil(t, err)
  410. _, err = secretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID1})
  411. tassert.Nil(t, err)
  412. tassert.True(t, provider.IsIamTokenCached(authorizedKey1))
  413. tassert.False(t, provider.IsIamTokenCached(authorizedKey2))
  414. fakeClock.AddDuration(tokenExpirationDuration * 2)
  415. // Access secretID2 with authorizedKey2, IAM token for authorizedKey2 should be cached
  416. secretsClient, err = provider.NewClient(ctx, store2, k8sClient, namespace)
  417. tassert.Nil(t, err)
  418. _, err = secretsClient.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID2})
  419. tassert.Nil(t, err)
  420. tassert.True(t, provider.IsIamTokenCached(authorizedKey1))
  421. tassert.True(t, provider.IsIamTokenCached(authorizedKey2))
  422. fakeClock.AddDuration(tokenExpirationDuration)
  423. tassert.True(t, provider.IsIamTokenCached(authorizedKey1))
  424. tassert.True(t, provider.IsIamTokenCached(authorizedKey2))
  425. provider.CleanUpIamTokenMap()
  426. tassert.False(t, provider.IsIamTokenCached(authorizedKey1))
  427. tassert.True(t, provider.IsIamTokenCached(authorizedKey2))
  428. fakeClock.AddDuration(tokenExpirationDuration)
  429. tassert.False(t, provider.IsIamTokenCached(authorizedKey1))
  430. tassert.True(t, provider.IsIamTokenCached(authorizedKey2))
  431. provider.CleanUpIamTokenMap()
  432. tassert.False(t, provider.IsIamTokenCached(authorizedKey1))
  433. tassert.False(t, provider.IsIamTokenCached(authorizedKey2))
  434. }
  435. func TestGetSecretMap(t *testing.T) {
  436. ctx := context.Background()
  437. namespace := uuid.NewString()
  438. authorizedKey := newFakeAuthorizedKey()
  439. fakeClock := clock.NewFakeClock()
  440. fakeLockboxServer := client.NewFakeLockboxServer(fakeClock, time.Hour)
  441. k1, v1 := "k1", "v1"
  442. k2, v2 := "k2", []byte("v2")
  443. secretID, _ := fakeLockboxServer.CreateSecret(authorizedKey,
  444. textEntry(k1, v1),
  445. binaryEntry(k2, v2),
  446. )
  447. k8sClient := clientfake.NewClientBuilder().Build()
  448. const authorizedKeySecretName = "authorizedKeySecretName"
  449. const authorizedKeySecretKey = "authorizedKeySecretKey"
  450. err := createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, authorizedKey))
  451. tassert.Nil(t, err)
  452. store := newYandexLockboxSecretStore("", namespace, authorizedKeySecretName, authorizedKeySecretKey)
  453. provider := newLockboxProvider(fakeClock, fakeLockboxServer)
  454. secretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
  455. tassert.Nil(t, err)
  456. data, err := secretsClient.GetSecretMap(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID})
  457. tassert.Nil(t, err)
  458. tassert.Equal(
  459. t,
  460. map[string][]byte{
  461. k1: []byte(v1),
  462. k2: v2,
  463. },
  464. data,
  465. )
  466. }
  467. func TestGetSecretMapByVersionID(t *testing.T) {
  468. ctx := context.Background()
  469. namespace := uuid.NewString()
  470. authorizedKey := newFakeAuthorizedKey()
  471. fakeClock := clock.NewFakeClock()
  472. fakeLockboxServer := client.NewFakeLockboxServer(fakeClock, time.Hour)
  473. oldKey, oldVal := "oldKey", "oldVal"
  474. secretID, oldVersionID := fakeLockboxServer.CreateSecret(authorizedKey,
  475. textEntry(oldKey, oldVal),
  476. )
  477. k8sClient := clientfake.NewClientBuilder().Build()
  478. const authorizedKeySecretName = "authorizedKeySecretName"
  479. const authorizedKeySecretKey = "authorizedKeySecretKey"
  480. err := createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, authorizedKey))
  481. tassert.Nil(t, err)
  482. store := newYandexLockboxSecretStore("", namespace, authorizedKeySecretName, authorizedKeySecretKey)
  483. provider := newLockboxProvider(fakeClock, fakeLockboxServer)
  484. secretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
  485. tassert.Nil(t, err)
  486. data, err := secretsClient.GetSecretMap(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID, Version: oldVersionID})
  487. tassert.Nil(t, err)
  488. tassert.Equal(t, map[string][]byte{oldKey: []byte(oldVal)}, data)
  489. newKey, newVal := "newKey", "newVal"
  490. newVersionID := fakeLockboxServer.AddVersion(secretID,
  491. textEntry(newKey, newVal),
  492. )
  493. data, err = secretsClient.GetSecretMap(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID, Version: oldVersionID})
  494. tassert.Nil(t, err)
  495. tassert.Equal(t, map[string][]byte{oldKey: []byte(oldVal)}, data)
  496. data, err = secretsClient.GetSecretMap(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: secretID, Version: newVersionID})
  497. tassert.Nil(t, err)
  498. tassert.Equal(t, map[string][]byte{newKey: []byte(newVal)}, data)
  499. }
  500. // helper functions
  501. func newLockboxProvider(clock clock.Clock, fakeLockboxServer *client.FakeLockboxServer) *common.YandexCloudProvider {
  502. return common.InitYandexCloudProvider(
  503. ctrl.Log.WithName("provider").WithName("yandex").WithName("lockbox"),
  504. clock,
  505. adaptInput,
  506. func(ctx context.Context, apiEndpoint string, authorizedKey *iamkey.Key, caCertificate []byte) (common.SecretGetter, error) {
  507. return newLockboxSecretGetter(client.NewFakeLockboxClient(fakeLockboxServer))
  508. },
  509. func(ctx context.Context, apiEndpoint string, authorizedKey *iamkey.Key, caCertificate []byte) (*common.IamToken, error) {
  510. return fakeLockboxServer.NewIamToken(authorizedKey), nil
  511. },
  512. 0,
  513. )
  514. }
  515. func newYandexLockboxSecretStore(apiEndpoint, namespace, authorizedKeySecretName, authorizedKeySecretKey string) esv1beta1.GenericStore {
  516. return &esv1beta1.SecretStore{
  517. ObjectMeta: metav1.ObjectMeta{
  518. Namespace: namespace,
  519. },
  520. Spec: esv1beta1.SecretStoreSpec{
  521. Provider: &esv1beta1.SecretStoreProvider{
  522. YandexLockbox: &esv1beta1.YandexLockboxProvider{
  523. APIEndpoint: apiEndpoint,
  524. Auth: esv1beta1.YandexLockboxAuth{
  525. AuthorizedKey: esmeta.SecretKeySelector{
  526. Name: authorizedKeySecretName,
  527. Key: authorizedKeySecretKey,
  528. },
  529. },
  530. },
  531. },
  532. },
  533. }
  534. }
  535. func toJSON(t *testing.T, v any) []byte {
  536. jsonBytes, err := json.Marshal(v)
  537. tassert.Nil(t, err)
  538. return jsonBytes
  539. }
  540. func createK8sSecret(ctx context.Context, t *testing.T, k8sClient k8sclient.Client, namespace, secretName, secretKey string, secretValue []byte) error {
  541. err := k8sClient.Create(ctx, &corev1.Secret{
  542. ObjectMeta: metav1.ObjectMeta{
  543. Namespace: namespace,
  544. Name: secretName,
  545. },
  546. Data: map[string][]byte{secretKey: secretValue},
  547. })
  548. tassert.Nil(t, err)
  549. return nil
  550. }
  551. func newFakeAuthorizedKey() *iamkey.Key {
  552. uniqueLabel := uuid.NewString()
  553. return &iamkey.Key{
  554. Id: uniqueLabel,
  555. Subject: &iamkey.Key_ServiceAccountId{
  556. ServiceAccountId: uniqueLabel,
  557. },
  558. PrivateKey: uniqueLabel,
  559. }
  560. }
  561. func textEntry(key, value string) *lockbox.Payload_Entry {
  562. return &lockbox.Payload_Entry{
  563. Key: key,
  564. Value: &lockbox.Payload_Entry_TextValue{
  565. TextValue: value,
  566. },
  567. }
  568. }
  569. func binaryEntry(key string, value []byte) *lockbox.Payload_Entry {
  570. return &lockbox.Payload_Entry{
  571. Key: key,
  572. Value: &lockbox.Payload_Entry_BinaryValue{
  573. BinaryValue: value,
  574. },
  575. }
  576. }
  577. func unmarshalStringMap(t *testing.T, data []byte) map[string]string {
  578. stringMap := make(map[string]string)
  579. err := json.Unmarshal(data, &stringMap)
  580. tassert.Nil(t, err)
  581. return stringMap
  582. }
  583. func base64(data []byte) string {
  584. return b64.StdEncoding.EncodeToString(data)
  585. }