Splunk HEC — CEF ingest from MaestroHub
Poll /audit/export from Splunk's HTTP Event Collector or the Modular HTTP Input. Records arrive as CEF and land in your existing CEF sourcetype without a custom parser.
1. Create an OAuth2 client for the collector
In MaestroHub:
- Settings → Identity & Access → OAuth2 clients → New client.
- Grant type: Client Credentials.
- Scopes:
audit_log:export(nothing else — the SIEM collector is a machine and needs nothing else). - Copy the client ID and client secret. Store the secret in Splunk's credential vault.
2. Fetch an access token
curl -s -X POST https://<your-maestro>/api/v1/oauth2/token \
-d grant_type=client_credentials \
-d client_id=$MAESTRO_CLIENT_ID \
-d client_secret=$MAESTRO_CLIENT_SECRET \
-d scope=audit_log:export \
| jq -r .access_token
Splunk's HEC integrations refresh the token on their own cadence. See Splunk docs — Modular Input token refresh for the reference config.
3. Configure the modular HTTP input
inputs.conf:
[maestrohub_audit://main]
endpoint = https://<your-maestro>/api/v1/audit/export
params = format=cef&from=${last_cursor_timestamp}&to=now&cursor=${last_cursor}&limit=1000
method = GET
auth_type = bearer
token = ${maestrohub_access_token}
interval = 60
sourcetype = cef
index = security_audit
${last_cursor} is Splunk's checkpoint variable — the modular input persists it across polls. On the first poll, it's empty; subsequent polls send the previous response's X-Audit-Next-Cursor.
4. Set the sourcetype extractor
props.conf:
[cef]
SHOULD_LINEMERGE = false
LINE_BREAKER = ([\r\n]+)CEF:
TRUNCATE = 10000
KV_MODE = auto
Splunk's out-of-the-box CEF extractor handles the header + extension parsing. The KV_MODE=auto line applies to the extension section.
5. Validate
Search:
sourcetype=cef | head 20
You should see recent audit events with signature=<action>, suser=<actor>, duser=<subject>, cs1=<correlation_id>, cs2=<org_id>, cat=iam|authentication|configuration.
Field reference
| CEF field | Maestro source | Note |
|---|---|---|
act | Action | e.g. role.assigned |
suser | Actor.Email (falls back to Actor.ID) | The actor identity |
duser | Subject | The user acted upon |
src | Actor.IPAddress | Origin IP |
requestClientApplication | Actor.UserAgent | Truncated at 1024 chars |
cat | Category | ECS-aligned: iam, authentication, configuration, process |
outcome | Result | success · failure · denied |
cs1 (label correlationId) | CorrelationID | OpenTelemetry trace id |
cs2 (label orgId) | OrgID | Multi-tenancy pivot |
cs3 (label impersonator) | ImpersonatorID | When set — admin impersonating a user |
cs4 (label resource) | ResourceType/ResourceID | The resource operated on |
Troubleshooting
Splunk shows cef events but the extractor didn't split fields.
Check props.conf — the extractor stanza name must match the sourcetype in inputs.conf (both cef).
429 in Splunk's modular-input log.
The collector polled faster than the rate limit. Extend interval to 60 (from a lower value) and — for large windows — set limit=1000 explicitly.
Response body is empty but X-Audit-Records-Exported: 0.
No records in the requested window. The endpoint response is well-formed; this just means the poll caught an empty window. Splunk skips the batch and re-polls on the next interval.
Bearer token returned 401. The access token expired. Splunk's HEC refresh cycle is typically 30 minutes; check that the token-refresh script is running.