Skip to content

Commit d7c7d63

Browse files
idastambukiwysiu
andauthored
Sigv4: Use externalId when signing with sigv4 (#238)
--------- Co-authored-by: Isabella Siu <[email protected]>
1 parent efc51c3 commit d7c7d63

File tree

2 files changed

+85
-7
lines changed

2 files changed

+85
-7
lines changed

pkg/sigv4/sigv4.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import (
2727

2828
var (
2929
signerCache sync.Map
30+
newStsCreds = stscreds.NewCredentials
31+
newV4Signer = v4.NewSigner
3032
)
3133

3234
type middleware struct {
@@ -235,10 +237,10 @@ func createSigner(cfg *Config, authSettings awsds.AuthSettings, verboseMode bool
235237
if err != nil {
236238
return nil, err
237239
}
238-
c = stscreds.NewCredentials(s, cfg.AssumeRoleARN)
240+
return getAssumeRoleSigner(s, cfg, signerOpts)
239241
}
240242

241-
return v4.NewSigner(c, signerOpts), nil
243+
return newV4Signer(c, signerOpts), nil
242244
case awsds.AuthTypeDefault:
243245
s, err := session.NewSession(&aws.Config{
244246
Region: aws.String(cfg.Region),
@@ -248,10 +250,10 @@ func createSigner(cfg *Config, authSettings awsds.AuthSettings, verboseMode bool
248250
}
249251

250252
if cfg.AssumeRoleARN != "" {
251-
return v4.NewSigner(stscreds.NewCredentials(s, cfg.AssumeRoleARN), signerOpts), nil
253+
return getAssumeRoleSigner(s, cfg, signerOpts)
252254
}
253255

254-
return v4.NewSigner(s.Config.Credentials, signerOpts), nil
256+
return newV4Signer(s.Config.Credentials, signerOpts), nil
255257
default:
256258
if cfg.AssumeRoleARN != "" {
257259
s, err := session.NewSession(&aws.Config{
@@ -260,7 +262,7 @@ func createSigner(cfg *Config, authSettings awsds.AuthSettings, verboseMode bool
260262
if err != nil {
261263
return nil, err
262264
}
263-
return v4.NewSigner(stscreds.NewCredentials(s, cfg.AssumeRoleARN), signerOpts), nil
265+
return getAssumeRoleSigner(s, cfg, signerOpts)
264266
}
265267
return nil, fmt.Errorf("invalid SigV4 auth type %q", authType)
266268
}
@@ -273,10 +275,19 @@ func createSigner(cfg *Config, authSettings awsds.AuthSettings, verboseMode bool
273275
if err != nil {
274276
return nil, err
275277
}
276-
return v4.NewSigner(stscreds.NewCredentials(s, cfg.AssumeRoleARN), signerOpts), nil
278+
return getAssumeRoleSigner(s, cfg, signerOpts)
277279
}
278280

279-
return v4.NewSigner(c, signerOpts), nil
281+
return newV4Signer(c, signerOpts), nil
282+
}
283+
284+
func getAssumeRoleSigner(s *session.Session, cfg *Config, signerOpts func(s *v4.Signer)) (*v4.Signer, error) {
285+
if cfg.ExternalID != "" {
286+
return newV4Signer(newStsCreds(s, cfg.AssumeRoleARN, func(p *stscreds.AssumeRoleProvider) {
287+
p.ExternalID = aws.String(cfg.ExternalID)
288+
}), signerOpts), nil
289+
}
290+
return newV4Signer(newStsCreds(s, cfg.AssumeRoleARN), signerOpts), nil
280291
}
281292

282293
func copyHeaderWithoutOverwrite(dst, src http.Header) {

pkg/sigv4/sigv4_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import (
1010
"github.com/grafana/grafana-plugin-sdk-go/backend"
1111
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
1212

13+
"github.com/aws/aws-sdk-go/aws/client"
1314
"github.com/aws/aws-sdk-go/aws/credentials"
15+
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
1416
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
1517

1618
"github.com/stretchr/testify/require"
@@ -301,6 +303,71 @@ func TestConfig(t *testing.T) {
301303
})
302304
}
303305

306+
func TestCreateSigner_UsesExternalID_WhenProvided(t *testing.T) {
307+
for _, tc := range []struct {
308+
authType string
309+
}{
310+
{authType: "default"},
311+
{authType: "credentials"},
312+
{authType: "keys"},
313+
{authType: "ec2_iam_role"},
314+
{authType: "grafana_assume_role"},
315+
} {
316+
t.Run(fmt.Sprintf("AuthType: %s", tc.authType), func(t *testing.T) {
317+
// Capture the external ID passed into the AssumeRoleProvider
318+
var capturedExternalID string
319+
var signerCalled bool
320+
321+
// Mock stscreds.NewCredentials
322+
newStsCreds = func(c client.ConfigProvider, arn string, optFns ...func(*stscreds.AssumeRoleProvider)) *credentials.Credentials {
323+
provider := &stscreds.AssumeRoleProvider{}
324+
for _, opt := range optFns {
325+
opt(provider)
326+
}
327+
if provider.ExternalID != nil {
328+
capturedExternalID = *provider.ExternalID
329+
}
330+
return credentials.NewStaticCredentials("mock-access", "mock-secret", "mock-token")
331+
}
332+
333+
// Mock v4.NewSigner
334+
newV4Signer = func(creds *credentials.Credentials, opts ...func(s *v4.Signer)) *v4.Signer {
335+
signerCalled = true
336+
return &v4.Signer{}
337+
}
338+
339+
// Restore mocks
340+
defer func() {
341+
newStsCreds = stscreds.NewCredentials
342+
newV4Signer = v4.NewSigner
343+
}()
344+
345+
cfg := &Config{
346+
Region: "us-east-2",
347+
AuthType: tc.authType,
348+
AssumeRoleARN: "arn:aws:iam::123456789:role/test-role",
349+
ExternalID: "external-id-123",
350+
}
351+
352+
signer, err := createSigner(cfg, awsds.AuthSettings{
353+
AllowedAuthProviders: []string{
354+
"default",
355+
"credentials",
356+
"keys",
357+
"ec2_iam_role",
358+
"grafana_assume_role",
359+
},
360+
AssumeRoleEnabled: true,
361+
}, false)
362+
363+
require.NoError(t, err)
364+
require.NotNil(t, signer)
365+
require.True(t, signerCalled)
366+
require.Equal(t, "external-id-123", capturedExternalID)
367+
})
368+
}
369+
}
370+
304371
type mockCredentialsProvider struct {
305372
credentials.Provider
306373
noCredentials bool

0 commit comments

Comments
 (0)