Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log auth info on permission denied due to ACL #2754

Merged
merged 4 commits into from
Jun 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions audit/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we had omitted the ClientTokenRemainingUses in the response intentionally. @jefferai What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, let me know. I can pull it back out (and maybe add a comment noting the inconsistency is intentional). In the meantime, pushed a fix for the tests that failed, including adding some additional fields.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we had too. At the moment I don't remember the context around it; it may be in comments in the PR that introduced that change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the noise. I couldn't find such a conversation in the earlier PR (#2437). On a second thought, this is looking fine to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So leave it in as I have it, then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

},

Request: AuditRequest{
Expand Down
33 changes: 20 additions & 13 deletions audit/format_json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"errors"

"fmt"
"github.com/hashicorp/vault/helper/jsonutil"
"github.com/hashicorp/vault/helper/salt"
"github.com/hashicorp/vault/logical"
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -65,7 +69,7 @@ func TestFormatJSON_formatRequest(t *testing.T) {
},
errors.New("this is an error"),
"@cee: ",
testFormatJSONReqBasicStr,
expectedResultStr,
},
}

Expand All @@ -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)
}

Expand All @@ -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"}
`
33 changes: 20 additions & 13 deletions audit/format_jsonx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"errors"

"fmt"
"github.com/hashicorp/vault/helper/salt"
"github.com/hashicorp/vault/logical"
)
Expand All @@ -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",
Expand All @@ -46,10 +50,11 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
errors.New("this is an error"),
"",
"",
`<json:object name="auth"><json:string name="accessor"></json:string><json:string name="client_token"></json:string><json:string name="display_name"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
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",
Expand All @@ -66,7 +71,8 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
errors.New("this is an error"),
"",
"@cee: ",
`<json:object name="auth"><json:string name="accessor"></json:string><json:string name="client_token"></json:string><json:string name="display_name"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
fooSalted),
},
}

Expand All @@ -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)
Expand All @@ -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))
}
}
}
21 changes: 12 additions & 9 deletions vault/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion vault/request_handling.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe this change is necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is necessary to get it to log the info in the response entry as well. In the case of an error, including the ACL error scenario I'm trying to address here, the auth object from this return statement then gets passed to c.auditBroker.LogResponse on line 107.

}

// Attach the display name
Expand Down