akeyless_api.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  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 akeyless
  13. import (
  14. "context"
  15. "encoding/base64"
  16. "encoding/json"
  17. "errors"
  18. "fmt"
  19. "io"
  20. "os"
  21. "strings"
  22. aws_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/aws"
  23. azure_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/azure"
  24. gcp_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/gcp"
  25. "github.com/akeylesslabs/akeyless-go/v2"
  26. authenticationv1 "k8s.io/api/authentication/v1"
  27. corev1 "k8s.io/api/core/v1"
  28. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  29. "k8s.io/apimachinery/pkg/types"
  30. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  31. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  32. )
  33. var apiErr akeyless.GenericOpenAPIError
  34. const DefServiceAccountFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
  35. func (a *akeylessBase) GetToken(accessID, accType, accTypeParam string, k8sAuth *esv1beta1.AkeylessKubernetesAuth) (string, error) {
  36. ctx := context.Background()
  37. authBody := akeyless.NewAuthWithDefaults()
  38. authBody.AccessId = akeyless.PtrString(accessID)
  39. if accType == "api_key" || accType == "access_key" {
  40. authBody.AccessKey = akeyless.PtrString(accTypeParam)
  41. } else if accType == "k8s" {
  42. jwtString, err := a.getK8SServiceAccountJWT(ctx, k8sAuth)
  43. if err != nil {
  44. return "", fmt.Errorf("failed to read JWT with Kubernetes Auth from %v. error: %w", DefServiceAccountFile, err)
  45. }
  46. jwtStringBase64 := base64.StdEncoding.EncodeToString([]byte(jwtString))
  47. K8SAuthConfigName := accTypeParam
  48. authBody.AccessType = akeyless.PtrString(accType)
  49. authBody.K8sServiceAccountToken = akeyless.PtrString(jwtStringBase64)
  50. authBody.K8sAuthConfigName = akeyless.PtrString(K8SAuthConfigName)
  51. } else {
  52. cloudID, err := a.getCloudID(accType, accTypeParam)
  53. if err != nil {
  54. return "", errors.New("Require Cloud ID " + err.Error())
  55. }
  56. authBody.AccessType = akeyless.PtrString(accType)
  57. authBody.CloudId = akeyless.PtrString(cloudID)
  58. }
  59. authOut, res, err := a.RestAPI.Auth(ctx).Body(*authBody).Execute()
  60. if err != nil {
  61. if errors.As(err, &apiErr) {
  62. return "", fmt.Errorf("authentication failed: %v", string(apiErr.Body()))
  63. }
  64. return "", fmt.Errorf("authentication failed: %w", err)
  65. }
  66. defer res.Body.Close()
  67. token := authOut.GetToken()
  68. return token, nil
  69. }
  70. func (a *akeylessBase) GetSecretByType(secretName, token string, version int32) (string, error) {
  71. item, err := a.DescribeItem(secretName, token)
  72. if err != nil {
  73. return "", err
  74. }
  75. secretType := item.GetItemType()
  76. switch secretType {
  77. case "STATIC_SECRET":
  78. return a.GetStaticSecret(secretName, token, version)
  79. case "DYNAMIC_SECRET":
  80. return a.GetDynamicSecrets(secretName, token)
  81. case "ROTATED_SECRET":
  82. return a.GetRotatedSecrets(secretName, token, version)
  83. default:
  84. return "", fmt.Errorf("invalid item type: %v", secretType)
  85. }
  86. }
  87. func (a *akeylessBase) DescribeItem(itemName, token string) (*akeyless.Item, error) {
  88. ctx := context.Background()
  89. body := akeyless.DescribeItem{
  90. Name: itemName,
  91. }
  92. if strings.HasPrefix(token, "u-") {
  93. body.UidToken = &token
  94. } else {
  95. body.Token = &token
  96. }
  97. gsvOut, res, err := a.RestAPI.DescribeItem(ctx).Body(body).Execute()
  98. if err != nil {
  99. if errors.As(err, &apiErr) {
  100. return nil, fmt.Errorf("can't describe item: %v", string(apiErr.Body()))
  101. }
  102. return nil, fmt.Errorf("can't describe item: %w", err)
  103. }
  104. res.Body.Close()
  105. return &gsvOut, nil
  106. }
  107. func (a *akeylessBase) GetRotatedSecrets(secretName, token string, version int32) (string, error) {
  108. ctx := context.Background()
  109. body := akeyless.GetRotatedSecretValue{
  110. Names: secretName,
  111. Version: &version,
  112. }
  113. if strings.HasPrefix(token, "u-") {
  114. body.UidToken = &token
  115. } else {
  116. body.Token = &token
  117. }
  118. gsvOut, res, err := a.RestAPI.GetRotatedSecretValue(ctx).Body(body).Execute()
  119. if err != nil {
  120. if errors.As(err, &apiErr) {
  121. return "", fmt.Errorf("can't get rotated secret value: %v", string(apiErr.Body()))
  122. }
  123. return "", fmt.Errorf("can't get rotated secret value: %w", err)
  124. }
  125. res.Body.Close()
  126. valI, ok := gsvOut["value"]
  127. if ok {
  128. val, convert := valI.(map[string]interface{})
  129. if !convert {
  130. return "", fmt.Errorf("failure converting key from gsvOut")
  131. }
  132. if _, ok := val["payload"]; ok {
  133. return fmt.Sprintf("%v", val["payload"]), nil
  134. } else if _, ok := val["target_value"]; ok {
  135. out, err := json.Marshal(val["target_value"])
  136. if err != nil {
  137. return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
  138. }
  139. return string(out), nil
  140. } else {
  141. out, err := json.Marshal(val)
  142. if err != nil {
  143. return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
  144. }
  145. return string(out), nil
  146. }
  147. }
  148. out, err := json.Marshal(gsvOut)
  149. if err != nil {
  150. return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
  151. }
  152. return string(out), nil
  153. }
  154. func (a *akeylessBase) GetDynamicSecrets(secretName, token string) (string, error) {
  155. ctx := context.Background()
  156. body := akeyless.GetDynamicSecretValue{
  157. Name: secretName,
  158. }
  159. if strings.HasPrefix(token, "u-") {
  160. body.UidToken = &token
  161. } else {
  162. body.Token = &token
  163. }
  164. gsvOut, res, err := a.RestAPI.GetDynamicSecretValue(ctx).Body(body).Execute()
  165. if err != nil {
  166. if errors.As(err, &apiErr) {
  167. return "", fmt.Errorf("can't get dynamic secret value: %v", string(apiErr.Body()))
  168. }
  169. return "", fmt.Errorf("can't get dynamic secret value: %w", err)
  170. }
  171. res.Body.Close()
  172. out, err := json.Marshal(gsvOut)
  173. if err != nil {
  174. return "", fmt.Errorf("can't marshal dynamic secret value: %w", err)
  175. }
  176. return string(out), nil
  177. }
  178. func (a *akeylessBase) GetStaticSecret(secretName, token string, version int32) (string, error) {
  179. ctx := context.Background()
  180. gsvBody := akeyless.GetSecretValue{
  181. Names: []string{secretName},
  182. Version: &version,
  183. }
  184. if strings.HasPrefix(token, "u-") {
  185. gsvBody.UidToken = &token
  186. } else {
  187. gsvBody.Token = &token
  188. }
  189. gsvOut, res, err := a.RestAPI.GetSecretValue(ctx).Body(gsvBody).Execute()
  190. if err != nil {
  191. if errors.As(err, &apiErr) {
  192. return "", fmt.Errorf("can't get secret value: %v", string(apiErr.Body()))
  193. }
  194. return "", fmt.Errorf("can't get secret value: %w", err)
  195. }
  196. res.Body.Close()
  197. val, ok := gsvOut[secretName]
  198. if !ok {
  199. return "", fmt.Errorf("can't get secret: %v", secretName)
  200. }
  201. return val, nil
  202. }
  203. func (a *akeylessBase) getCloudID(provider, accTypeParam string) (string, error) {
  204. var cloudID string
  205. var err error
  206. switch provider {
  207. case "azure_ad":
  208. cloudID, err = azure_cloud_id.GetCloudId(accTypeParam)
  209. case "aws_iam":
  210. cloudID, err = aws_cloud_id.GetCloudId()
  211. case "gcp":
  212. cloudID, err = gcp_cloud_id.GetCloudID(accTypeParam)
  213. default:
  214. return "", fmt.Errorf("unable to determine provider: %s", provider)
  215. }
  216. return cloudID, err
  217. }
  218. func (a *akeylessBase) getK8SServiceAccountJWT(ctx context.Context, kubernetesAuth *esv1beta1.AkeylessKubernetesAuth) (string, error) {
  219. if kubernetesAuth != nil && kubernetesAuth.ServiceAccountRef != nil {
  220. // Kubernetes <v1.24 fetch token via ServiceAccount.Secrets[]
  221. jwt, err := a.getJWTFromServiceAccount(ctx, kubernetesAuth.ServiceAccountRef)
  222. if jwt != "" {
  223. return jwt, err
  224. }
  225. // Kubernetes >=v1.24: fetch token via TokenRequest API
  226. jwt, err = a.getJWTfromServiceAccountToken(ctx, *kubernetesAuth.ServiceAccountRef, nil, 600)
  227. if err != nil {
  228. return "", err
  229. }
  230. return jwt, nil
  231. } else if kubernetesAuth != nil && kubernetesAuth.SecretRef != nil {
  232. tokenRef := kubernetesAuth.SecretRef
  233. if tokenRef.Key == "" {
  234. tokenRef = kubernetesAuth.SecretRef.DeepCopy()
  235. tokenRef.Key = "token"
  236. }
  237. jwt, err := a.secretKeyRef(ctx, tokenRef)
  238. if err != nil {
  239. return "", err
  240. }
  241. return jwt, nil
  242. }
  243. return readK8SServiceAccountJWT()
  244. }
  245. func (a *akeylessBase) getJWTFromServiceAccount(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) (string, error) {
  246. serviceAccount := &corev1.ServiceAccount{}
  247. ref := types.NamespacedName{
  248. Namespace: a.namespace,
  249. Name: serviceAccountRef.Name,
  250. }
  251. if (a.store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind) &&
  252. (serviceAccountRef.Namespace != nil) {
  253. ref.Namespace = *serviceAccountRef.Namespace
  254. }
  255. err := a.kube.Get(ctx, ref, serviceAccount)
  256. if err != nil {
  257. return "", fmt.Errorf(errGetKubeSA, ref.Name, err)
  258. }
  259. if len(serviceAccount.Secrets) == 0 {
  260. return "", fmt.Errorf(errGetKubeSASecrets, ref.Name)
  261. }
  262. for _, tokenRef := range serviceAccount.Secrets {
  263. retval, err := a.secretKeyRef(ctx, &esmeta.SecretKeySelector{
  264. Name: tokenRef.Name,
  265. Namespace: &ref.Namespace,
  266. Key: "token",
  267. })
  268. if err != nil {
  269. continue
  270. }
  271. return retval, nil
  272. }
  273. return "", fmt.Errorf(errGetKubeSANoToken, ref.Name)
  274. }
  275. func (a *akeylessBase) secretKeyRef(ctx context.Context, secretRef *esmeta.SecretKeySelector) (string, error) {
  276. secret := &corev1.Secret{}
  277. ref := types.NamespacedName{
  278. Namespace: a.namespace,
  279. Name: secretRef.Name,
  280. }
  281. if (a.store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind) &&
  282. (secretRef.Namespace != nil) {
  283. ref.Namespace = *secretRef.Namespace
  284. }
  285. err := a.kube.Get(ctx, ref, secret)
  286. if err != nil {
  287. return "", fmt.Errorf(errGetKubeSecret, ref.Name, err)
  288. }
  289. keyBytes, ok := secret.Data[secretRef.Key]
  290. if !ok {
  291. return "", fmt.Errorf(errSecretKeyFmt, secretRef.Key)
  292. }
  293. value := string(keyBytes)
  294. valueStr := strings.TrimSpace(value)
  295. return valueStr, nil
  296. }
  297. func (a *akeylessBase) getJWTfromServiceAccountToken(ctx context.Context, serviceAccountRef esmeta.ServiceAccountSelector, additionalAud []string, expirationSeconds int64) (string, error) {
  298. audiences := serviceAccountRef.Audiences
  299. if len(additionalAud) > 0 {
  300. audiences = append(audiences, additionalAud...)
  301. }
  302. tokenRequest := &authenticationv1.TokenRequest{
  303. ObjectMeta: metav1.ObjectMeta{
  304. Namespace: a.namespace,
  305. },
  306. Spec: authenticationv1.TokenRequestSpec{
  307. Audiences: audiences,
  308. ExpirationSeconds: &expirationSeconds,
  309. },
  310. }
  311. if (a.store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind) &&
  312. (serviceAccountRef.Namespace != nil) {
  313. tokenRequest.Namespace = *serviceAccountRef.Namespace
  314. }
  315. tokenResponse, err := a.corev1.ServiceAccounts(tokenRequest.Namespace).CreateToken(ctx, serviceAccountRef.Name, tokenRequest, metav1.CreateOptions{})
  316. if err != nil {
  317. return "", fmt.Errorf(errGetKubeSATokenRequest, serviceAccountRef.Name, err)
  318. }
  319. return tokenResponse.Status.Token, nil
  320. }
  321. // readK8SServiceAccountJWT reads the JWT data for the Agent to submit to Akeyless Gateway.
  322. func readK8SServiceAccountJWT() (string, error) {
  323. data, err := os.Open(DefServiceAccountFile)
  324. if err != nil {
  325. return "", err
  326. }
  327. defer data.Close()
  328. contentBytes, err := io.ReadAll(data)
  329. if err != nil {
  330. return "", err
  331. }
  332. jwt := strings.TrimSpace(string(contentBytes))
  333. return jwt, nil
  334. }