From 04b65c6dfb45817244d81d9c3c285e7d5b6f3389 Mon Sep 17 00:00:00 2001 From: "Michael S. Fischer" Date: Mon, 16 Dec 2019 15:01:33 -0800 Subject: [PATCH] Add AWS Access Key ID to log Add the AWS Access Key ID to the STS response log. This should make it easier to audit the true identity of the user or process that authenticated to the cluster. Fixes #263. --- pkg/server/server.go | 3 ++- pkg/token/token.go | 15 ++++++++++++--- pkg/token/token_test.go | 8 ++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/pkg/server/server.go b/pkg/server/server.go index 1c125a50b..289d0c5ba 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -1,5 +1,5 @@ /* -Copyright 2017 by the contributors. +Copyright 2017-2019 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,6 +294,7 @@ func (h *handler) authenticateEndpoint(w http.ResponseWriter, req *http.Request) } log.WithFields(logrus.Fields{ + "awsaccesskeyid": identity.AWSAccessKeyID, "arn": identity.ARN, "accountid": identity.AccountID, "userid": identity.UserID, diff --git a/pkg/token/token.go b/pkg/token/token.go index ec923a071..a6a8d4046 100644 --- a/pkg/token/token.go +++ b/pkg/token/token.go @@ -1,5 +1,5 @@ /* -Copyright 2017 by the contributors. +Copyright 2017-2019 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. @@ -65,6 +65,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. + AWSAccessKeyID string } const ( @@ -429,6 +434,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 + awsAccessKeyID := 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())} @@ -471,8 +479,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, + AWSAccessKeyID: awsAccessKeyID, } 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..080950068 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.AWSAccessKeyID != accessKeyID { + t.Errorf("expected AWSAccessKeyID to be %q but was %q", accessKeyID, identity.AWSAccessKeyID) + } 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)