provider_test.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965
  1. /*
  2. Copyright © The ESO Authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package conjur
  14. import (
  15. "context"
  16. "errors"
  17. "fmt"
  18. "reflect"
  19. "testing"
  20. "time"
  21. "github.com/cyberark/conjur-api-go/conjurapi"
  22. "github.com/cyberark/conjur-api-go/conjurapi/authn"
  23. "github.com/golang-jwt/jwt/v5"
  24. "github.com/google/go-cmp/cmp"
  25. corev1 "k8s.io/api/core/v1"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
  28. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  29. clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
  30. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  31. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  32. "github.com/external-secrets/external-secrets/providers/v1/conjur/fake"
  33. utilfake "github.com/external-secrets/external-secrets/runtime/util/fake"
  34. )
  35. var (
  36. svcURL = "https://example.com"
  37. svcUser = "user"
  38. svcApikey = "apikey"
  39. svcAccount = "account1"
  40. jwtAuthenticator = "jwt-authenticator"
  41. jwtAuthnService = "jwt-auth-service"
  42. jwtSecretName = "jwt-secret"
  43. certServiceID = "cert-auth-service"
  44. certClientCertName = "conjur-client-cert"
  45. certClientKeyName = "conjur-client-key"
  46. )
  47. func makeValidRef(k string) *esv1.ExternalSecretDataRemoteRef {
  48. return &esv1.ExternalSecretDataRemoteRef{
  49. Key: k,
  50. Version: "default",
  51. }
  52. }
  53. func makeValidFindRef(search string, tags map[string]string) *esv1.ExternalSecretFind {
  54. var name *esv1.FindName
  55. if search != "" {
  56. name = &esv1.FindName{
  57. RegExp: search,
  58. }
  59. }
  60. return &esv1.ExternalSecretFind{
  61. Name: name,
  62. Tags: tags,
  63. }
  64. }
  65. func TestGetSecret(t *testing.T) {
  66. type args struct {
  67. store esv1.GenericStore
  68. kube kclient.Client
  69. corev1 typedcorev1.CoreV1Interface
  70. namespace string
  71. secretPath string
  72. }
  73. type want struct {
  74. err error
  75. value string
  76. }
  77. type testCase struct {
  78. reason string
  79. args args
  80. want want
  81. }
  82. cases := map[string]testCase{
  83. "ApiKeyReadSecretSuccess": {
  84. reason: "Should read a secret successfully using an ApiKey auth secret store.",
  85. args: args{
  86. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  87. kube: clientfake.NewClientBuilder().
  88. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  89. namespace: "default",
  90. secretPath: "path/to/secret",
  91. },
  92. want: want{
  93. err: nil,
  94. value: "secret",
  95. },
  96. },
  97. "ApiKeyReadSecretFailure": {
  98. reason: "Should fail to read secret using ApiKey auth secret store.",
  99. args: args{
  100. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  101. kube: clientfake.NewClientBuilder().
  102. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  103. namespace: "default",
  104. secretPath: "error",
  105. },
  106. want: want{
  107. err: errors.New("error"),
  108. value: "",
  109. },
  110. },
  111. "JwtWithServiceAccountRefReadSecretSuccess": {
  112. reason: "Should read a secret successfully using a JWT auth secret store that references a k8s service account.",
  113. args: args{
  114. store: makeJWTSecretStore(svcURL, svcAccount, "", jwtAuthenticator, "", "myconjuraccount"),
  115. kube: clientfake.NewClientBuilder().
  116. WithObjects().Build(),
  117. namespace: "default",
  118. secretPath: "path/to/secret",
  119. corev1: utilfake.NewCreateTokenMock().WithToken(createFakeJwtToken(true)),
  120. },
  121. want: want{
  122. err: nil,
  123. value: "secret",
  124. },
  125. },
  126. "JwtWithServiceAccountRefWithHostIdReadSecretSuccess": {
  127. reason: "Should read a secret successfully using a JWT auth secret store that references a k8s service account and uses a host ID.",
  128. args: args{
  129. store: makeJWTSecretStore(svcURL, svcAccount, "", jwtAuthenticator, "myhostid", "myconjuraccount"),
  130. kube: clientfake.NewClientBuilder().
  131. WithObjects().Build(),
  132. namespace: "default",
  133. secretPath: "path/to/secret",
  134. corev1: utilfake.NewCreateTokenMock().WithToken(createFakeJwtToken(true)),
  135. },
  136. want: want{
  137. err: nil,
  138. value: "secret",
  139. },
  140. },
  141. "JwtWithSecretRefReadSecretSuccess": {
  142. reason: "Should read a secret successfully using an JWT auth secret store that references a k8s secret.",
  143. args: args{
  144. store: makeJWTSecretStore(svcURL, "", jwtSecretName, jwtAuthenticator, "", "myconjuraccount"),
  145. kube: clientfake.NewClientBuilder().
  146. WithObjects(&corev1.Secret{
  147. ObjectMeta: metav1.ObjectMeta{
  148. Name: jwtSecretName,
  149. Namespace: "default",
  150. },
  151. Data: map[string][]byte{
  152. "token": []byte(createFakeJwtToken(true)),
  153. },
  154. }).Build(),
  155. namespace: "default",
  156. secretPath: "path/to/secret",
  157. },
  158. want: want{
  159. err: nil,
  160. value: "secret",
  161. },
  162. },
  163. "JwtWithCABundleSuccess": {
  164. reason: "Should read a secret successfully using a JWT auth secret store that references a k8s service account.",
  165. args: args{
  166. store: makeJWTSecretStore(svcURL, svcAccount, "", jwtAuthenticator, "", "myconjuraccount"),
  167. kube: clientfake.NewClientBuilder().
  168. WithObjects().Build(),
  169. namespace: "default",
  170. secretPath: "path/to/secret",
  171. corev1: utilfake.NewCreateTokenMock().WithToken(createFakeJwtToken(true)),
  172. },
  173. want: want{
  174. err: nil,
  175. value: "secret",
  176. },
  177. },
  178. "CertReadSecretSuccess": {
  179. reason: "Should read a secret successfully using a Cert auth secret store (spiffe).",
  180. args: args{
  181. store: makeCertSecretStore(svcURL, certServiceID, "", "myconjuraccount"),
  182. kube: clientfake.NewClientBuilder().
  183. WithObjects(makeFakeCertSecrets()...).Build(),
  184. namespace: "default",
  185. secretPath: "path/to/secret",
  186. },
  187. want: want{
  188. err: nil,
  189. value: "secret",
  190. },
  191. },
  192. "CertWithHostIdReadSecretSuccess": {
  193. reason: "Should read a secret successfully using a Cert auth secret store with a host ID.",
  194. args: args{
  195. store: makeCertSecretStore(svcURL, certServiceID, "myhostid", "myconjuraccount"),
  196. kube: clientfake.NewClientBuilder().
  197. WithObjects(makeFakeCertSecrets()...).Build(),
  198. namespace: "default",
  199. secretPath: "path/to/secret",
  200. },
  201. want: want{
  202. err: nil,
  203. value: "secret",
  204. },
  205. },
  206. "CertReadSecretFailure": {
  207. reason: "Should fail to read secret using Cert auth secret store.",
  208. args: args{
  209. store: makeCertSecretStore(svcURL, certServiceID, "", "myconjuraccount"),
  210. kube: clientfake.NewClientBuilder().
  211. WithObjects(makeFakeCertSecrets()...).Build(),
  212. namespace: "default",
  213. secretPath: "error",
  214. },
  215. want: want{
  216. err: errors.New("error"),
  217. value: "",
  218. },
  219. },
  220. "CertMissingClientCertSecret": {
  221. reason: "Should fail when the client certificate secret does not exist.",
  222. args: args{
  223. store: makeCertSecretStore(svcURL, certServiceID, "", "myconjuraccount"),
  224. kube: clientfake.NewClientBuilder().
  225. WithObjects(&corev1.Secret{
  226. ObjectMeta: metav1.ObjectMeta{
  227. Name: certClientKeyName,
  228. Namespace: "default",
  229. },
  230. Data: map[string][]byte{
  231. "tls.key": []byte("-----BEGIN RSA PRIVATE KEY-----\nfakekey\n-----END RSA PRIVATE KEY-----"),
  232. },
  233. }).Build(),
  234. namespace: "default",
  235. secretPath: "path/to/secret",
  236. },
  237. want: want{
  238. err: fmt.Errorf(errBadClientCert, fmt.Errorf("cannot get Kubernetes secret \"%s\" from namespace \"default\": secrets \"%s\" not found", certClientCertName, certClientCertName)),
  239. value: "",
  240. },
  241. },
  242. "CertMissingClientKeySecret": {
  243. reason: "Should fail when the client key secret does not exist.",
  244. args: args{
  245. store: makeCertSecretStore(svcURL, certServiceID, "", "myconjuraccount"),
  246. kube: clientfake.NewClientBuilder().
  247. WithObjects(&corev1.Secret{
  248. ObjectMeta: metav1.ObjectMeta{
  249. Name: certClientCertName,
  250. Namespace: "default",
  251. },
  252. Data: map[string][]byte{
  253. "tls.crt": []byte("-----BEGIN CERTIFICATE-----\nfakecert\n-----END CERTIFICATE-----"),
  254. },
  255. }).Build(),
  256. namespace: "default",
  257. secretPath: "path/to/secret",
  258. },
  259. want: want{
  260. err: fmt.Errorf(errBadClientKey, fmt.Errorf("cannot get Kubernetes secret \"%s\" from namespace \"default\": secrets \"%s\" not found", certClientKeyName, certClientKeyName)),
  261. value: "",
  262. },
  263. },
  264. }
  265. runTest := func(t *testing.T, _ string, tc testCase) {
  266. provider, _ := newConjurProvider(context.Background(), tc.args.store, tc.args.kube, tc.args.namespace, tc.args.corev1, &ConjurMockAPIClient{})
  267. ref := makeValidRef(tc.args.secretPath)
  268. secret, err := provider.GetSecret(context.Background(), *ref)
  269. if diff := cmp.Diff(tc.want.err, err, EquateErrors()); diff != "" {
  270. t.Errorf("\n%s\nconjur.GetSecret(...): -want error, +got error:\n%s", tc.reason, diff)
  271. }
  272. secretString := string(secret)
  273. if secretString != tc.want.value {
  274. t.Errorf("\n%s\nconjur.GetSecret(...): want value %v got %v", tc.reason, tc.want.value, secretString)
  275. }
  276. }
  277. for name, tc := range cases {
  278. t.Run(name, func(t *testing.T) {
  279. runTest(t, name, tc)
  280. })
  281. }
  282. }
  283. func TestGetAllSecrets(t *testing.T) {
  284. type args struct {
  285. store esv1.GenericStore
  286. kube kclient.Client
  287. corev1 typedcorev1.CoreV1Interface
  288. namespace string
  289. search string
  290. tags map[string]string
  291. }
  292. type want struct {
  293. err error
  294. values map[string][]byte
  295. }
  296. type testCase struct {
  297. reason string
  298. args args
  299. want want
  300. }
  301. cases := map[string]testCase{
  302. "SimpleSearchSingleResultSuccess": {
  303. reason: "Should search for secrets successfully using a simple string.",
  304. args: args{
  305. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  306. kube: clientfake.NewClientBuilder().
  307. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  308. namespace: "default",
  309. search: "secret1",
  310. },
  311. want: want{
  312. err: nil,
  313. values: map[string][]byte{
  314. "secret1": []byte("secret"),
  315. },
  316. },
  317. },
  318. "RegexSearchMultipleResultsSuccess": {
  319. reason: "Should search for secrets successfully using a regex and return multiple results.",
  320. args: args{
  321. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  322. kube: clientfake.NewClientBuilder().
  323. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  324. namespace: "default",
  325. search: "^secret[1,2]$",
  326. },
  327. want: want{
  328. err: nil,
  329. values: map[string][]byte{
  330. "secret1": []byte("secret"),
  331. "secret2": []byte("secret"),
  332. },
  333. },
  334. },
  335. "RegexSearchInvalidRegexFailure": {
  336. reason: "Should fail to search for secrets using an invalid regex.",
  337. args: args{
  338. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  339. kube: clientfake.NewClientBuilder().
  340. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  341. namespace: "default",
  342. search: "^secret[1,2", // Missing `]`
  343. },
  344. want: want{
  345. err: fmt.Errorf("could not compile find.name.regexp [%s]: %w", "^secret[1,2", errors.New("error parsing regexp: missing closing ]: `[1,2`")),
  346. values: nil,
  347. },
  348. },
  349. "SimpleSearchNoResultsSuccess": {
  350. reason: "Should search for secrets successfully using a simple string and return no results.",
  351. args: args{
  352. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  353. kube: clientfake.NewClientBuilder().
  354. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  355. namespace: "default",
  356. search: "nonexistent",
  357. },
  358. want: want{
  359. err: nil,
  360. values: map[string][]byte{},
  361. },
  362. },
  363. "TagSearchSingleResultSuccess": {
  364. reason: "Should search for secrets successfully using a tag.",
  365. args: args{
  366. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  367. kube: clientfake.NewClientBuilder().
  368. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  369. namespace: "default",
  370. tags: map[string]string{
  371. "conjur/kind": "password",
  372. },
  373. },
  374. want: want{
  375. err: nil,
  376. values: map[string][]byte{
  377. "secret2": []byte("secret"),
  378. },
  379. },
  380. },
  381. }
  382. runTest := func(t *testing.T, _ string, tc testCase) {
  383. provider, _ := newConjurProvider(context.Background(), tc.args.store, tc.args.kube, tc.args.namespace, tc.args.corev1, &ConjurMockAPIClient{})
  384. ref := makeValidFindRef(tc.args.search, tc.args.tags)
  385. secrets, err := provider.GetAllSecrets(context.Background(), *ref)
  386. if diff := cmp.Diff(tc.want.err, err, EquateErrors()); diff != "" {
  387. t.Errorf("\n%s\nconjur.GetAllSecrets(...): -want error, +got error:\n%s", tc.reason, diff)
  388. }
  389. if diff := cmp.Diff(tc.want.values, secrets); diff != "" {
  390. t.Errorf("\n%s\nconjur.GetAllSecrets(...): -want, +got:\n%s", tc.reason, diff)
  391. }
  392. }
  393. for name, tc := range cases {
  394. t.Run(name, func(t *testing.T) {
  395. runTest(t, name, tc)
  396. })
  397. }
  398. }
  399. func TestGetSecretMap(t *testing.T) {
  400. type args struct {
  401. store esv1.GenericStore
  402. kube kclient.Client
  403. corev1 typedcorev1.CoreV1Interface
  404. namespace string
  405. ref *esv1.ExternalSecretDataRemoteRef
  406. }
  407. type want struct {
  408. err error
  409. val map[string][]byte
  410. }
  411. type testCase struct {
  412. reason string
  413. args args
  414. want want
  415. }
  416. cases := map[string]testCase{
  417. "ReadJsonSecret": {
  418. reason: "Should read a JSON key value secret.",
  419. args: args{
  420. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  421. kube: clientfake.NewClientBuilder().
  422. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  423. namespace: "default",
  424. ref: makeValidRef("json_map"),
  425. },
  426. want: want{
  427. err: nil,
  428. val: map[string][]byte{
  429. "key1": []byte("value1"),
  430. "key2": []byte("value2"),
  431. },
  432. },
  433. },
  434. "ReadJsonSecretFailure": {
  435. reason: "Should fail to read a non JSON secret",
  436. args: args{
  437. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  438. kube: clientfake.NewClientBuilder().
  439. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  440. namespace: "default",
  441. ref: makeValidRef("secret1"),
  442. },
  443. want: want{
  444. err: fmt.Errorf("%w", fmt.Errorf("unable to unmarshal secret secret1: invalid character 's' looking for beginning of value")),
  445. val: nil,
  446. },
  447. },
  448. "ReadJsonSecretSpecificKey": {
  449. reason: "Should read a specific key from a JSON secret.",
  450. args: args{
  451. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  452. kube: clientfake.NewClientBuilder().
  453. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  454. namespace: "default",
  455. ref: &esv1.ExternalSecretDataRemoteRef{
  456. Key: "json_nested",
  457. Version: "default",
  458. Property: "key2",
  459. },
  460. },
  461. want: want{
  462. err: nil,
  463. val: map[string][]byte{
  464. "key3": []byte("value3"),
  465. "key4": []byte("value4"),
  466. },
  467. },
  468. },
  469. "ReadJsonSecretSpecificKeyNotFound": {
  470. reason: "Should fail to read a nonexistent key from a JSON secret.",
  471. args: args{
  472. store: makeAPIKeySecretStore(svcURL, "conjur-hostid", "conjur-apikey", "myconjuraccount"),
  473. kube: clientfake.NewClientBuilder().
  474. WithObjects(makeFakeAPIKeySecrets()...).Build(),
  475. namespace: "default",
  476. ref: &esv1.ExternalSecretDataRemoteRef{
  477. Key: "json_map",
  478. Version: "default",
  479. Property: "key3",
  480. },
  481. },
  482. want: want{
  483. err: fmt.Errorf("%w", errors.New("error getting secret json_map: cannot find secret data for key: \"key3\"")),
  484. val: nil,
  485. },
  486. },
  487. }
  488. runTest := func(t *testing.T, _ string, tc testCase) {
  489. provider, _ := newConjurProvider(context.Background(), tc.args.store, tc.args.kube, tc.args.namespace, tc.args.corev1, &ConjurMockAPIClient{})
  490. val, err := provider.GetSecretMap(context.Background(), *tc.args.ref)
  491. if diff := cmp.Diff(tc.want.err, err, EquateErrors()); diff != "" {
  492. t.Errorf("\n%s\nconjur.GetSecretMap(...): -want error, +got error:\n%s", tc.reason, diff)
  493. }
  494. if diff := cmp.Diff(tc.want.val, val); diff != "" {
  495. t.Errorf("\n%s\nconjur.GetSecretMap(...): -want val, +got val:\n%s", tc.reason, diff)
  496. }
  497. }
  498. for name, tc := range cases {
  499. t.Run(name, func(t *testing.T) {
  500. runTest(t, name, tc)
  501. })
  502. }
  503. }
  504. func TestGetCA(t *testing.T) {
  505. type args struct {
  506. store esv1.GenericStore
  507. kube kclient.Client
  508. corev1 typedcorev1.CoreV1Interface
  509. namespace string
  510. }
  511. type want struct {
  512. err error
  513. cert string
  514. }
  515. type testCase struct {
  516. reason string
  517. args args
  518. want want
  519. }
  520. certData := `-----BEGIN CERTIFICATE-----
  521. MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
  522. CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
  523. Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
  524. MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
  525. bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG
  526. ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS
  527. 7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp
  528. 0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS
  529. B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49
  530. BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ
  531. LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4
  532. DXZDjC5Ty3zfDBeWUA==
  533. -----END CERTIFICATE-----`
  534. certDataEncoded := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNHVENDQVorZ0F3SUJBZ0lRQ2VDVFphejMyY2k1UGh3TEJDb3U4ekFLQmdncWhrak9QUVFEQXpCT01Rc3cKQ1FZRFZRUUdFd0pWVXpFWE1CVUdBMVVFQ2hNT1JHbG5hVU5sY25Rc0lFbHVZeTR4SmpBa0JnTlZCQU1USFVScApaMmxEWlhKMElGUk1VeUJGUTBNZ1VETTROQ0JTYjI5MElFYzFNQjRYRFRJeE1ERXhOVEF3TURBd01Gb1hEVFEyCk1ERXhOREl6TlRrMU9Wb3dUakVMTUFrR0ExVUVCaE1DVlZNeEZ6QVZCZ05WQkFvVERrUnBaMmxEWlhKMExDQkoKYm1NdU1TWXdKQVlEVlFRREV4MUVhV2RwUTJWeWRDQlVURk1nUlVORElGQXpPRFFnVW05dmRDQkhOVEIyTUJBRwpCeXFHU000OUFnRUdCU3VCQkFBaUEySUFCTUZFb2M4UmwxQ2EzaU9DTlFmTjBNc1luZEx4ZjNjMVR6dmRsSEpTCjdjSTcrT3o2ZTJ0WUlPeVpyc244YUxOMXVkc0o3TWdUOVU3R0NoMW1NRXk3SDBjS1BHRVFRaWw4cFFnTzRDTHAKMHpWb3pwdGpuNFMxbVUxWW9JNzFWT2VWeWFOQ01FQXdIUVlEVlIwT0JCWUVGTUZSUlZCWnF6N25MRnI2SUNJUwpCNENJZkJGcU1BNEdBMVVkRHdFQi93UUVBd0lCaGpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5CkJBTURBMmdBTUdVQ01RQ0phbzFINSt6OGJsVUQyV2RzSms2RHh2M0oreXNUdkxkNmpMUmwwbWxwWXhOak95WlEKTGdHaGVRYVJuVWkvd3I0Q01FZkRGWHV4b0pHWlNaT29QSHpvUmdhTExQSXhBSlNkWXNpSnZSbUVGT21sK3dHNApEWFpEakM1VHkzemZEQmVXVUE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t"
  535. cases := map[string]testCase{
  536. "UseCABundleSuccess": {
  537. reason: "Should read a caBundle successfully.",
  538. args: args{
  539. store: makeStoreWithCA("cabundle", certDataEncoded),
  540. kube: clientfake.NewClientBuilder().
  541. WithObjects().Build(),
  542. namespace: "default",
  543. corev1: utilfake.NewCreateTokenMock().WithToken(createFakeJwtToken(true)),
  544. },
  545. want: want{
  546. err: nil,
  547. cert: certDataEncoded,
  548. },
  549. },
  550. "UseCAProviderConfigMapSuccess": {
  551. reason: "Should read a ca from a ConfigMap successfully.",
  552. args: args{
  553. store: makeStoreWithCA("configmap", ""),
  554. kube: clientfake.NewClientBuilder().
  555. WithObjects(makeFakeCASource("configmap", certData)).Build(),
  556. namespace: "default",
  557. corev1: utilfake.NewCreateTokenMock().WithToken(createFakeJwtToken(true)),
  558. },
  559. want: want{
  560. err: nil,
  561. cert: certDataEncoded,
  562. },
  563. },
  564. "UseCAProviderSecretSuccess": {
  565. reason: "Should read a ca from a Secret successfully.",
  566. args: args{
  567. store: makeStoreWithCA("secret", ""),
  568. kube: clientfake.NewClientBuilder().
  569. WithObjects(makeFakeCASource("secret", certData)).Build(),
  570. namespace: "default",
  571. corev1: utilfake.NewCreateTokenMock().WithToken(createFakeJwtToken(true)),
  572. },
  573. want: want{
  574. err: nil,
  575. cert: certDataEncoded,
  576. },
  577. },
  578. }
  579. runTest := func(t *testing.T, _ string, tc testCase) {
  580. provider, _ := newConjurProvider(context.Background(), tc.args.store, tc.args.kube, tc.args.namespace, tc.args.corev1, &ConjurMockAPIClient{})
  581. _, err := provider.GetSecret(context.Background(), esv1.ExternalSecretDataRemoteRef{
  582. Key: "path/to/secret",
  583. })
  584. if diff := cmp.Diff(tc.want.err, err, EquateErrors()); diff != "" {
  585. t.Errorf("\n%s\nconjur.GetCA(...): -want error, +got error:\n%s", tc.reason, diff)
  586. }
  587. }
  588. for name, tc := range cases {
  589. t.Run(name, func(t *testing.T) {
  590. runTest(t, name, tc)
  591. })
  592. }
  593. }
  594. func makeAPIKeySecretStore(svcURL, svcUser, svcApikey, svcAccount string) *esv1.SecretStore {
  595. uref := &esmeta.SecretKeySelector{
  596. Name: "user",
  597. Key: "conjur-hostid",
  598. }
  599. if svcUser == "" {
  600. uref = nil
  601. }
  602. aref := &esmeta.SecretKeySelector{
  603. Name: "apikey",
  604. Key: "conjur-apikey",
  605. }
  606. if svcApikey == "" {
  607. aref = nil
  608. }
  609. store := &esv1.SecretStore{
  610. Spec: esv1.SecretStoreSpec{
  611. Provider: &esv1.SecretStoreProvider{
  612. Conjur: &esv1.ConjurProvider{
  613. URL: svcURL,
  614. Auth: esv1.ConjurAuth{
  615. APIKey: &esv1.ConjurAPIKey{
  616. Account: svcAccount,
  617. UserRef: uref,
  618. APIKeyRef: aref,
  619. },
  620. },
  621. },
  622. },
  623. },
  624. }
  625. return store
  626. }
  627. func makeJWTSecretStore(svcURL, serviceAccountName, secretName, jwtServiceID, jwtHostID, conjurAccount string) *esv1.SecretStore {
  628. serviceAccountRef := &esmeta.ServiceAccountSelector{
  629. Name: serviceAccountName,
  630. Audiences: []string{"conjur"},
  631. }
  632. if serviceAccountName == "" {
  633. serviceAccountRef = nil
  634. }
  635. secretRef := &esmeta.SecretKeySelector{
  636. Name: secretName,
  637. Key: "token",
  638. }
  639. if secretName == "" {
  640. secretRef = nil
  641. }
  642. store := &esv1.SecretStore{
  643. Spec: esv1.SecretStoreSpec{
  644. Provider: &esv1.SecretStoreProvider{
  645. Conjur: &esv1.ConjurProvider{
  646. URL: svcURL,
  647. Auth: esv1.ConjurAuth{
  648. Jwt: &esv1.ConjurJWT{
  649. Account: conjurAccount,
  650. ServiceID: jwtServiceID,
  651. ServiceAccountRef: serviceAccountRef,
  652. SecretRef: secretRef,
  653. HostID: jwtHostID,
  654. },
  655. },
  656. },
  657. },
  658. },
  659. }
  660. return store
  661. }
  662. func makeStoreWithCA(caSource, caData string) *esv1.SecretStore {
  663. store := makeJWTSecretStore(svcURL, "conjur", "", jwtAuthnService, "", "myconjuraccount")
  664. if caSource == "secret" {
  665. store.Spec.Provider.Conjur.CAProvider = &esv1.CAProvider{
  666. Type: esv1.CAProviderTypeSecret,
  667. Name: "conjur-cert",
  668. Key: "ca",
  669. }
  670. } else if caSource == "configmap" {
  671. store.Spec.Provider.Conjur.CAProvider = &esv1.CAProvider{
  672. Type: esv1.CAProviderTypeConfigMap,
  673. Name: "conjur-cert",
  674. Key: "ca",
  675. }
  676. } else {
  677. store.Spec.Provider.Conjur.CABundle = caData
  678. }
  679. return store
  680. }
  681. func makeCertSecretStore(svcURL, certServiceID, hostID, conjurAccount string) *esv1.SecretStore {
  682. store := &esv1.SecretStore{
  683. Spec: esv1.SecretStoreSpec{
  684. Provider: &esv1.SecretStoreProvider{
  685. Conjur: &esv1.ConjurProvider{
  686. URL: svcURL,
  687. Auth: esv1.ConjurAuth{
  688. Cert: &esv1.ConjurCert{
  689. Account: conjurAccount,
  690. ServiceID: certServiceID,
  691. HostID: hostID,
  692. ClientCertRef: &esmeta.SecretKeySelector{
  693. Name: certClientCertName,
  694. Key: "tls.crt",
  695. },
  696. ClientKeyRef: &esmeta.SecretKeySelector{
  697. Name: certClientKeyName,
  698. Key: "tls.key",
  699. },
  700. },
  701. },
  702. },
  703. },
  704. },
  705. }
  706. return store
  707. }
  708. func makeCertSecretStoreWithMissingRefs(svcURL, certServiceID, conjurAccount string, hasCert, hasKey bool) *esv1.SecretStore {
  709. var certRef *esmeta.SecretKeySelector
  710. var keyRef *esmeta.SecretKeySelector
  711. if hasCert {
  712. certRef = &esmeta.SecretKeySelector{
  713. Name: certClientCertName,
  714. Key: "tls.crt",
  715. }
  716. }
  717. if hasKey {
  718. keyRef = &esmeta.SecretKeySelector{
  719. Name: certClientKeyName,
  720. Key: "tls.key",
  721. }
  722. }
  723. store := &esv1.SecretStore{
  724. Spec: esv1.SecretStoreSpec{
  725. Provider: &esv1.SecretStoreProvider{
  726. Conjur: &esv1.ConjurProvider{
  727. URL: svcURL,
  728. Auth: esv1.ConjurAuth{
  729. Cert: &esv1.ConjurCert{
  730. Account: conjurAccount,
  731. ServiceID: certServiceID,
  732. ClientCertRef: certRef,
  733. ClientKeyRef: keyRef,
  734. },
  735. },
  736. },
  737. },
  738. },
  739. }
  740. return store
  741. }
  742. func makeMultiAuthSecretStore(svcURL string) *esv1.SecretStore {
  743. return &esv1.SecretStore{
  744. Spec: esv1.SecretStoreSpec{
  745. Provider: &esv1.SecretStoreProvider{
  746. Conjur: &esv1.ConjurProvider{
  747. URL: svcURL,
  748. Auth: esv1.ConjurAuth{
  749. APIKey: &esv1.ConjurAPIKey{
  750. Account: svcAccount,
  751. UserRef: &esmeta.SecretKeySelector{Name: svcUser, Key: "username"},
  752. APIKeyRef: &esmeta.SecretKeySelector{Name: svcApikey, Key: "apikey"},
  753. },
  754. Jwt: &esv1.ConjurJWT{
  755. Account: "myconjuraccount",
  756. ServiceID: jwtAuthnService,
  757. ServiceAccountRef: &esmeta.ServiceAccountSelector{Name: "conjur"},
  758. },
  759. },
  760. },
  761. },
  762. },
  763. }
  764. }
  765. func makeCertSecretStoreWithEmptyRefNames(svcURL, certServiceID, conjurAccount string, emptyCertName, emptyKeyName bool) *esv1.SecretStore {
  766. certName := certClientCertName
  767. if emptyCertName {
  768. certName = ""
  769. }
  770. keyName := certClientKeyName
  771. if emptyKeyName {
  772. keyName = ""
  773. }
  774. store := &esv1.SecretStore{
  775. Spec: esv1.SecretStoreSpec{
  776. Provider: &esv1.SecretStoreProvider{
  777. Conjur: &esv1.ConjurProvider{
  778. URL: svcURL,
  779. Auth: esv1.ConjurAuth{
  780. Cert: &esv1.ConjurCert{
  781. Account: conjurAccount,
  782. ServiceID: certServiceID,
  783. ClientCertRef: &esmeta.SecretKeySelector{
  784. Name: certName,
  785. Key: "tls.crt",
  786. },
  787. ClientKeyRef: &esmeta.SecretKeySelector{
  788. Name: keyName,
  789. Key: "tls.key",
  790. },
  791. },
  792. },
  793. },
  794. },
  795. },
  796. }
  797. return store
  798. }
  799. func makeFakeAPIKeySecrets() []kclient.Object {
  800. return []kclient.Object{
  801. &corev1.Secret{
  802. ObjectMeta: metav1.ObjectMeta{
  803. Name: "user",
  804. Namespace: "default",
  805. },
  806. Data: map[string][]byte{
  807. "conjur-hostid": []byte("myhostid"),
  808. },
  809. },
  810. &corev1.Secret{
  811. ObjectMeta: metav1.ObjectMeta{
  812. Name: "apikey",
  813. Namespace: "default",
  814. },
  815. Data: map[string][]byte{
  816. "conjur-apikey": []byte("apikey"),
  817. },
  818. },
  819. }
  820. }
  821. func makeFakeCertSecrets() []kclient.Object {
  822. return []kclient.Object{
  823. &corev1.Secret{
  824. ObjectMeta: metav1.ObjectMeta{
  825. Name: certClientCertName,
  826. Namespace: "default",
  827. },
  828. Data: map[string][]byte{
  829. "tls.crt": []byte("-----BEGIN CERTIFICATE-----\nfakecert\n-----END CERTIFICATE-----"),
  830. },
  831. },
  832. &corev1.Secret{
  833. ObjectMeta: metav1.ObjectMeta{
  834. Name: certClientKeyName,
  835. Namespace: "default",
  836. },
  837. Data: map[string][]byte{
  838. "tls.key": []byte("-----BEGIN RSA PRIVATE KEY-----\nfakekey\n-----END RSA PRIVATE KEY-----"),
  839. },
  840. },
  841. }
  842. }
  843. func makeFakeCASource(kind, caData string) kclient.Object {
  844. if kind == "secret" {
  845. return &corev1.Secret{
  846. ObjectMeta: metav1.ObjectMeta{
  847. Name: "conjur-cert",
  848. Namespace: "default",
  849. },
  850. Data: map[string][]byte{
  851. "ca": []byte(caData),
  852. },
  853. }
  854. }
  855. return &corev1.ConfigMap{
  856. ObjectMeta: metav1.ObjectMeta{
  857. Name: "conjur-cert",
  858. Namespace: "default",
  859. },
  860. Data: map[string]string{
  861. "ca": caData,
  862. },
  863. }
  864. }
  865. func createFakeJwtToken(expires bool) string {
  866. signingKey := []byte("fakekey")
  867. token := jwt.New(jwt.SigningMethodHS256)
  868. claims := token.Claims.(jwt.MapClaims)
  869. if expires {
  870. claims["exp"] = time.Now().Add(time.Minute * 30).Unix()
  871. }
  872. jwtTokenString, err := token.SignedString(signingKey)
  873. if err != nil {
  874. panic(err)
  875. }
  876. return jwtTokenString
  877. }
  878. // ConjurMockAPIClient is a mock implementation of the ApiClient interface.
  879. type ConjurMockAPIClient struct {
  880. }
  881. func (c *ConjurMockAPIClient) NewClientFromCert(_ conjurapi.Config) (SecretsClient, error) {
  882. return &fake.ConjurMockClient{}, nil
  883. }
  884. func (c *ConjurMockAPIClient) NewClientFromKey(_ conjurapi.Config, _ authn.LoginPair) (SecretsClient, error) {
  885. return &fake.ConjurMockClient{}, nil
  886. }
  887. func (c *ConjurMockAPIClient) NewClientFromJWT(_ conjurapi.Config) (SecretsClient, error) {
  888. return &fake.ConjurMockClient{}, nil
  889. }
  890. // EquateErrors returns true if the supplied errors are of the same type and
  891. // produce identical strings. This mirrors the error comparison behavior of
  892. // https://github.com/go-test/deep, which most Crossplane tests targeted before
  893. // we switched to go-cmp.
  894. //
  895. // This differs from cmpopts.EquateErrors, which does not test for error strings
  896. // and instead returns whether one error 'is' (in the errors.Is sense) the
  897. // other.
  898. func EquateErrors() cmp.Option {
  899. return cmp.Comparer(func(a, b error) bool {
  900. if a == nil || b == nil {
  901. return a == nil && b == nil
  902. }
  903. av := reflect.ValueOf(a)
  904. bv := reflect.ValueOf(b)
  905. if av.Type() != bv.Type() {
  906. return false
  907. }
  908. return a.Error() == b.Error()
  909. })
  910. }