credentials.go 5.3 KB

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