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:
co-authored by
Arve Knudsen
parent
13dd9dff50
commit
2d660ee502
@@ -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)
|
||||
|
||||
|
||||
@@ -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{
|
||||
{
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user