HashiCorp Vault — Audit Log Format

2 minute read

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