diff --git a/pkg/server/server.go b/pkg/server/server.go index 1c125a50b..2db531c6a 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -1,5 +1,5 @@ /* -Copyright 2017 by the contributors. +Copyright 2017-2020 by the contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -294,10 +294,11 @@ func (h *handler) authenticateEndpoint(w http.ResponseWriter, req *http.Request) } log.WithFields(logrus.Fields{ - "arn": identity.ARN, - "accountid": identity.AccountID, - "userid": identity.UserID, - "session": identity.SessionName, + "accesskeyid": identity.AccessKeyID, + "arn": identity.ARN, + "accountid": identity.AccountID, + "userid": identity.UserID, + "session": identity.SessionName, }).Info("STS response") // look up the ARN in each of our mappings to fill in the username and groups diff --git a/pkg/token/token.go b/pkg/token/token.go index dba5e8bda..00970a991 100644 --- a/pkg/token/token.go +++ b/pkg/token/token.go @@ -1,5 +1,5 @@ /* -Copyright 2017 by the contributors. +Copyright 2017-2020 by the contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -66,6 +66,11 @@ type Identity struct { // users or other roles are allowed to assume the role, they can provide // (nearly) arbitrary strings here. SessionName string + + // The AWS Access Key ID used to authenticate the request. This can be used + // in conjuction with CloudTrail to determine the identity of the individual + // if the individual assumed an IAM role before making the request. + AccessKeyID string } const ( @@ -431,6 +436,9 @@ func (v tokenVerifier) Verify(token string) (*Identity, error) { return nil, FormatError{"X-Amz-Date parameter must be present in pre-signed URL"} } + // Obtain AWS Access Key ID from supplied credentials + accessKeyID := strings.Split(queryParamsLower.Get("x-amz-credential"), "/")[0] + dateParam, err := time.Parse(dateHeaderFormat, date) if err != nil { return nil, FormatError{fmt.Sprintf("error parsing X-Amz-Date parameter %s into format %s: %s", date, dateHeaderFormat, err.Error())} @@ -473,8 +481,9 @@ func (v tokenVerifier) Verify(token string) (*Identity, error) { // parse the response into an Identity id := &Identity{ - ARN: callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn, - AccountID: callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Account, + ARN: callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn, + AccountID: callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Account, + AccessKeyID: accessKeyID, } id.CanonicalARN, err = arn.Canonicalize(id.ARN) if err != nil { diff --git a/pkg/token/token_test.go b/pkg/token/token_test.go index 1a6f30618..de6359ca2 100644 --- a/pkg/token/token_test.go +++ b/pkg/token/token_test.go @@ -48,8 +48,8 @@ func assertSTSError(t *testing.T, err error) { var ( now = time.Now() timeStr = now.UTC().Format("20060102T150405Z") + validURL = fmt.Sprintf("https://sts.amazonaws.com/?action=GetCallerIdentity&X-Amz-Credential=ASIABCDEFGHIJKLMNOPQ%%2F20191216%%2Fus-west-2%%2Fs3%%2Faws4_request&x-amz-signedheaders=x-k8s-aws-id&x-amz-expires=60&x-amz-date=%s", timeStr) validToken = toToken(validURL) - validURL = fmt.Sprintf("https://sts.amazonaws.com/?action=GetCallerIdentity&x-amz-signedheaders=x-k8s-aws-id&x-amz-expires=60&x-amz-date=%s", timeStr) ) func toToken(url string) string { @@ -209,15 +209,19 @@ func TestVerifyNoSession(t *testing.T) { arn := "arn:aws:iam::123456789012:user/Alice" account := "123456789012" userID := "Alice" + accessKeyID := "ASIABCDEFGHIJKLMNOPQ" identity, err := newVerifier(200, jsonResponse(arn, account, userID), nil).Verify(validToken) if err != nil { t.Errorf("expected error to be nil was %q", err) } + if identity.AccessKeyID != accessKeyID { + t.Errorf("expected AccessKeyID to be %q but was %q", accessKeyID, identity.AccessKeyID) + } if identity.ARN != arn { t.Errorf("expected ARN to be %q but was %q", arn, identity.ARN) } if identity.CanonicalARN != arn { - t.Errorf("expected CannonicalARN to be %q but was %q", arn, identity.CanonicalARN) + t.Errorf("expected CanonicalARN to be %q but was %q", arn, identity.CanonicalARN) } if identity.UserID != userID { t.Errorf("expected Username to be %q but was %q", userID, identity.UserID)