CloudWatch: Re-implement authentication (#25548)
* CloudWatch: Revisit authentication Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Simplify auth code Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Use ARN Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Add Drone configuration Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove unused code Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove .drone.yml Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix external ID usage Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Fix issues after merge Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove stale code Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove stale code Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Use auth type enum Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test snapshot * Coordinate frontend and backend option names Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove old comments Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix front-end tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Introduce session cache Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Use constants Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix field alignment * CloudWatch: Fix log message Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Tidy go.mod Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Handle arn auth type Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Fix role assumption duration Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Inline unnecessary constants Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Use serial comma in UI Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Inline unnecessary constants Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Fail if missing region Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Handle unconfigured region Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Log when using cached session Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Include region in cache key Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Add UI warnings for lecagy support * Do not clear ARN fields whenging change authentication provider * Graph NG: annotations display (#27972) * Annotations support POC * Fix markers memoization * dev dashboard update * Update public/app/plugins/panel/graph3/plugins/AnnotationsPlugin.tsx * CloudWatch: Remove errors.BadRequest Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Undo unintentional change Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove log line Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix cache key computation Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Add region to cache key Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Improve log messages Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CloudWatch: Add documentation Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Improve tooltip Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Improve docs Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Improve docs Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Improve docs Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Improve tooltip Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Add role assumption provisioning example Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Add upgrade notes Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Improve docs Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Apply suggestions from code review Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> * backend: use latest sdk (#28147) fixes #27713 via https://github.com/grafana/grafana-plugin-sdk-go/pull/227 * Docs: Update Permissions documentation (#28144) * removed overview.md * content updates * Update datasource_permissions.md * update content * content updates * Update organization_roles.md * Update docs/sources/enterprise/saml.md Co-authored-by: Kyle Brandt <kyle@grafana.com> * Update dashboard_folder_permissions.md Co-authored-by: Kyle Brandt <kyle@grafana.com> * area/grafana/toolkit: ci-package needs to use synchronous writes (#28148) * ci needs to use synchronous writes or the file ends up with zero length * <Enterprise Docs> Add instructions to upload license via UI (#28067) * Add UI license upload option, reformat Enterprise license activation section Added the option to upload a license file through the Server Admin UI, and did a little reformatting to make license activation look more like a process. * Headers not bold, hyphens not asterisks * Github: run metrics collector workflow every 10min (#28153) * GithubActions: Updated cron schedule * Updated * Docs: Update explore docs: remove dot at the end of line (#28151) HI - Removed Dot(.) at the end of line to make it consistent with other 2 points. Thanks, Ashish * Fix frontend tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix frontend tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Docs: Update upgrade notes Co-authored-by: Sofia Papagiannaki <sofia@grafana.com> Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> Co-authored-by: Kyle Brandt <kyle@grafana.com> Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> Co-authored-by: Brian Gann <briangann@users.noreply.github.com> Co-authored-by: Mitch Seaman <mjseaman@users.noreply.github.com> Co-authored-by: Torkel Ödegaard <torkel@grafana.org> Co-authored-by: Torkel Ödegaard <torkel@grafana.com> Co-authored-by: ashishagarwal06 <34888589+ashishagarwal06@users.noreply.github.com>
This commit is contained in:
co-authored by
Sofia Papagiannaki
Dominik Prokop
Marcus Efraimsson
Kyle Brandt
Diana Payton
Brian Gann
Mitch Seaman
Torkel Ödegaard
Torkel Ödegaard
ashishagarwal06
parent
519ec93c7d
commit
957c88eaca
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -11,6 +12,8 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
@@ -31,8 +34,8 @@ import (
|
||||
type datasourceInfo struct {
|
||||
Profile string
|
||||
Region string
|
||||
AuthType string
|
||||
AssumeRoleArn string
|
||||
AuthType authType
|
||||
AssumeRoleARN string
|
||||
ExternalID string
|
||||
Namespace string
|
||||
|
||||
@@ -74,16 +77,105 @@ type cloudWatchExecutor struct {
|
||||
|
||||
func (e *cloudWatchExecutor) newSession(region string) (*session.Session, error) {
|
||||
dsInfo := e.getDSInfo(region)
|
||||
creds, err := getCredentials(dsInfo)
|
||||
|
||||
bldr := strings.Builder{}
|
||||
for i, s := range []string{
|
||||
dsInfo.AuthType.String(), dsInfo.AccessKey, dsInfo.Profile, dsInfo.AssumeRoleARN, region,
|
||||
} {
|
||||
if i != 0 {
|
||||
bldr.WriteString(":")
|
||||
}
|
||||
bldr.WriteString(strings.ReplaceAll(s, ":", `\:`))
|
||||
}
|
||||
cacheKey := bldr.String()
|
||||
|
||||
sessCacheLock.RLock()
|
||||
if env, ok := sessCache[cacheKey]; ok {
|
||||
if env.expiration.After(time.Now().UTC()) {
|
||||
sessCacheLock.RUnlock()
|
||||
return env.session, nil
|
||||
}
|
||||
}
|
||||
sessCacheLock.RUnlock()
|
||||
|
||||
cfgs := []*aws.Config{
|
||||
{
|
||||
CredentialsChainVerboseErrors: aws.Bool(true),
|
||||
},
|
||||
}
|
||||
|
||||
var regionCfg *aws.Config
|
||||
if dsInfo.Region == defaultRegion {
|
||||
plog.Warn("Region is set to \"default\", which is unsupported")
|
||||
dsInfo.Region = ""
|
||||
}
|
||||
if dsInfo.Region != "" {
|
||||
regionCfg = &aws.Config{Region: aws.String(dsInfo.Region)}
|
||||
cfgs = append(cfgs, regionCfg)
|
||||
}
|
||||
|
||||
switch dsInfo.AuthType {
|
||||
case authTypeSharedCreds:
|
||||
plog.Debug("Authenticating towards AWS with shared credentials", "profile", dsInfo.Profile,
|
||||
"region", dsInfo.Region)
|
||||
cfgs = append(cfgs, &aws.Config{
|
||||
Credentials: credentials.NewSharedCredentials("", dsInfo.Profile),
|
||||
})
|
||||
case authTypeKeys:
|
||||
plog.Debug("Authenticating towards AWS with an access key pair", "region", dsInfo.Region)
|
||||
cfgs = append(cfgs, &aws.Config{
|
||||
Credentials: credentials.NewStaticCredentials(dsInfo.AccessKey, dsInfo.SecretKey, ""),
|
||||
})
|
||||
case authTypeDefault:
|
||||
plog.Debug("Authenticating towards AWS with default SDK method", "region", dsInfo.Region)
|
||||
default:
|
||||
panic(fmt.Sprintf("Unrecognized authType: %d", dsInfo.AuthType))
|
||||
}
|
||||
sess, err := newSession(cfgs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := &aws.Config{
|
||||
Region: aws.String(dsInfo.Region),
|
||||
Credentials: creds,
|
||||
duration := stscreds.DefaultDuration
|
||||
expiration := time.Now().Add(duration)
|
||||
if dsInfo.AssumeRoleARN != "" {
|
||||
// We should assume a role in AWS
|
||||
plog.Debug("Trying to assume role in AWS", "arn", dsInfo.AssumeRoleARN)
|
||||
|
||||
cfgs := []*aws.Config{
|
||||
{
|
||||
CredentialsChainVerboseErrors: aws.Bool(true),
|
||||
},
|
||||
{
|
||||
Credentials: newSTSCredentials(sess, dsInfo.AssumeRoleARN, func(p *stscreds.AssumeRoleProvider) {
|
||||
// Not sure if this is necessary, overlaps with p.Duration and is undocumented
|
||||
p.Expiry.SetExpiration(expiration, 0)
|
||||
p.Duration = duration
|
||||
if dsInfo.ExternalID != "" {
|
||||
p.ExternalID = aws.String(dsInfo.ExternalID)
|
||||
}
|
||||
}),
|
||||
},
|
||||
}
|
||||
if regionCfg != nil {
|
||||
cfgs = append(cfgs, regionCfg)
|
||||
}
|
||||
sess, err = newSession(cfgs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return newSession(cfg)
|
||||
|
||||
plog.Debug("Successfully created AWS session")
|
||||
|
||||
sessCacheLock.Lock()
|
||||
sessCache[cacheKey] = envelope{
|
||||
session: sess,
|
||||
expiration: expiration,
|
||||
}
|
||||
sessCacheLock.Unlock()
|
||||
|
||||
return sess, nil
|
||||
}
|
||||
|
||||
func (e *cloudWatchExecutor) getCWClient(region string) (cloudwatchiface.CloudWatchAPI, error) {
|
||||
@@ -282,18 +374,54 @@ func (e *cloudWatchExecutor) executeLogAlertQuery(ctx context.Context, queryCont
|
||||
return response, nil
|
||||
}
|
||||
|
||||
type authType int
|
||||
|
||||
const (
|
||||
authTypeDefault authType = iota
|
||||
authTypeSharedCreds
|
||||
authTypeKeys
|
||||
)
|
||||
|
||||
func (at authType) String() string {
|
||||
switch at {
|
||||
case authTypeDefault:
|
||||
return "default"
|
||||
case authTypeSharedCreds:
|
||||
return "sharedCreds"
|
||||
case authTypeKeys:
|
||||
return "keys"
|
||||
default:
|
||||
panic(fmt.Sprintf("Unrecognized auth type %d", at))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *cloudWatchExecutor) getDSInfo(region string) *datasourceInfo {
|
||||
if region == defaultRegion {
|
||||
region = e.DataSource.JsonData.Get("defaultRegion").MustString()
|
||||
}
|
||||
|
||||
authType := e.DataSource.JsonData.Get("authType").MustString()
|
||||
assumeRoleArn := e.DataSource.JsonData.Get("assumeRoleArn").MustString()
|
||||
atStr := e.DataSource.JsonData.Get("authType").MustString()
|
||||
assumeRoleARN := e.DataSource.JsonData.Get("assumeRoleArn").MustString()
|
||||
externalID := e.DataSource.JsonData.Get("externalId").MustString()
|
||||
decrypted := e.DataSource.DecryptedValues()
|
||||
accessKey := decrypted["accessKey"]
|
||||
secretKey := decrypted["secretKey"]
|
||||
|
||||
at := authTypeDefault
|
||||
switch atStr {
|
||||
case "credentials":
|
||||
at = authTypeSharedCreds
|
||||
case "keys":
|
||||
at = authTypeKeys
|
||||
case "default":
|
||||
at = authTypeDefault
|
||||
case "arn":
|
||||
at = authTypeDefault
|
||||
plog.Warn("Authentication type \"arn\" is deprecated, falling back to default")
|
||||
default:
|
||||
plog.Warn("Unrecognized AWS authentication type", "type", atStr)
|
||||
}
|
||||
|
||||
profile := e.DataSource.JsonData.Get("profile").MustString()
|
||||
if profile == "" {
|
||||
profile = e.DataSource.Database // legacy support
|
||||
@@ -302,8 +430,8 @@ func (e *cloudWatchExecutor) getDSInfo(region string) *datasourceInfo {
|
||||
return &datasourceInfo{
|
||||
Region: region,
|
||||
Profile: profile,
|
||||
AuthType: authType,
|
||||
AssumeRoleArn: assumeRoleArn,
|
||||
AuthType: at,
|
||||
AssumeRoleARN: assumeRoleARN,
|
||||
ExternalID: externalID,
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
|
||||
Reference in New Issue
Block a user