HashiCorp Vault — Audit Log Format
Vault audit logs are JSON-formatted, containing both the request and the response for every API interaction.
Log Entry Structure
Each API call produces two log entries — a request and a response:
{
"time": "2024-01-15T10:30:00.123456Z",
"type": "request",
"auth": {
"client_token": "hmac-sha256:abc123...",
"accessor": "hmac-sha256:def456...",
"display_name": "token-admin",
"policies": ["root"],
"token_policies": ["root"],
"metadata": null,
"entity_id": "entity-uuid",
"token_type": "service",
"token_ttl": 0
},
"request": {
"id": "request-uuid",
"operation": "read",
"mount_type": "kv",
"client_token": "hmac-sha256:abc123...",
"client_token_accessor": "hmac-sha256:def456...",
"namespace": {
"id": "root"
},
"path": "secret/data/myapp/config",
"remote_address": "10.0.1.50",
"remote_port": 43210
}
}
{
"time": "2024-01-15T10:30:00.125000Z",
"type": "response",
"auth": {
"client_token": "hmac-sha256:abc123...",
"accessor": "hmac-sha256:def456...",
"display_name": "token-admin",
"policies": ["root"]
},
"request": {
"id": "request-uuid",
"operation": "read",
"path": "secret/data/myapp/config"
},
"response": {
"mount_type": "kv",
"data": {
"data": {
"password": "hmac-sha256:ghi789...",
"username": "hmac-sha256:jkl012..."
},
"metadata": {
"created_time": "hmac-sha256:...",
"version": 1
}
}
}
}
Key Fields
| Field | Description |
|---|---|
time |
ISO 8601 timestamp |
type |
request or response |
auth.client_token |
HMAC’d token (not the real token) |
auth.display_name |
Human-readable token name |
auth.policies |
Policies attached to the token |
auth.entity_id |
Identity entity |
request.operation |
create, read, update, delete, list |
request.path |
API path accessed |
request.remote_address |
Client IP address |
response.data |
Response payload (values HMAC’d) |
HMAC’d Values
By default, Vault HMAC’s sensitive values with a per-audit-device salt:
Actual value: "super-secret-password"
In audit log: "hmac-sha256:3kJ4Hm9..."
Verify HMAC’d Values
You can verify a value matches its HMAC:
# Check if a value matches the HMAC in the audit log
vault write sys/audit-hash/file-audit \
input="super-secret-password"
# Returns the HMAC — compare with audit log entry
Parsing Audit Logs
Count Operations
# Count requests by operation type
cat /var/log/vault/audit.log | \
jq -r 'select(.type=="request") | .request.operation' | \
sort | uniq -c | sort -rn
# Output:
# 1523 read
# 342 update
# 201 create
# 89 list
# 12 delete
Find Access to Specific Path
# Who accessed the database credentials?
cat /var/log/vault/audit.log | \
jq -r 'select(.request.path | startswith("database/creds/")) |
"\(.time) \(.auth.display_name) \(.request.operation) \(.request.path) from \(.request.remote_address)"'
Failed Authentication Attempts
# Find auth failures
cat /var/log/vault/audit.log | \
jq -r 'select(.type=="response" and .response.data.error != null) |
"\(.time) \(.request.remote_address) \(.request.path) ERROR: \(.response.data.error)"'
Unique Clients
# List unique client IPs
cat /var/log/vault/audit.log | \
jq -r 'select(.type=="request") | .request.remote_address' | \
sort -u
Ship to ELK Stack
# Filebeat configuration
filebeat.inputs:
- type: log
paths:
- /var/log/vault/audit.log
json.keys_under_root: true
json.add_error_key: true
output.elasticsearch:
hosts: ["elasticsearch:9200"]
index: "vault-audit-%{+yyyy.MM.dd}"
Quick Reference
| Task | Command |
|---|---|
| Count operations | jq 'select(.type=="request") \| .request.operation' |
| Find path access | jq 'select(.request.path \| startswith("PATH"))' |
| Find errors | jq 'select(.response.data.error != null)' |
| Verify HMAC | vault write sys/audit-hash/DEVICE input=VALUE |
Next: Telemetry