credentials.go 5.2 KB

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