aws.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package integrations
  2. import (
  3. "context"
  4. "fmt"
  5. "gorm.io/gorm"
  6. "github.com/aws/aws-sdk-go/aws"
  7. "github.com/aws/aws-sdk-go/service/sts"
  8. "github.com/aws/aws-sdk-go/aws/credentials"
  9. "github.com/aws/aws-sdk-go/aws/session"
  10. "github.com/porter-dev/porter/api/types"
  11. "sigs.k8s.io/aws-iam-authenticator/pkg/token"
  12. )
  13. // AWSIntegration is an auth mechanism that uses a AWS IAM user to
  14. // authenticate
  15. type AWSIntegration struct {
  16. gorm.Model
  17. // The id of the user that linked this auth mechanism
  18. UserID uint `json:"user_id"`
  19. // The project that this integration belongs to
  20. ProjectID uint `json:"project_id"`
  21. // The AWS arn this is integration is linked to
  22. AWSArn string `json:"aws_arn"`
  23. // The optional AWS region (required by some session configurations)
  24. AWSRegion string `json:"aws_region"`
  25. // The assumed role ARN to use for sessions
  26. AWSAssumeRoleArn string
  27. // ------------------------------------------------------------------
  28. // All fields encrypted before storage.
  29. // ------------------------------------------------------------------
  30. // The AWS cluster ID
  31. // See https://github.com/kubernetes-sigs/aws-iam-authenticator#what-is-a-cluster-id
  32. AWSClusterID []byte `json:"aws_cluster_id"`
  33. // The AWS access key for this IAM user
  34. AWSAccessKeyID []byte `json:"aws_access_key_id"`
  35. // The AWS secret key for this IAM user
  36. AWSSecretAccessKey []byte `json:"aws_secret_access_key"`
  37. // An optional session token, if the user is assuming a role
  38. AWSSessionToken []byte `json:"aws_session_token"`
  39. }
  40. func (a *AWSIntegration) ToAWSIntegrationType() *types.AWSIntegration {
  41. return &types.AWSIntegration{
  42. CreatedAt: a.CreatedAt,
  43. ID: a.ID,
  44. UserID: a.UserID,
  45. ProjectID: a.ProjectID,
  46. AWSArn: a.AWSArn,
  47. }
  48. }
  49. // GetSession retrieves an AWS session to use based on the access key and secret
  50. // access key
  51. func (a *AWSIntegration) GetSession() (*session.Session, error) {
  52. awsConf := &aws.Config{
  53. Credentials: credentials.NewStaticCredentials(
  54. string(a.AWSAccessKeyID),
  55. string(a.AWSSecretAccessKey),
  56. string(a.AWSSessionToken),
  57. ),
  58. }
  59. if a.AWSRegion != "" {
  60. awsConf.Region = &a.AWSRegion
  61. }
  62. return session.NewSessionWithOptions(session.Options{
  63. SharedConfigState: session.SharedConfigEnable,
  64. Config: *awsConf,
  65. })
  66. }
  67. // PopulateAWSArn uses the access key/secret to get the caller identity, and
  68. // attaches it to the AWS integration
  69. func (a *AWSIntegration) PopulateAWSArn() error {
  70. sess, err := a.GetSession()
  71. if err != nil {
  72. return err
  73. }
  74. svc := sts.New(sess)
  75. result, err := svc.GetCallerIdentity(&sts.GetCallerIdentityInput{})
  76. if err != nil {
  77. return err
  78. }
  79. a.AWSArn = *result.Arn
  80. return nil
  81. }
  82. // GetBearerToken retrieves a bearer token for an AWS account
  83. func (a *AWSIntegration) GetBearerToken(
  84. ctx context.Context,
  85. getTokenCache GetTokenCacheFunc,
  86. setTokenCache SetTokenCacheFunc,
  87. clusterID string,
  88. shouldClusterIdOverride bool,
  89. ) (string, error) {
  90. cache, err := getTokenCache(ctx)
  91. // check the token cache for a non-expired token
  92. if cache != nil {
  93. if tok := cache.Token; err == nil && !cache.IsExpired() && len(tok) > 0 {
  94. return string(tok), nil
  95. }
  96. }
  97. generator, err := token.NewGenerator(false, false)
  98. if err != nil {
  99. return "", fmt.Errorf("error creating token generator: %w", err)
  100. }
  101. sess, err := a.GetSession()
  102. if err != nil {
  103. return "", fmt.Errorf("error getting session: %w", err)
  104. }
  105. var validClusterId string
  106. if shouldClusterIdOverride {
  107. validClusterId = clusterID
  108. } else {
  109. validClusterId = string(a.AWSClusterID)
  110. if validClusterId == "" {
  111. validClusterId = clusterID
  112. }
  113. }
  114. tok, err := generator.GetWithOptions(&token.GetTokenOptions{
  115. AssumeRoleARN: a.AWSAssumeRoleArn,
  116. Session: sess,
  117. ClusterID: validClusterId,
  118. })
  119. if err != nil {
  120. return "", fmt.Errorf("error generating token: %w", err)
  121. }
  122. err = setTokenCache(ctx, tok.Token, tok.Expiration)
  123. if err != nil {
  124. return "", fmt.Errorf("non-fatal error setting token cache: %w", err)
  125. }
  126. return tok.Token, nil
  127. }