akeyless_api.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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/v3"
  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(ctx context.Context, secretName, token string, version int32) (string, error) {
  71. item, err := a.DescribeItem(ctx, 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(ctx, secretName, token, version)
  79. case "DYNAMIC_SECRET":
  80. return a.GetDynamicSecrets(ctx, secretName, token)
  81. case "ROTATED_SECRET":
  82. return a.GetRotatedSecrets(ctx, secretName, token, version)
  83. default:
  84. return "", fmt.Errorf("invalid item type: %v", secretType)
  85. }
  86. }
  87. func (a *akeylessBase) DescribeItem(ctx context.Context, itemName, token string) (*akeyless.Item, error) {
  88. body := akeyless.DescribeItem{
  89. Name: itemName,
  90. }
  91. if strings.HasPrefix(token, "u-") {
  92. body.UidToken = &token
  93. } else {
  94. body.Token = &token
  95. }
  96. gsvOut, res, err := a.RestAPI.DescribeItem(ctx).Body(body).Execute()
  97. if err != nil {
  98. if errors.As(err, &apiErr) {
  99. return nil, fmt.Errorf("can't describe item: %v", string(apiErr.Body()))
  100. }
  101. return nil, fmt.Errorf("can't describe item: %w", err)
  102. }
  103. defer res.Body.Close()
  104. return &gsvOut, nil
  105. }
  106. func (a *akeylessBase) GetRotatedSecrets(ctx context.Context, secretName, token string, version int32) (string, error) {
  107. body := akeyless.GetRotatedSecretValue{
  108. Names: secretName,
  109. Version: &version,
  110. }
  111. if strings.HasPrefix(token, "u-") {
  112. body.UidToken = &token
  113. } else {
  114. body.Token = &token
  115. }
  116. gsvOut, res, err := a.RestAPI.GetRotatedSecretValue(ctx).Body(body).Execute()
  117. if err != nil {
  118. if errors.As(err, &apiErr) {
  119. return "", fmt.Errorf("can't get rotated secret value: %v", string(apiErr.Body()))
  120. }
  121. return "", fmt.Errorf("can't get rotated secret value: %w", err)
  122. }
  123. defer res.Body.Close()
  124. valI, ok := gsvOut["value"]
  125. if ok {
  126. val, convert := valI.(map[string]interface{})
  127. if !convert {
  128. return "", fmt.Errorf("failure converting key from gsvOut")
  129. }
  130. if _, ok := val["payload"]; ok {
  131. return fmt.Sprintf("%v", val["payload"]), nil
  132. } else if _, ok := val["target_value"]; ok {
  133. out, err := json.Marshal(val["target_value"])
  134. if err != nil {
  135. return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
  136. }
  137. return string(out), nil
  138. } else {
  139. out, err := json.Marshal(val)
  140. if err != nil {
  141. return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
  142. }
  143. return string(out), nil
  144. }
  145. }
  146. out, err := json.Marshal(gsvOut)
  147. if err != nil {
  148. return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
  149. }
  150. return string(out), nil
  151. }
  152. func (a *akeylessBase) GetDynamicSecrets(ctx context.Context, secretName, token string) (string, error) {
  153. body := akeyless.GetDynamicSecretValue{
  154. Name: secretName,
  155. }
  156. if strings.HasPrefix(token, "u-") {
  157. body.UidToken = &token
  158. } else {
  159. body.Token = &token
  160. }
  161. gsvOut, res, err := a.RestAPI.GetDynamicSecretValue(ctx).Body(body).Execute()
  162. if err != nil {
  163. if errors.As(err, &apiErr) {
  164. return "", fmt.Errorf("can't get dynamic secret value: %v", string(apiErr.Body()))
  165. }
  166. return "", fmt.Errorf("can't get dynamic secret value: %w", err)
  167. }
  168. defer res.Body.Close()
  169. out, err := json.Marshal(gsvOut)
  170. if err != nil {
  171. return "", fmt.Errorf("can't marshal dynamic secret value: %w", err)
  172. }
  173. return string(out), nil
  174. }
  175. func (a *akeylessBase) GetStaticSecret(ctx context.Context, secretName, token string, version int32) (string, error) {
  176. gsvBody := akeyless.GetSecretValue{
  177. Names: []string{secretName},
  178. Version: &version,
  179. }
  180. if strings.HasPrefix(token, "u-") {
  181. gsvBody.UidToken = &token
  182. } else {
  183. gsvBody.Token = &token
  184. }
  185. gsvOut, res, err := a.RestAPI.GetSecretValue(ctx).Body(gsvBody).Execute()
  186. if err != nil {
  187. if errors.As(err, &apiErr) {
  188. return "", fmt.Errorf("can't get secret value: %v", string(apiErr.Body()))
  189. }
  190. return "", fmt.Errorf("can't get secret value: %w", err)
  191. }
  192. defer res.Body.Close()
  193. val, ok := gsvOut[secretName]
  194. if !ok {
  195. return "", fmt.Errorf("can't get secret: %v", secretName)
  196. }
  197. return val, nil
  198. }
  199. func (a *akeylessBase) getCloudID(provider, accTypeParam string) (string, error) {
  200. var cloudID string
  201. var err error
  202. switch provider {
  203. case "azure_ad":
  204. cloudID, err = azure_cloud_id.GetCloudId(accTypeParam)
  205. case "aws_iam":
  206. cloudID, err = aws_cloud_id.GetCloudId()
  207. case "gcp":
  208. cloudID, err = gcp_cloud_id.GetCloudID(accTypeParam)
  209. default:
  210. return "", fmt.Errorf("unable to determine provider: %s", provider)
  211. }
  212. return cloudID, err
  213. }
  214. func (a *akeylessBase) ListSecrets(ctx context.Context, path, tag, token string) ([]string, error) {
  215. secretTypes := &[]string{"static-secret", "dynamic-secret", "rotated-secret"}
  216. MinimalView := true
  217. if tag != "" {
  218. MinimalView = false
  219. }
  220. gsvBody := akeyless.ListItems{
  221. Filter: &path,
  222. Type: secretTypes,
  223. MinimalView: &MinimalView,
  224. Tag: &tag,
  225. }
  226. if strings.HasPrefix(token, "u-") {
  227. gsvBody.UidToken = &token
  228. } else {
  229. gsvBody.Token = &token
  230. }
  231. lipOut, res, err := a.RestAPI.ListItems(ctx).Body(gsvBody).Execute()
  232. if err != nil {
  233. if errors.As(err, &apiErr) {
  234. return nil, fmt.Errorf("can't get secrets list: %v", string(apiErr.Body()))
  235. }
  236. return nil, fmt.Errorf("error on get secrets list: %w", err)
  237. }
  238. defer res.Body.Close()
  239. if lipOut.Items == nil {
  240. return nil, nil
  241. }
  242. listNames := make([]string, 0)
  243. for _, v := range *lipOut.Items {
  244. if path == "" || strings.HasPrefix(*v.ItemName, path) {
  245. listNames = append(listNames, *v.ItemName)
  246. }
  247. }
  248. return listNames, nil
  249. }
  250. func (a *akeylessBase) getK8SServiceAccountJWT(ctx context.Context, kubernetesAuth *esv1beta1.AkeylessKubernetesAuth) (string, error) {
  251. if kubernetesAuth != nil {
  252. if kubernetesAuth.ServiceAccountRef != nil {
  253. // Kubernetes <v1.24 fetch token via ServiceAccount.Secrets[]
  254. jwt, err := a.getJWTFromServiceAccount(ctx, kubernetesAuth.ServiceAccountRef)
  255. if jwt != "" {
  256. return jwt, err
  257. }
  258. // Kubernetes >=v1.24: fetch token via TokenRequest API
  259. jwt, err = a.getJWTfromServiceAccountToken(ctx, *kubernetesAuth.ServiceAccountRef, nil, 600)
  260. if err != nil {
  261. return "", err
  262. }
  263. return jwt, nil
  264. } else if kubernetesAuth.SecretRef != nil {
  265. tokenRef := kubernetesAuth.SecretRef
  266. if tokenRef.Key == "" {
  267. tokenRef = kubernetesAuth.SecretRef.DeepCopy()
  268. tokenRef.Key = "token"
  269. }
  270. jwt, err := a.secretKeyRef(ctx, tokenRef)
  271. if err != nil {
  272. return "", err
  273. }
  274. return jwt, nil
  275. }
  276. }
  277. return readK8SServiceAccountJWT()
  278. }
  279. func (a *akeylessBase) getJWTFromServiceAccount(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) (string, error) {
  280. serviceAccount := &corev1.ServiceAccount{}
  281. ref := types.NamespacedName{
  282. Namespace: a.namespace,
  283. Name: serviceAccountRef.Name,
  284. }
  285. if (a.storeKind == esv1beta1.ClusterSecretStoreKind) &&
  286. (serviceAccountRef.Namespace != nil) {
  287. ref.Namespace = *serviceAccountRef.Namespace
  288. }
  289. err := a.kube.Get(ctx, ref, serviceAccount)
  290. if err != nil {
  291. return "", fmt.Errorf(errGetKubeSA, ref.Name, err)
  292. }
  293. if len(serviceAccount.Secrets) == 0 {
  294. return "", fmt.Errorf(errGetKubeSASecrets, ref.Name)
  295. }
  296. for _, tokenRef := range serviceAccount.Secrets {
  297. retval, err := a.secretKeyRef(ctx, &esmeta.SecretKeySelector{
  298. Name: tokenRef.Name,
  299. Namespace: &ref.Namespace,
  300. Key: "token",
  301. })
  302. if err != nil {
  303. continue
  304. }
  305. return retval, nil
  306. }
  307. return "", fmt.Errorf(errGetKubeSANoToken, ref.Name)
  308. }
  309. func (a *akeylessBase) secretKeyRef(ctx context.Context, secretRef *esmeta.SecretKeySelector) (string, error) {
  310. secret := &corev1.Secret{}
  311. ref := types.NamespacedName{
  312. Namespace: a.namespace,
  313. Name: secretRef.Name,
  314. }
  315. if (a.storeKind == esv1beta1.ClusterSecretStoreKind) &&
  316. (secretRef.Namespace != nil) {
  317. ref.Namespace = *secretRef.Namespace
  318. }
  319. err := a.kube.Get(ctx, ref, secret)
  320. if err != nil {
  321. return "", fmt.Errorf(errGetKubeSecret, ref.Name, err)
  322. }
  323. keyBytes, ok := secret.Data[secretRef.Key]
  324. if !ok {
  325. return "", fmt.Errorf(errSecretKeyFmt, secretRef.Key)
  326. }
  327. value := string(keyBytes)
  328. valueStr := strings.TrimSpace(value)
  329. return valueStr, nil
  330. }
  331. func (a *akeylessBase) getJWTfromServiceAccountToken(ctx context.Context, serviceAccountRef esmeta.ServiceAccountSelector, additionalAud []string, expirationSeconds int64) (string, error) {
  332. audiences := serviceAccountRef.Audiences
  333. if len(additionalAud) > 0 {
  334. audiences = append(audiences, additionalAud...)
  335. }
  336. tokenRequest := &authenticationv1.TokenRequest{
  337. ObjectMeta: metav1.ObjectMeta{
  338. Namespace: a.namespace,
  339. },
  340. Spec: authenticationv1.TokenRequestSpec{
  341. Audiences: audiences,
  342. ExpirationSeconds: &expirationSeconds,
  343. },
  344. }
  345. if (a.storeKind == esv1beta1.ClusterSecretStoreKind) &&
  346. (serviceAccountRef.Namespace != nil) {
  347. tokenRequest.Namespace = *serviceAccountRef.Namespace
  348. }
  349. tokenResponse, err := a.corev1.ServiceAccounts(tokenRequest.Namespace).CreateToken(ctx, serviceAccountRef.Name, tokenRequest, metav1.CreateOptions{})
  350. if err != nil {
  351. return "", fmt.Errorf(errGetKubeSATokenRequest, serviceAccountRef.Name, err)
  352. }
  353. return tokenResponse.Status.Token, nil
  354. }
  355. // readK8SServiceAccountJWT reads the JWT data for the Agent to submit to Akeyless Gateway.
  356. func readK8SServiceAccountJWT() (string, error) {
  357. data, err := os.Open(DefServiceAccountFile)
  358. if err != nil {
  359. return "", err
  360. }
  361. defer data.Close()
  362. contentBytes, err := io.ReadAll(data)
  363. if err != nil {
  364. return "", err
  365. }
  366. jwt := strings.TrimSpace(string(contentBytes))
  367. return jwt, nil
  368. }