credentials.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. package cloudwatch
  2. import (
  3. "fmt"
  4. "os"
  5. "strings"
  6. "sync"
  7. "time"
  8. "github.com/aws/aws-sdk-go/aws"
  9. "github.com/aws/aws-sdk-go/aws/credentials"
  10. "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
  11. "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds"
  12. "github.com/aws/aws-sdk-go/aws/ec2metadata"
  13. "github.com/aws/aws-sdk-go/aws/session"
  14. "github.com/aws/aws-sdk-go/service/cloudwatch"
  15. "github.com/aws/aws-sdk-go/service/sts"
  16. )
  17. type cache struct {
  18. credential *credentials.Credentials
  19. expiration *time.Time
  20. }
  21. var awsCredentialCache map[string]cache = make(map[string]cache)
  22. var credentialCacheLock sync.RWMutex
  23. func GetCredentials(dsInfo *DatasourceInfo) (*credentials.Credentials, error) {
  24. cacheKey := dsInfo.AccessKey + ":" + dsInfo.Profile + ":" + dsInfo.AssumeRoleArn
  25. credentialCacheLock.RLock()
  26. if _, ok := awsCredentialCache[cacheKey]; ok {
  27. if awsCredentialCache[cacheKey].expiration != nil &&
  28. (*awsCredentialCache[cacheKey].expiration).After(time.Now().UTC()) {
  29. result := awsCredentialCache[cacheKey].credential
  30. credentialCacheLock.RUnlock()
  31. return result, nil
  32. }
  33. }
  34. credentialCacheLock.RUnlock()
  35. accessKeyId := ""
  36. secretAccessKey := ""
  37. sessionToken := ""
  38. var expiration *time.Time
  39. expiration = nil
  40. if dsInfo.AuthType == "arn" && strings.Index(dsInfo.AssumeRoleArn, "arn:aws:iam:") == 0 {
  41. params := &sts.AssumeRoleInput{
  42. RoleArn: aws.String(dsInfo.AssumeRoleArn),
  43. RoleSessionName: aws.String("GrafanaSession"),
  44. DurationSeconds: aws.Int64(900),
  45. }
  46. stsSess, err := session.NewSession()
  47. if err != nil {
  48. return nil, err
  49. }
  50. stsCreds := credentials.NewChainCredentials(
  51. []credentials.Provider{
  52. &credentials.EnvProvider{},
  53. &credentials.SharedCredentialsProvider{Filename: "", Profile: dsInfo.Profile},
  54. remoteCredProvider(stsSess),
  55. })
  56. stsConfig := &aws.Config{
  57. Region: aws.String(dsInfo.Region),
  58. Credentials: stsCreds,
  59. }
  60. sess, err := session.NewSession(stsConfig)
  61. if err != nil {
  62. return nil, err
  63. }
  64. svc := sts.New(sess, stsConfig)
  65. resp, err := svc.AssumeRole(params)
  66. if err != nil {
  67. return nil, err
  68. }
  69. if resp.Credentials != nil {
  70. accessKeyId = *resp.Credentials.AccessKeyId
  71. secretAccessKey = *resp.Credentials.SecretAccessKey
  72. sessionToken = *resp.Credentials.SessionToken
  73. expiration = resp.Credentials.Expiration
  74. }
  75. } else {
  76. now := time.Now()
  77. e := now.Add(5 * time.Minute)
  78. expiration = &e
  79. }
  80. sess, err := session.NewSession()
  81. if err != nil {
  82. return nil, err
  83. }
  84. creds := credentials.NewChainCredentials(
  85. []credentials.Provider{
  86. &credentials.StaticProvider{Value: credentials.Value{
  87. AccessKeyID: accessKeyId,
  88. SecretAccessKey: secretAccessKey,
  89. SessionToken: sessionToken,
  90. }},
  91. &credentials.EnvProvider{},
  92. &credentials.StaticProvider{Value: credentials.Value{
  93. AccessKeyID: dsInfo.AccessKey,
  94. SecretAccessKey: dsInfo.SecretKey,
  95. }},
  96. &credentials.SharedCredentialsProvider{Filename: "", Profile: dsInfo.Profile},
  97. remoteCredProvider(sess),
  98. })
  99. credentialCacheLock.Lock()
  100. awsCredentialCache[cacheKey] = cache{
  101. credential: creds,
  102. expiration: expiration,
  103. }
  104. credentialCacheLock.Unlock()
  105. return creds, nil
  106. }
  107. func remoteCredProvider(sess *session.Session) credentials.Provider {
  108. ecsCredURI := os.Getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")
  109. if len(ecsCredURI) > 0 {
  110. return ecsCredProvider(sess, ecsCredURI)
  111. }
  112. return ec2RoleProvider(sess)
  113. }
  114. func ecsCredProvider(sess *session.Session, uri string) credentials.Provider {
  115. const host = `169.254.170.2`
  116. c := ec2metadata.New(sess)
  117. return endpointcreds.NewProviderClient(
  118. c.Client.Config,
  119. c.Client.Handlers,
  120. fmt.Sprintf("http://%s%s", host, uri),
  121. func(p *endpointcreds.Provider) { p.ExpiryWindow = 5 * time.Minute })
  122. }
  123. func ec2RoleProvider(sess *session.Session) credentials.Provider {
  124. return &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute}
  125. }
  126. func (e *CloudWatchExecutor) getDsInfo(region string) *DatasourceInfo {
  127. defaultRegion := e.DataSource.JsonData.Get("defaultRegion").MustString()
  128. if region == "default" {
  129. region = defaultRegion
  130. }
  131. authType := e.DataSource.JsonData.Get("authType").MustString()
  132. assumeRoleArn := e.DataSource.JsonData.Get("assumeRoleArn").MustString()
  133. accessKey := ""
  134. secretKey := ""
  135. for key, value := range e.DataSource.SecureJsonData.Decrypt() {
  136. if key == "accessKey" {
  137. accessKey = value
  138. }
  139. if key == "secretKey" {
  140. secretKey = value
  141. }
  142. }
  143. datasourceInfo := &DatasourceInfo{
  144. Region: region,
  145. Profile: e.DataSource.Database,
  146. AuthType: authType,
  147. AssumeRoleArn: assumeRoleArn,
  148. AccessKey: accessKey,
  149. SecretKey: secretKey,
  150. }
  151. return datasourceInfo
  152. }
  153. func (e *CloudWatchExecutor) getAwsConfig(dsInfo *DatasourceInfo) (*aws.Config, error) {
  154. creds, err := GetCredentials(dsInfo)
  155. if err != nil {
  156. return nil, err
  157. }
  158. cfg := &aws.Config{
  159. Region: aws.String(dsInfo.Region),
  160. Credentials: creds,
  161. }
  162. return cfg, nil
  163. }
  164. func (e *CloudWatchExecutor) getClient(region string) (*cloudwatch.CloudWatch, error) {
  165. datasourceInfo := e.getDsInfo(region)
  166. cfg, err := e.getAwsConfig(datasourceInfo)
  167. if err != nil {
  168. return nil, err
  169. }
  170. sess, err := session.NewSession(cfg)
  171. if err != nil {
  172. return nil, err
  173. }
  174. client := cloudwatch.New(sess, cfg)
  175. return client, nil
  176. }