CloudWatch: Restrict auth provider and assume role usage according to Grafana configuration (#31805)

* restrict usage of providers and assume role according to grafana config

* update docs

* Update docs/sources/datasources/cloudwatch.md

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/tsdb/cloudwatch/cloudwatch.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/tsdb/cloudwatch/session_test.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* pr feedback

* fix failing test

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Erik Sundell
2021-03-09 21:20:59 +01:00
committed by GitHub
co-authored by Arve Knudsen
parent 13dd9dff50
commit 2d660ee502
11 changed files with 105 additions and 25 deletions
+21 -3
View File
@@ -64,6 +64,7 @@ func init() {
type CloudWatchService struct {
LogsService *LogsService `inject:""`
Cfg *setting.Cfg `inject:""`
}
func (s *CloudWatchService) Init() error {
@@ -71,12 +72,13 @@ func (s *CloudWatchService) Init() error {
}
func (s *CloudWatchService) NewExecutor(*models.DataSource) (plugins.DataPlugin, error) {
return newExecutor(s.LogsService), nil
return newExecutor(s.LogsService, s.Cfg), nil
}
func newExecutor(logsService *LogsService) *cloudWatchExecutor {
func newExecutor(logsService *LogsService, cfg *setting.Cfg) *cloudWatchExecutor {
return &cloudWatchExecutor{
logsService: logsService,
cfg: cfg,
}
}
@@ -88,11 +90,27 @@ type cloudWatchExecutor struct {
rgtaClient resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI
logsService *LogsService
cfg *setting.Cfg
}
func (e *cloudWatchExecutor) newSession(region string) (*session.Session, error) {
dsInfo := e.getDSInfo(region)
authTypeAllowed := false
for _, provider := range e.cfg.AWSAllowedAuthProviders {
if provider == dsInfo.AuthType.String() {
authTypeAllowed = true
break
}
}
if !authTypeAllowed {
return nil, fmt.Errorf("attempting to use an auth type that is not allowed: %q", dsInfo.AuthType.String())
}
if dsInfo.AssumeRoleARN != "" && !e.cfg.AWSAssumeRoleEnabled {
return nil, fmt.Errorf("attempting to use assume role (ARN) which is disabled in grafana.ini")
}
bldr := strings.Builder{}
for i, s := range []string{
dsInfo.AuthType.String(), dsInfo.AccessKey, dsInfo.Profile, dsInfo.AssumeRoleARN, region, dsInfo.Endpoint,
@@ -164,7 +182,7 @@ func (e *cloudWatchExecutor) newSession(region string) (*session.Session, error)
duration := stscreds.DefaultDuration
expiration := time.Now().UTC().Add(duration)
if dsInfo.AssumeRoleARN != "" {
if dsInfo.AssumeRoleARN != "" && e.cfg.AWSAssumeRoleEnabled {
// We should assume a role in AWS
plog.Debug("Trying to assume role in AWS", "arn", dsInfo.AssumeRoleARN)
+7 -8
View File
@@ -47,7 +47,7 @@ func TestQuery_DescribeLogGroups(t *testing.T) {
},
}
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
resp, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
Queries: []plugins.DataSubQuery{
{
@@ -100,7 +100,7 @@ func TestQuery_DescribeLogGroups(t *testing.T) {
},
}
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
resp, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
Queries: []plugins.DataSubQuery{
{
@@ -169,8 +169,7 @@ func TestQuery_GetLogGroupFields(t *testing.T) {
}
const refID = "A"
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
resp, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
Queries: []plugins.DataSubQuery{
{
@@ -249,7 +248,7 @@ func TestQuery_StartQuery(t *testing.T) {
To: "1584700643000",
}
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
_, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
TimeRange: &timeRange,
Queries: []plugins.DataSubQuery{
@@ -295,7 +294,7 @@ func TestQuery_StartQuery(t *testing.T) {
To: "1584873443000",
}
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
resp, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
TimeRange: &timeRange,
Queries: []plugins.DataSubQuery{
@@ -371,7 +370,7 @@ func TestQuery_StopQuery(t *testing.T) {
To: "1584700643000",
}
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
resp, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
TimeRange: &timeRange,
Queries: []plugins.DataSubQuery{
@@ -458,7 +457,7 @@ func TestQuery_GetQueryResults(t *testing.T) {
},
}
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
resp, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
Queries: []plugins.DataSubQuery{
{
@@ -44,7 +44,7 @@ func TestQuery_Metrics(t *testing.T) {
},
},
}
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
resp, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
Queries: []plugins.DataSubQuery{
{
@@ -101,7 +101,7 @@ func TestQuery_Metrics(t *testing.T) {
},
},
}
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
resp, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
Queries: []plugins.DataSubQuery{
{
@@ -163,7 +163,7 @@ func TestQuery_Regions(t *testing.T) {
cli = fakeEC2Client{
regions: []string{regionName},
}
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
resp, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
Queries: []plugins.DataSubQuery{
{
@@ -245,7 +245,7 @@ func TestQuery_InstanceAttributes(t *testing.T) {
},
},
}
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
resp, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
Queries: []plugins.DataSubQuery{
{
@@ -348,7 +348,7 @@ func TestQuery_EBSVolumeIDs(t *testing.T) {
},
},
}
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
resp, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
Queries: []plugins.DataSubQuery{
{
@@ -448,7 +448,7 @@ func TestQuery_ResourceARNs(t *testing.T) {
},
},
}
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
resp, err := executor.DataQuery(context.Background(), fakeDataSource(), plugins.DataQuery{
Queries: []plugins.DataSubQuery{
{
@@ -6,12 +6,13 @@ import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestQueryTransformer(t *testing.T) {
executor := newExecutor(nil)
executor := newExecutor(nil, &setting.Cfg{})
t.Run("One cloudwatchQuery is generated when its request query has one stat", func(t *testing.T) {
requestQueries := []*requestQuery{
{
+48 -3
View File
@@ -13,6 +13,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -58,7 +59,7 @@ func TestNewSession_AssumeRole(t *testing.T) {
const roleARN = "test"
e := newExecutor(nil)
e := newExecutor(nil, newTestConfig())
e.DataSource = fakeDataSource(fakeDataSourceCfg{
assumeRoleARN: roleARN,
})
@@ -85,7 +86,7 @@ func TestNewSession_AssumeRole(t *testing.T) {
const roleARN = "test"
const externalID = "external"
e := newExecutor(nil)
e := newExecutor(nil, newTestConfig())
e.DataSource = fakeDataSource(fakeDataSourceCfg{
assumeRoleARN: roleARN,
externalID: externalID,
@@ -105,6 +106,50 @@ func TestNewSession_AssumeRole(t *testing.T) {
}), cmpopts.IgnoreFields(stscreds.AssumeRoleProvider{}, "Expiry"))
assert.Empty(t, diff)
})
t.Run("Assume role not enabled", func(t *testing.T) {
t.Cleanup(func() {
sessCache = map[string]envelope{}
})
const roleARN = "test"
e := newExecutor(nil, &setting.Cfg{AWSAllowedAuthProviders: []string{"default"}, AWSAssumeRoleEnabled: false})
e.DataSource = fakeDataSource(fakeDataSourceCfg{
assumeRoleARN: roleARN,
})
sess, err := e.newSession(defaultRegion)
require.Error(t, err)
require.Nil(t, sess)
expectedError := "attempting to use assume role (ARN) which is disabled in grafana.ini"
assert.Equal(t, expectedError, err.Error())
})
}
func TestNewSession_AllowedAuthProviders(t *testing.T) {
t.Run("Not allowed auth type is used", func(t *testing.T) {
e := newExecutor(nil, &setting.Cfg{AWSAllowedAuthProviders: []string{"keys"}})
e.DataSource = fakeDataSource()
e.DataSource.JsonData.Set("authType", "default")
sess, err := e.newSession(defaultRegion)
require.Error(t, err)
require.Nil(t, sess)
assert.Equal(t, `attempting to use an auth type that is not allowed: "default"`, err.Error())
})
t.Run("Allowed auth type is used", func(t *testing.T) {
e := newExecutor(nil, &setting.Cfg{AWSAllowedAuthProviders: []string{"keys"}})
e.DataSource = fakeDataSource()
e.DataSource.JsonData.Set("authType", "keys")
sess, err := e.newSession(defaultRegion)
require.NoError(t, err)
require.NotNil(t, sess)
})
}
func TestNewSession_EC2IAMRole(t *testing.T) {
@@ -123,7 +168,7 @@ func TestNewSession_EC2IAMRole(t *testing.T) {
}
t.Run("Credentials are created", func(t *testing.T) {
e := newExecutor(nil)
e := newExecutor(nil, &setting.Cfg{AWSAllowedAuthProviders: []string{"ec2_iam_role"}, AWSAssumeRoleEnabled: true})
e.DataSource = fakeDataSource()
e.DataSource.JsonData.Set("authType", "ec2_iam_role")
+5
View File
@@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana/pkg/components/securejsondata"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
type fakeDataSourceCfg struct {
@@ -145,3 +146,7 @@ func (c fakeRGTAClient) GetResourcesPages(in *resourcegroupstaggingapi.GetResour
}, true)
return nil
}
func newTestConfig() *setting.Cfg {
return &setting.Cfg{AWSAllowedAuthProviders: []string{"default"}, AWSAssumeRoleEnabled: true}
}
@@ -9,7 +9,7 @@ import (
)
func TestTimeSeriesQuery(t *testing.T) {
executor := newExecutor(nil)
executor := newExecutor(nil, newTestConfig())
t.Run("End time before start time should result in error", func(t *testing.T) {
timeRange := plugins.NewDataTimeRange("now-1h", "now-2h")