From d4fb2624b46e02eab6a348a319d2c3abbd895b88 Mon Sep 17 00:00:00 2001 From: Brian Rodgers Date: Mon, 5 Jun 2017 17:04:31 -0500 Subject: [PATCH] Log auth info on permission denied due to ACL (#2754) --- audit/format.go | 20 ++++++++++++++++---- audit/format_json_test.go | 33 ++++++++++++++++++++------------- audit/format_jsonx_test.go | 33 ++++++++++++++++++++------------- vault/core.go | 21 ++++++++++++--------- vault/request_handling.go | 2 +- 5 files changed, 69 insertions(+), 40 deletions(-) diff --git a/audit/format.go b/audit/format.go index 773d0ad3f7ae..0f42f9ec9313 100644 --- a/audit/format.go +++ b/audit/format.go @@ -78,9 +78,17 @@ func (f *AuditFormatter) FormatRequest( // Hash any sensitive information if auth != nil { + // Cache and restore accessor in the auth + var authAccessor string + if !config.HMACAccessor && auth.Accessor != "" { + authAccessor = auth.Accessor + } if err := Hash(salt, auth); err != nil { return err } + if authAccessor != "" { + auth.Accessor = authAccessor + } } // Cache and restore accessor in the request @@ -110,6 +118,8 @@ func (f *AuditFormatter) FormatRequest( Error: errString, Auth: AuditAuth{ + ClientToken: auth.ClientToken, + Accessor: auth.Accessor, DisplayName: auth.DisplayName, Policies: auth.Policies, Metadata: auth.Metadata, @@ -297,11 +307,13 @@ func (f *AuditFormatter) FormatResponse( respEntry := &AuditResponseEntry{ Type: "response", Error: errString, - Auth: AuditAuth{ - DisplayName: auth.DisplayName, - Policies: auth.Policies, - Metadata: auth.Metadata, + ClientToken: auth.ClientToken, + Accessor: auth.Accessor, + DisplayName: auth.DisplayName, + Policies: auth.Policies, + Metadata: auth.Metadata, + RemainingUses: req.ClientTokenRemainingUses, }, Request: AuditRequest{ diff --git a/audit/format_json_test.go b/audit/format_json_test.go index 4155dbb18a15..688ae3d90bde 100644 --- a/audit/format_json_test.go +++ b/audit/format_json_test.go @@ -9,6 +9,7 @@ import ( "errors" + "fmt" "github.com/hashicorp/vault/helper/jsonutil" "github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/logical" @@ -22,15 +23,18 @@ func TestFormatJSON_formatRequest(t *testing.T) { saltFunc := func() (*salt.Salt, error) { return salter, nil } + + expectedResultStr := fmt.Sprintf(testFormatJSONReqBasicStrFmt, salter.GetIdentifiedHMAC("foo")) + cases := map[string]struct { - Auth *logical.Auth - Req *logical.Request - Err error - Prefix string - Result string + Auth *logical.Auth + Req *logical.Request + Err error + Prefix string + ExpectedStr string }{ "auth, request": { - &logical.Auth{ClientToken: "foo", Policies: []string{"root"}}, + &logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}}, &logical.Request{ Operation: logical.UpdateOperation, Path: "/foo", @@ -46,10 +50,10 @@ func TestFormatJSON_formatRequest(t *testing.T) { }, errors.New("this is an error"), "", - testFormatJSONReqBasicStr, + expectedResultStr, }, "auth, request with prefix": { - &logical.Auth{ClientToken: "foo", Policies: []string{"root"}}, + &logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}}, &logical.Request{ Operation: logical.UpdateOperation, Path: "/foo", @@ -65,7 +69,7 @@ func TestFormatJSON_formatRequest(t *testing.T) { }, errors.New("this is an error"), "@cee: ", - testFormatJSONReqBasicStr, + expectedResultStr, }, } @@ -77,17 +81,20 @@ func TestFormatJSON_formatRequest(t *testing.T) { SaltFunc: saltFunc, }, } - config := FormatterConfig{} + config := FormatterConfig{ + HMACAccessor: false, + } if err := formatter.FormatRequest(&buf, config, tc.Auth, tc.Req, tc.Err); err != nil { t.Fatalf("bad: %s\nerr: %s", name, err) } if !strings.HasPrefix(buf.String(), tc.Prefix) { - t.Fatalf("no prefix: %s \n log: %s\nprefix: %s", name, tc.Result, tc.Prefix) + t.Fatalf("no prefix: %s \n log: %s\nprefix: %s", name, expectedResultStr, tc.Prefix) } var expectedjson = new(AuditRequestEntry) - if err := jsonutil.DecodeJSON([]byte(tc.Result), &expectedjson); err != nil { + + if err := jsonutil.DecodeJSON([]byte(expectedResultStr), &expectedjson); err != nil { t.Fatalf("bad json: %s", err) } @@ -111,5 +118,5 @@ func TestFormatJSON_formatRequest(t *testing.T) { } } -const testFormatJSONReqBasicStr = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"display_name":"","policies":["root"],"metadata":null},"request":{"operation":"update","path":"/foo","data":null,"wrap_ttl":60,"remote_address":"127.0.0.1","headers":{"foo":["bar"]}},"error":"this is an error"} +const testFormatJSONReqBasicStrFmt = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"client_token":"%s","accessor":"bar","display_name":"testtoken","policies":["root"],"metadata":null},"request":{"operation":"update","path":"/foo","data":null,"wrap_ttl":60,"remote_address":"127.0.0.1","headers":{"foo":["bar"]}},"error":"this is an error"} ` diff --git a/audit/format_jsonx_test.go b/audit/format_jsonx_test.go index a0cc3a191dcb..b04ccd0be125 100644 --- a/audit/format_jsonx_test.go +++ b/audit/format_jsonx_test.go @@ -8,6 +8,7 @@ import ( "errors" + "fmt" "github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/logical" ) @@ -20,16 +21,19 @@ func TestFormatJSONx_formatRequest(t *testing.T) { saltFunc := func() (*salt.Salt, error) { return salter, nil } + + fooSalted := salter.GetIdentifiedHMAC("foo") + cases := map[string]struct { - Auth *logical.Auth - Req *logical.Request - Err error - Prefix string - Result string - Expected string + Auth *logical.Auth + Req *logical.Request + Err error + Prefix string + Result string + ExpectedStr string }{ "auth, request": { - &logical.Auth{ClientToken: "foo", Policies: []string{"root"}}, + &logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}}, &logical.Request{ Operation: logical.UpdateOperation, Path: "/foo", @@ -46,10 +50,11 @@ func TestFormatJSONx_formatRequest(t *testing.T) { errors.New("this is an error"), "", "", - `rootthis is an errorbarupdate/foo127.0.0.160request`, + fmt.Sprintf(`bar%stesttokenrootthis is an errorbarupdate/foo127.0.0.160request`, + fooSalted), }, "auth, request with prefix": { - &logical.Auth{ClientToken: "foo", Policies: []string{"root"}}, + &logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}}, &logical.Request{ Operation: logical.UpdateOperation, Path: "/foo", @@ -66,7 +71,8 @@ func TestFormatJSONx_formatRequest(t *testing.T) { errors.New("this is an error"), "", "@cee: ", - `rootthis is an errorbarupdate/foo127.0.0.160request`, + fmt.Sprintf(`bar%stesttokenrootthis is an errorbarupdate/foo127.0.0.160request`, + fooSalted), }, } @@ -79,7 +85,8 @@ func TestFormatJSONx_formatRequest(t *testing.T) { }, } config := FormatterConfig{ - OmitTime: true, + OmitTime: true, + HMACAccessor: false, } if err := formatter.FormatRequest(&buf, config, tc.Auth, tc.Req, tc.Err); err != nil { t.Fatalf("bad: %s\nerr: %s", name, err) @@ -89,10 +96,10 @@ func TestFormatJSONx_formatRequest(t *testing.T) { t.Fatalf("no prefix: %s \n log: %s\nprefix: %s", name, tc.Result, tc.Prefix) } - if !strings.HasSuffix(strings.TrimSpace(buf.String()), string(tc.Expected)) { + if !strings.HasSuffix(strings.TrimSpace(buf.String()), string(tc.ExpectedStr)) { t.Fatalf( "bad: %s\nResult:\n\n'%s'\n\nExpected:\n\n'%s'", - name, strings.TrimSpace(buf.String()), string(tc.Expected)) + name, strings.TrimSpace(buf.String()), string(tc.ExpectedStr)) } } } diff --git a/vault/core.go b/vault/core.go index 698afcacccf6..01ebdd0d4632 100644 --- a/vault/core.go +++ b/vault/core.go @@ -663,24 +663,27 @@ func (c *Core) checkToken(req *logical.Request) (*logical.Auth, *TokenEntry, err panic("unreachable code") } } + // Create the auth response + auth := &logical.Auth{ + ClientToken: req.ClientToken, + Accessor: req.ClientTokenAccessor, + Policies: te.Policies, + Metadata: te.Meta, + DisplayName: te.DisplayName, + } // Check the standard non-root ACLs. Return the token entry if it's not // allowed so we can decrement the use count. allowed, rootPrivs := acl.AllowOperation(req) if !allowed { - return nil, te, logical.ErrPermissionDenied + // Return auth for audit logging even if not allowed + return auth, te, logical.ErrPermissionDenied } if rootPath && !rootPrivs { - return nil, te, logical.ErrPermissionDenied + // Return auth for audit logging even if not allowed + return auth, te, logical.ErrPermissionDenied } - // Create the auth response - auth := &logical.Auth{ - ClientToken: req.ClientToken, - Policies: te.Policies, - Metadata: te.Meta, - DisplayName: te.DisplayName, - } return auth, te, nil } diff --git a/vault/request_handling.go b/vault/request_handling.go index b136df9373d5..8f198e9c9acd 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -171,7 +171,7 @@ func (c *Core) handleRequest(req *logical.Request) (retResp *logical.Response, r if errType != nil { retErr = multierror.Append(retErr, errType) } - return logical.ErrorResponse(ctErr.Error()), nil, retErr + return logical.ErrorResponse(ctErr.Error()), auth, retErr } // Attach the display name