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