diff --git a/CHANGELOG.md b/CHANGELOG.md index 85748702d6a2..f285ffff17fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGES: * token: Token renewals will now return token policies within the `token_policies` , identity policies within `identity_policies`, and the full policy set within `policies`. [[GH-8535](https://github.com/hashicorp/vault/pull/8535)] * cubbyhole: Reject reads and writes to an empty ("") path. [[GH-8971](https://github.com/hashicorp/vault/pull/8971)] * core: Remove the addition of newlines to parsed configuration when using integer/boolean values [[GH-8928](https://github.com/hashicorp/vault/pull/8928)] +* audit: Token TTL and issue time are now provided in the auth portion of audit logs. [[GH-9091](https://github.com/hashicorp/vault/pull/9091)] IMPROVEMENTS: diff --git a/audit/format.go b/audit/format.go index 329e7ba7125a..a25aa8bcde34 100644 --- a/audit/format.go +++ b/audit/format.go @@ -106,6 +106,7 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config EntityID: auth.EntityID, RemainingUses: req.ClientTokenRemainingUses, TokenType: auth.TokenType.String(), + TokenTTL: int64(auth.TTL.Seconds()), }, Request: &AuditRequest{ @@ -127,6 +128,10 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config }, } + if !auth.IssueTime.IsZero() { + reqEntry.Auth.TokenIssueTime = auth.IssueTime.Format(time.RFC3339) + } + if req.WrapInfo != nil { reqEntry.Request.WrapTTL = int(req.WrapInfo.TTL / time.Second) } @@ -212,6 +217,10 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config NumUses: resp.Auth.NumUses, EntityID: resp.Auth.EntityID, TokenType: resp.Auth.TokenType.String(), + TokenTTL: int64(resp.Auth.TTL.Seconds()), + } + if !resp.Auth.IssueTime.IsZero() { + respAuth.TokenIssueTime = resp.Auth.IssueTime.Format(time.RFC3339) } } @@ -258,6 +267,7 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config RemainingUses: req.ClientTokenRemainingUses, EntityID: auth.EntityID, TokenType: auth.TokenType.String(), + TokenTTL: int64(auth.TTL.Seconds()), }, Request: &AuditRequest{ @@ -289,6 +299,9 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config }, } + if !auth.IssueTime.IsZero() { + respEntry.Auth.TokenIssueTime = auth.IssueTime.Format(time.RFC3339) + } if req.WrapInfo != nil { respEntry.Request.WrapTTL = int(req.WrapInfo.TTL / time.Second) } @@ -359,6 +372,8 @@ type AuditAuth struct { RemainingUses int `json:"remaining_uses,omitempty"` EntityID string `json:"entity_id,omitempty"` TokenType string `json:"token_type,omitempty"` + TokenTTL int64 `json:"token_ttl,omitempty"` + TokenIssueTime string `json:"token_issue_time,omitempty"` } type AuditSecret struct { diff --git a/audit/format_json_test.go b/audit/format_json_test.go index a1e32a11035a..bfffe501b8f2 100644 --- a/audit/format_json_test.go +++ b/audit/format_json_test.go @@ -29,6 +29,7 @@ func TestFormatJSON_formatRequest(t *testing.T) { expectedResultStr := fmt.Sprintf(testFormatJSONReqBasicStrFmt, salter.GetIdentifiedHMAC("foo")) + issueTime, _ := time.Parse(time.RFC3339, "2020-05-28T13:40:18-05:00") cases := map[string]struct { Auth *logical.Auth Req *logical.Request @@ -45,6 +46,10 @@ func TestFormatJSON_formatRequest(t *testing.T) { NoDefaultPolicy: true, Policies: []string{"root"}, TokenType: logical.TokenTypeService, + LeaseOptions: logical.LeaseOptions{ + TTL: time.Hour * 4, + IssueTime: issueTime, + }, }, &logical.Request{ Operation: logical.UpdateOperation, @@ -72,6 +77,10 @@ func TestFormatJSON_formatRequest(t *testing.T) { NoDefaultPolicy: true, Policies: []string{"root"}, TokenType: logical.TokenTypeService, + LeaseOptions: logical.LeaseOptions{ + TTL: time.Hour * 4, + IssueTime: issueTime, + }, }, &logical.Request{ Operation: logical.UpdateOperation, @@ -143,5 +152,5 @@ func TestFormatJSON_formatRequest(t *testing.T) { } } -const testFormatJSONReqBasicStrFmt = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"client_token":"%s","accessor":"bar","display_name":"testtoken","policies":["root"],"no_default_policy":true,"metadata":null,"entity_id":"foobarentity","token_type":"service"},"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"],"no_default_policy":true,"metadata":null,"entity_id":"foobarentity","token_type":"service", "token_ttl": 14400, "token_issue_time": "2020-05-28T13:40:18-05:00"},"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 6c46d3d9db38..dae1f9fcdfde 100644 --- a/audit/format_jsonx_test.go +++ b/audit/format_jsonx_test.go @@ -26,6 +26,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) { } fooSalted := salter.GetIdentifiedHMAC("foo") + issueTime, _ := time.Parse(time.RFC3339, "2020-05-28T13:40:18-05:00") cases := map[string]struct { Auth *logical.Auth @@ -44,6 +45,10 @@ func TestFormatJSONx_formatRequest(t *testing.T) { NoDefaultPolicy: true, Policies: []string{"root"}, TokenType: logical.TokenTypeService, + LeaseOptions: logical.LeaseOptions{ + TTL: time.Hour * 4, + IssueTime: issueTime, + }, }, &logical.Request{ ID: "request", @@ -65,7 +70,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) { errors.New("this is an error"), "", "", - fmt.Sprintf(`bar%stesttokenfoobarentitytruerootservicethis is an error%sbarbarrequestrootupdate/footrue127.0.0.160request`, + fmt.Sprintf(`bar%stesttokenfoobarentitytrueroot2020-05-28T13:40:18-05:0014400servicethis is an error%sbarbarrequestrootupdate/footrue127.0.0.160request`, fooSalted, fooSalted), }, "auth, request with prefix": { @@ -77,6 +82,10 @@ func TestFormatJSONx_formatRequest(t *testing.T) { EntityID: "foobarentity", Policies: []string{"root"}, TokenType: logical.TokenTypeService, + LeaseOptions: logical.LeaseOptions{ + TTL: time.Hour * 4, + IssueTime: issueTime, + }, }, &logical.Request{ ID: "request", @@ -98,7 +107,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) { errors.New("this is an error"), "", "@cee: ", - fmt.Sprintf(`bar%stesttokenfoobarentitytruerootservicethis is an error%sbarbarrequestrootupdate/footrue127.0.0.160request`, + fmt.Sprintf(`bar%stesttokenfoobarentitytrueroot2020-05-28T13:40:18-05:0014400servicethis is an error%sbarbarrequestrootupdate/footrue127.0.0.160request`, fooSalted, fooSalted), }, } diff --git a/vault/request_handling.go b/vault/request_handling.go index eeeda8ae6d55..f0d82c195c40 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -343,6 +343,10 @@ func (c *Core) checkToken(ctx context.Context, req *logical.Request, unauth bool // Store the entity ID in the request object req.EntityID = te.EntityID auth.TokenType = te.Type + auth.TTL = te.TTL + if te.CreationTime > 0 { + auth.IssueTime = time.Unix(te.CreationTime, 0) + } } // Check the standard non-root ACLs. Return the token entry if it's not