Skip to main content
Version: 3.0

Splunk HEC: CEF ingest from MaestroHub

Bring MaestroHub audit events into Splunk as CEF, parsed by Splunk's built-in CEF extractor with no custom parser.

Where
Admin › Identity & Access › API Clients
Scope
audit_log:export
Role
One that exports the audit trail
Sourcetype
cef
Poll interval
60 seconds

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:

  1. Admin → Identity & Access → API Clients → Register Application, as a Confidential Client.
  2. Grant type: Client Credentials.
  3. Scopes: audit_log:export (nothing else: the SIEM collector is a machine and needs nothing else).
  4. Access: give the client a role that can export the audit trail. The scope alone is not enough: a new client has no access until it holds a role, and its requests are refused until then. In release 3.0 give it Workspace Administrator; with only the audit_log:export scope the client can still use nothing but the export. See the three things every setup needs for the choice of role. You can also add the role later under Roles on the client's page.
  5. 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=<workspace_id>, cat=iam|authentication|configuration.

Field reference​

CEF fieldMaestro sourceNote
actActione.g. role.assigned
suserActor.Email (falls back to Actor.ID)The actor identity
duserSubjectThe user acted upon
srcActor.IPAddressOrigin IP
requestClientApplicationActor.UserAgentTruncated at 1024 chars
catCategoryECS-aligned: iam, authentication, configuration, process
outcomeResultsuccess · failure · denied
cs1 (label correlationId)CorrelationIDOpenTelemetry trace id
cs2 (label workspaceId)WorkspaceIDWorkspace ID, the pivot across workspaces
cs3 (label impersonator)ImpersonatorIDWhen set: admin impersonating a user
cs4 (label resource)ResourceType/ResourceIDThe 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.