keyvault.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  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 keyvault
  13. import (
  14. "context"
  15. "encoding/json"
  16. "errors"
  17. "fmt"
  18. "os"
  19. "path"
  20. "regexp"
  21. "strings"
  22. "github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
  23. "github.com/Azure/go-autorest/autorest"
  24. "github.com/Azure/go-autorest/autorest/adal"
  25. "github.com/Azure/go-autorest/autorest/azure"
  26. kvauth "github.com/Azure/go-autorest/autorest/azure/auth"
  27. "github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
  28. "github.com/tidwall/gjson"
  29. authv1 "k8s.io/api/authentication/v1"
  30. corev1 "k8s.io/api/core/v1"
  31. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  32. "k8s.io/apimachinery/pkg/types"
  33. "k8s.io/client-go/kubernetes"
  34. kcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
  35. "sigs.k8s.io/controller-runtime/pkg/client"
  36. ctrlcfg "sigs.k8s.io/controller-runtime/pkg/client/config"
  37. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  38. smmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  39. "github.com/external-secrets/external-secrets/pkg/utils"
  40. )
  41. const (
  42. defaultObjType = "secret"
  43. objectTypeCert = "cert"
  44. objectTypeKey = "key"
  45. AzureDefaultAudience = "api://AzureADTokenExchange"
  46. AnnotationClientID = "azure.workload.identity/client-id"
  47. AnnotationTenantID = "azure.workload.identity/tenant-id"
  48. errUnexpectedStoreSpec = "unexpected store spec"
  49. errMissingAuthType = "cannot initialize Azure Client: no valid authType was specified"
  50. errPropNotExist = "property %s does not exist in key %s"
  51. errTagNotExist = "tag %s does not exist"
  52. errUnknownObjectType = "unknown Azure Keyvault object Type for %s"
  53. errUnmarshalJSONData = "error unmarshalling json data: %w"
  54. errDataFromCert = "cannot get use dataFrom to get certificate secret"
  55. errDataFromKey = "cannot get use dataFrom to get key secret"
  56. errMissingTenant = "missing tenantID in store config"
  57. errMissingSecretRef = "missing secretRef in provider config"
  58. errMissingClientIDSecret = "missing accessKeyID/secretAccessKey in store config"
  59. errFindSecret = "could not find secret %s/%s: %w"
  60. errFindDataKey = "no data for %q in secret '%s/%s'"
  61. errInvalidStore = "invalid store"
  62. errInvalidStoreSpec = "invalid store spec"
  63. errInvalidStoreProv = "invalid store provider"
  64. errInvalidAzureProv = "invalid azure keyvault provider"
  65. errInvalidSecRefClientID = "invalid AuthSecretRef.ClientID: %w"
  66. errInvalidSecRefClientSecret = "invalid AuthSecretRef.ClientSecret: %w"
  67. errInvalidSARef = "invalid ServiceAccountRef: %w"
  68. errMissingWorkloadEnvVars = "missing environment variables. AZURE_CLIENT_ID, AZURE_TENANT_ID and AZURE_FEDERATED_TOKEN_FILE must be set"
  69. errReadTokenFile = "unable to read token file %s: %w"
  70. errMissingSAAnnotation = "missing service account annotation: %s"
  71. )
  72. // https://github.com/external-secrets/external-secrets/issues/644
  73. var _ esv1beta1.SecretsClient = &Azure{}
  74. var _ esv1beta1.Provider = &Azure{}
  75. // interface to keyvault.BaseClient.
  76. type SecretClient interface {
  77. GetKey(ctx context.Context, vaultBaseURL string, keyName string, keyVersion string) (result keyvault.KeyBundle, err error)
  78. GetSecret(ctx context.Context, vaultBaseURL string, secretName string, secretVersion string) (result keyvault.SecretBundle, err error)
  79. GetSecretsComplete(ctx context.Context, vaultBaseURL string, maxresults *int32) (result keyvault.SecretListResultIterator, err error)
  80. GetCertificate(ctx context.Context, vaultBaseURL string, certificateName string, certificateVersion string) (result keyvault.CertificateBundle, err error)
  81. }
  82. type Azure struct {
  83. crClient client.Client
  84. kubeClient kcorev1.CoreV1Interface
  85. store esv1beta1.GenericStore
  86. provider *esv1beta1.AzureKVProvider
  87. baseClient SecretClient
  88. namespace string
  89. }
  90. func init() {
  91. esv1beta1.Register(&Azure{}, &esv1beta1.SecretStoreProvider{
  92. AzureKV: &esv1beta1.AzureKVProvider{},
  93. })
  94. }
  95. // Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
  96. func (a *Azure) Capabilities() esv1beta1.SecretStoreCapabilities {
  97. return esv1beta1.SecretStoreReadOnly
  98. }
  99. // NewClient constructs a new secrets client based on the provided store.
  100. func (a *Azure) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
  101. return newClient(ctx, store, kube, namespace)
  102. }
  103. func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
  104. provider, err := getProvider(store)
  105. if err != nil {
  106. return nil, err
  107. }
  108. cfg, err := ctrlcfg.GetConfig()
  109. if err != nil {
  110. return nil, err
  111. }
  112. kubeClient, err := kubernetes.NewForConfig(cfg)
  113. if err != nil {
  114. return nil, err
  115. }
  116. az := &Azure{
  117. crClient: kube,
  118. kubeClient: kubeClient.CoreV1(),
  119. store: store,
  120. namespace: namespace,
  121. provider: provider,
  122. }
  123. var authorizer autorest.Authorizer
  124. switch *provider.AuthType {
  125. case esv1beta1.AzureManagedIdentity:
  126. authorizer, err = az.authorizerForManagedIdentity()
  127. case esv1beta1.AzureServicePrincipal:
  128. authorizer, err = az.authorizerForServicePrincipal(ctx)
  129. case esv1beta1.AzureWorkloadIdentity:
  130. authorizer, err = az.authorizerForWorkloadIdentity(ctx, NewTokenProvider)
  131. default:
  132. err = fmt.Errorf(errMissingAuthType)
  133. }
  134. cl := keyvault.New()
  135. cl.Authorizer = authorizer
  136. az.baseClient = &cl
  137. return az, err
  138. }
  139. func getProvider(store esv1beta1.GenericStore) (*esv1beta1.AzureKVProvider, error) {
  140. spc := store.GetSpec()
  141. if spc == nil || spc.Provider.AzureKV == nil {
  142. return nil, errors.New(errUnexpectedStoreSpec)
  143. }
  144. return spc.Provider.AzureKV, nil
  145. }
  146. func (a *Azure) ValidateStore(store esv1beta1.GenericStore) error {
  147. if store == nil {
  148. return fmt.Errorf(errInvalidStore)
  149. }
  150. spc := store.GetSpec()
  151. if spc == nil {
  152. return fmt.Errorf(errInvalidStoreSpec)
  153. }
  154. if spc.Provider == nil {
  155. return fmt.Errorf(errInvalidStoreProv)
  156. }
  157. p := spc.Provider.AzureKV
  158. if p == nil {
  159. return fmt.Errorf(errInvalidAzureProv)
  160. }
  161. if p.AuthSecretRef != nil {
  162. if p.AuthSecretRef.ClientID != nil {
  163. if err := utils.ValidateSecretSelector(store, *p.AuthSecretRef.ClientID); err != nil {
  164. return fmt.Errorf(errInvalidSecRefClientID, err)
  165. }
  166. }
  167. if p.AuthSecretRef.ClientSecret != nil {
  168. if err := utils.ValidateSecretSelector(store, *p.AuthSecretRef.ClientSecret); err != nil {
  169. return fmt.Errorf(errInvalidSecRefClientSecret, err)
  170. }
  171. }
  172. }
  173. if p.ServiceAccountRef != nil {
  174. if err := utils.ValidateServiceAccountSelector(store, *p.ServiceAccountRef); err != nil {
  175. return fmt.Errorf(errInvalidSARef, err)
  176. }
  177. }
  178. return nil
  179. }
  180. func (a *Azure) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushRemoteRef) error {
  181. return fmt.Errorf("not implemented")
  182. }
  183. // Not Implemented PushSecret.
  184. func (a *Azure) PushSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
  185. return fmt.Errorf("not implemented")
  186. }
  187. // Implements store.Client.GetAllSecrets Interface.
  188. // Retrieves a map[string][]byte with the secret names as key and the secret itself as the calue.
  189. func (a *Azure) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  190. basicClient := a.baseClient
  191. secretsMap := make(map[string][]byte)
  192. checkTags := len(ref.Tags) > 0
  193. checkName := ref.Name != nil && len(ref.Name.RegExp) > 0
  194. secretListIter, err := basicClient.GetSecretsComplete(context.Background(), *a.provider.VaultURL, nil)
  195. if err != nil {
  196. return nil, err
  197. }
  198. for secretListIter.NotDone() {
  199. secretList := secretListIter.Response().Value
  200. for _, secret := range *secretList {
  201. ok, secretName := isValidSecret(checkTags, checkName, ref, secret)
  202. if !ok {
  203. continue
  204. }
  205. secretResp, err := basicClient.GetSecret(context.Background(), *a.provider.VaultURL, secretName, "")
  206. if err != nil {
  207. return nil, err
  208. }
  209. secretValue := *secretResp.Value
  210. secretsMap[secretName] = []byte(secretValue)
  211. }
  212. err = secretListIter.Next()
  213. if err != nil {
  214. return nil, err
  215. }
  216. }
  217. return secretsMap, nil
  218. }
  219. // Retrieves a tag value if specified and all tags in JSON format if not.
  220. func getSecretTag(tags map[string]*string, property string) ([]byte, error) {
  221. if property == "" {
  222. secretTagsData := make(map[string]string)
  223. for k, v := range tags {
  224. secretTagsData[k] = *v
  225. }
  226. return json.Marshal(secretTagsData)
  227. }
  228. if val, exist := tags[property]; exist {
  229. return []byte(*val), nil
  230. }
  231. idx := strings.Index(property, ".")
  232. if idx < 0 {
  233. return nil, fmt.Errorf(errTagNotExist, property)
  234. }
  235. if idx > 0 {
  236. tagName := property[0:idx]
  237. if val, exist := tags[tagName]; exist {
  238. key := strings.Replace(property, tagName+".", "", 1)
  239. return getProperty(*val, key, property)
  240. }
  241. }
  242. return nil, fmt.Errorf(errTagNotExist, property)
  243. }
  244. // Retrieves a property value if specified and the secret value if not.
  245. func getProperty(secret, property, key string) ([]byte, error) {
  246. if property == "" {
  247. return []byte(secret), nil
  248. }
  249. res := gjson.Get(secret, property)
  250. if !res.Exists() {
  251. idx := strings.Index(property, ".")
  252. if idx < 0 {
  253. return nil, fmt.Errorf(errPropNotExist, property, key)
  254. }
  255. escaped := strings.ReplaceAll(property, ".", "\\.")
  256. jValue := gjson.Get(secret, escaped)
  257. if jValue.Exists() {
  258. return []byte(jValue.String()), nil
  259. }
  260. return nil, fmt.Errorf(errPropNotExist, property, key)
  261. }
  262. return []byte(res.String()), nil
  263. }
  264. // Implements store.Client.GetSecret Interface.
  265. // Retrieves a secret/Key/Certificate/Tag with the secret name defined in ref.Name
  266. // The Object Type is defined as a prefix in the ref.Name , if no prefix is defined , we assume a secret is required.
  267. func (a *Azure) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  268. objectType, secretName := getObjType(ref)
  269. switch objectType {
  270. case defaultObjType:
  271. // returns a SecretBundle with the secret value
  272. // https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault#SecretBundle
  273. secretResp, err := a.baseClient.GetSecret(context.Background(), *a.provider.VaultURL, secretName, ref.Version)
  274. if err != nil {
  275. return nil, err
  276. }
  277. if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
  278. return getSecretTag(secretResp.Tags, ref.Property)
  279. }
  280. return getProperty(*secretResp.Value, ref.Property, ref.Key)
  281. case objectTypeCert:
  282. // returns a CertBundle. We return CER contents of x509 certificate
  283. // see: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault#CertificateBundle
  284. certResp, err := a.baseClient.GetCertificate(context.Background(), *a.provider.VaultURL, secretName, ref.Version)
  285. if err != nil {
  286. return nil, err
  287. }
  288. if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
  289. return getSecretTag(certResp.Tags, ref.Property)
  290. }
  291. return *certResp.Cer, nil
  292. case objectTypeKey:
  293. // returns a KeyBundle that contains a jwk
  294. // azure kv returns only public keys
  295. // see: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault#KeyBundle
  296. keyResp, err := a.baseClient.GetKey(context.Background(), *a.provider.VaultURL, secretName, ref.Version)
  297. if err != nil {
  298. return nil, err
  299. }
  300. if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
  301. return getSecretTag(keyResp.Tags, ref.Property)
  302. }
  303. return json.Marshal(keyResp.Key)
  304. }
  305. return nil, fmt.Errorf(errUnknownObjectType, secretName)
  306. }
  307. // returns a SecretBundle with the tags values.
  308. func (a *Azure) getSecretTags(ref esv1beta1.ExternalSecretDataRemoteRef) (map[string]*string, error) {
  309. _, secretName := getObjType(ref)
  310. secretResp, err := a.baseClient.GetSecret(context.Background(), *a.provider.VaultURL, secretName, ref.Version)
  311. if err != nil {
  312. return nil, err
  313. }
  314. secretTagsData := make(map[string]*string)
  315. for tagname, tagval := range secretResp.Tags {
  316. name := secretName + "_" + tagname
  317. kv := make(map[string]string)
  318. err = json.Unmarshal([]byte(*tagval), &kv)
  319. // if the tagvalue is not in JSON format then we added to secretTagsData we added as it is
  320. if err != nil {
  321. secretTagsData[name] = tagval
  322. } else {
  323. for k, v := range kv {
  324. value := v
  325. secretTagsData[name+"_"+k] = &value
  326. }
  327. }
  328. }
  329. return secretTagsData, nil
  330. }
  331. // Implements store.Client.GetSecretMap Interface.
  332. // New version of GetSecretMap.
  333. func (a *Azure) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  334. objectType, secretName := getObjType(ref)
  335. switch objectType {
  336. case defaultObjType:
  337. data, err := a.GetSecret(ctx, ref)
  338. if err != nil {
  339. return nil, err
  340. }
  341. if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
  342. tags, _ := a.getSecretTags(ref)
  343. return getSecretMapProperties(tags, ref.Key, ref.Property), nil
  344. }
  345. return getSecretMapMap(data)
  346. case objectTypeCert:
  347. return nil, fmt.Errorf(errDataFromCert)
  348. case objectTypeKey:
  349. return nil, fmt.Errorf(errDataFromKey)
  350. }
  351. return nil, fmt.Errorf(errUnknownObjectType, secretName)
  352. }
  353. func getSecretMapMap(data []byte) (map[string][]byte, error) {
  354. kv := make(map[string]json.RawMessage)
  355. err := json.Unmarshal(data, &kv)
  356. if err != nil {
  357. return nil, fmt.Errorf(errUnmarshalJSONData, err)
  358. }
  359. secretData := make(map[string][]byte)
  360. for k, v := range kv {
  361. var strVal string
  362. err = json.Unmarshal(v, &strVal)
  363. if err == nil {
  364. secretData[k] = []byte(strVal)
  365. } else {
  366. secretData[k] = v
  367. }
  368. }
  369. return secretData, nil
  370. }
  371. func getSecretMapProperties(tags map[string]*string, key, property string) map[string][]byte {
  372. tagByteArray := make(map[string][]byte)
  373. if property != "" {
  374. keyPropertyName := key + "_" + property
  375. singleTag, _ := getSecretTag(tags, keyPropertyName)
  376. tagByteArray[keyPropertyName] = singleTag
  377. return tagByteArray
  378. }
  379. for k, v := range tags {
  380. tagByteArray[k] = []byte(*v)
  381. }
  382. return tagByteArray
  383. }
  384. func (a *Azure) authorizerForWorkloadIdentity(ctx context.Context, tokenProvider tokenProviderFunc) (autorest.Authorizer, error) {
  385. aadEndpoint := AadEndpointForType(a.provider.EnvironmentType)
  386. kvResource := kvResourceForProviderConfig(a.provider.EnvironmentType)
  387. // if no serviceAccountRef was provided
  388. // we expect certain env vars to be present.
  389. // They are set by the azure workload identity webhook.
  390. if a.provider.ServiceAccountRef == nil {
  391. clientID := os.Getenv("AZURE_CLIENT_ID")
  392. tenantID := os.Getenv("AZURE_TENANT_ID")
  393. tokenFilePath := os.Getenv("AZURE_FEDERATED_TOKEN_FILE")
  394. if clientID == "" || tenantID == "" || tokenFilePath == "" {
  395. return nil, errors.New(errMissingWorkloadEnvVars)
  396. }
  397. token, err := os.ReadFile(tokenFilePath)
  398. if err != nil {
  399. return nil, fmt.Errorf(errReadTokenFile, tokenFilePath, err)
  400. }
  401. tp, err := tokenProvider(ctx, string(token), clientID, tenantID, aadEndpoint, kvResource)
  402. if err != nil {
  403. return nil, err
  404. }
  405. return autorest.NewBearerAuthorizer(tp), nil
  406. }
  407. ns := a.namespace
  408. if a.store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind {
  409. ns = *a.provider.ServiceAccountRef.Namespace
  410. }
  411. var sa corev1.ServiceAccount
  412. err := a.crClient.Get(ctx, types.NamespacedName{
  413. Name: a.provider.ServiceAccountRef.Name,
  414. Namespace: ns,
  415. }, &sa)
  416. if err != nil {
  417. return nil, err
  418. }
  419. clientID, ok := sa.ObjectMeta.Annotations[AnnotationClientID]
  420. if !ok {
  421. return nil, fmt.Errorf(errMissingSAAnnotation, AnnotationClientID)
  422. }
  423. tenantID, ok := sa.ObjectMeta.Annotations[AnnotationTenantID]
  424. if !ok {
  425. return nil, fmt.Errorf(errMissingSAAnnotation, AnnotationTenantID)
  426. }
  427. audiences := []string{AzureDefaultAudience}
  428. if len(a.provider.ServiceAccountRef.Audiences) > 0 {
  429. audiences = append(audiences, a.provider.ServiceAccountRef.Audiences...)
  430. }
  431. token, err := FetchSAToken(ctx, ns, a.provider.ServiceAccountRef.Name, audiences, a.kubeClient)
  432. if err != nil {
  433. return nil, err
  434. }
  435. tp, err := tokenProvider(ctx, token, clientID, tenantID, aadEndpoint, kvResource)
  436. if err != nil {
  437. return nil, err
  438. }
  439. return autorest.NewBearerAuthorizer(tp), nil
  440. }
  441. func FetchSAToken(ctx context.Context, ns, name string, audiences []string, kubeClient kcorev1.CoreV1Interface) (string, error) {
  442. token, err := kubeClient.ServiceAccounts(ns).CreateToken(ctx, name, &authv1.TokenRequest{
  443. Spec: authv1.TokenRequestSpec{
  444. Audiences: audiences,
  445. },
  446. }, metav1.CreateOptions{})
  447. if err != nil {
  448. return "", err
  449. }
  450. return token.Status.Token, nil
  451. }
  452. // tokenProvider satisfies the adal.OAuthTokenProvider interface.
  453. type tokenProvider struct {
  454. accessToken string
  455. }
  456. type tokenProviderFunc func(ctx context.Context, token, clientID, tenantID, aadEndpoint, kvResource string) (adal.OAuthTokenProvider, error)
  457. func NewTokenProvider(ctx context.Context, token, clientID, tenantID, aadEndpoint, kvResource string) (adal.OAuthTokenProvider, error) {
  458. // exchange token with Azure AccessToken
  459. cred := confidential.NewCredFromAssertionCallback(func(ctx context.Context, aro confidential.AssertionRequestOptions) (string, error) {
  460. return token, nil
  461. })
  462. cClient, err := confidential.New(clientID, cred, confidential.WithAuthority(
  463. fmt.Sprintf("%s%s/oauth2/token", aadEndpoint, tenantID),
  464. ))
  465. if err != nil {
  466. return nil, err
  467. }
  468. scope := kvResource
  469. // .default needs to be added to the scope
  470. if !strings.Contains(kvResource, ".default") {
  471. scope = fmt.Sprintf("%s/.default", kvResource)
  472. }
  473. authRes, err := cClient.AcquireTokenByCredential(ctx, []string{
  474. scope,
  475. })
  476. if err != nil {
  477. return nil, err
  478. }
  479. return &tokenProvider{
  480. accessToken: authRes.AccessToken,
  481. }, nil
  482. }
  483. func (t *tokenProvider) OAuthToken() string {
  484. return t.accessToken
  485. }
  486. func (a *Azure) authorizerForManagedIdentity() (autorest.Authorizer, error) {
  487. msiConfig := kvauth.NewMSIConfig()
  488. msiConfig.Resource = kvResourceForProviderConfig(a.provider.EnvironmentType)
  489. if a.provider.IdentityID != nil {
  490. msiConfig.ClientID = *a.provider.IdentityID
  491. }
  492. return msiConfig.Authorizer()
  493. }
  494. func (a *Azure) authorizerForServicePrincipal(ctx context.Context) (autorest.Authorizer, error) {
  495. if a.provider.TenantID == nil {
  496. return nil, fmt.Errorf(errMissingTenant)
  497. }
  498. if a.provider.AuthSecretRef == nil {
  499. return nil, fmt.Errorf(errMissingSecretRef)
  500. }
  501. if a.provider.AuthSecretRef.ClientID == nil || a.provider.AuthSecretRef.ClientSecret == nil {
  502. return nil, fmt.Errorf(errMissingClientIDSecret)
  503. }
  504. clusterScoped := false
  505. if a.store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind {
  506. clusterScoped = true
  507. }
  508. cid, err := a.secretKeyRef(ctx, a.store.GetNamespace(), *a.provider.AuthSecretRef.ClientID, clusterScoped)
  509. if err != nil {
  510. return nil, err
  511. }
  512. csec, err := a.secretKeyRef(ctx, a.store.GetNamespace(), *a.provider.AuthSecretRef.ClientSecret, clusterScoped)
  513. if err != nil {
  514. return nil, err
  515. }
  516. clientCredentialsConfig := kvauth.NewClientCredentialsConfig(cid, csec, *a.provider.TenantID)
  517. clientCredentialsConfig.Resource = kvResourceForProviderConfig(a.provider.EnvironmentType)
  518. clientCredentialsConfig.AADEndpoint = AadEndpointForType(a.provider.EnvironmentType)
  519. return clientCredentialsConfig.Authorizer()
  520. }
  521. // secretKeyRef fetch a secret key.
  522. func (a *Azure) secretKeyRef(ctx context.Context, namespace string, secretRef smmeta.SecretKeySelector, clusterScoped bool) (string, error) {
  523. var secret corev1.Secret
  524. ref := types.NamespacedName{
  525. Namespace: namespace,
  526. Name: secretRef.Name,
  527. }
  528. if clusterScoped && secretRef.Namespace != nil {
  529. ref.Namespace = *secretRef.Namespace
  530. }
  531. err := a.crClient.Get(ctx, ref, &secret)
  532. if err != nil {
  533. return "", fmt.Errorf(errFindSecret, ref.Namespace, ref.Name, err)
  534. }
  535. keyBytes, ok := secret.Data[secretRef.Key]
  536. if !ok {
  537. return "", fmt.Errorf(errFindDataKey, secretRef.Key, secretRef.Name, namespace)
  538. }
  539. value := strings.TrimSpace(string(keyBytes))
  540. return value, nil
  541. }
  542. func (a *Azure) Close(ctx context.Context) error {
  543. return nil
  544. }
  545. func (a *Azure) Validate() (esv1beta1.ValidationResult, error) {
  546. return esv1beta1.ValidationResultReady, nil
  547. }
  548. func AadEndpointForType(t esv1beta1.AzureEnvironmentType) string {
  549. switch t {
  550. case esv1beta1.AzureEnvironmentPublicCloud:
  551. return azure.PublicCloud.ActiveDirectoryEndpoint
  552. case esv1beta1.AzureEnvironmentChinaCloud:
  553. return azure.ChinaCloud.ActiveDirectoryEndpoint
  554. case esv1beta1.AzureEnvironmentUSGovernmentCloud:
  555. return azure.USGovernmentCloud.ActiveDirectoryEndpoint
  556. case esv1beta1.AzureEnvironmentGermanCloud:
  557. return azure.GermanCloud.ActiveDirectoryEndpoint
  558. default:
  559. return azure.PublicCloud.ActiveDirectoryEndpoint
  560. }
  561. }
  562. func kvResourceForProviderConfig(t esv1beta1.AzureEnvironmentType) string {
  563. var res string
  564. switch t {
  565. case esv1beta1.AzureEnvironmentPublicCloud:
  566. res = azure.PublicCloud.KeyVaultEndpoint
  567. case esv1beta1.AzureEnvironmentChinaCloud:
  568. res = azure.ChinaCloud.KeyVaultEndpoint
  569. case esv1beta1.AzureEnvironmentUSGovernmentCloud:
  570. res = azure.USGovernmentCloud.KeyVaultEndpoint
  571. case esv1beta1.AzureEnvironmentGermanCloud:
  572. res = azure.GermanCloud.KeyVaultEndpoint
  573. default:
  574. res = azure.PublicCloud.KeyVaultEndpoint
  575. }
  576. return strings.TrimSuffix(res, "/")
  577. }
  578. func getObjType(ref esv1beta1.ExternalSecretDataRemoteRef) (string, string) {
  579. objectType := defaultObjType
  580. secretName := ref.Key
  581. nameSplitted := strings.Split(secretName, "/")
  582. if len(nameSplitted) > 1 {
  583. objectType = nameSplitted[0]
  584. secretName = nameSplitted[1]
  585. // TODO: later tokens can be used to read the secret tags
  586. }
  587. return objectType, secretName
  588. }
  589. func isValidSecret(checkTags, checkName bool, ref esv1beta1.ExternalSecretFind, secret keyvault.SecretItem) (bool, string) {
  590. if secret.ID == nil || !*secret.Attributes.Enabled {
  591. return false, ""
  592. }
  593. if checkTags && !okByTags(ref, secret) {
  594. return false, ""
  595. }
  596. secretName := path.Base(*secret.ID)
  597. if checkName && !okByName(ref, secretName) {
  598. return false, ""
  599. }
  600. return true, secretName
  601. }
  602. func okByName(ref esv1beta1.ExternalSecretFind, secretName string) bool {
  603. matches, _ := regexp.MatchString(ref.Name.RegExp, secretName)
  604. return matches
  605. }
  606. func okByTags(ref esv1beta1.ExternalSecretFind, secret keyvault.SecretItem) bool {
  607. tagsFound := true
  608. for k, v := range ref.Tags {
  609. if val, ok := secret.Tags[k]; !ok || *val != v {
  610. tagsFound = false
  611. break
  612. }
  613. }
  614. return tagsFound
  615. }