Files
parsedmarc/dashboards/splunk
Sean Whalen 826e78c390 Fix DMARC dashboard metrics (OSD + Splunk) and add dashboard-dev bootstrap (#736)
* OSD: fix aggregate dashboard metrics to sum(message_count)

13 panels on the DMARC aggregate dashboard were aggregating with `count`
(number of OSD docs) when they should have been summing `message_count`.
Each parsedmarc OSD doc represents one (source_ip, auth_results) tuple from
the XML and carries an integer message_count, so doc-counting reports
"distinct sources" rather than "messages". Panels with titles like "Message
volume by header from", "DMARC passage over time", etc. were producing
misleading numbers.

Affected panels: SPF/DKIM/Passed-DMARC pies; Reporting orgs; Sources by
reverse DNS / header from / name+type / ASN / country / IP; Map; SPF and
DKIM details. (DMARC failure email samples kept count — one OSD doc per
RUF sample, so it's correct. SMTP TLS panels untouched — they sum the
right session-count fields.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Splunk: align dashboards with OSD and fix query bugs

Aggregate dashboard:
- Add "Message sources by Autonomous System" panel (source_asn / as_name /
  as_domain), formatted "AS<n>" at render with eval, matching the OSD addition.
- DKIM details: add the missing dkim_aligned column.
- SPF details: reorder columns to OSD order (spf_aligned at end).
- Map / country titles renamed to match OSD ("Map of message sources by
  country", "Message sources by country").
- Map widget: stats count by Country -> stats sum(message_count) by
  Country, so the choropleth shades by message volume not record count.
- fillnull "none"/"unknown" applied to source_reverse_dns, source_base_domain,
  source_country to mirror OSD's missing-bucket labels.
- charting.fieldColors {true: green, false: red} on SPF/DKIM/Passed-DMARC
  pies and the DMARC-passage timechart.

Forensic dashboard:
- Restructure to match OSD's two-panel layout (markdown + samples table).
- Drop the country map / IP table / country-ISO table panels (not in OSD).
- Samples table columns aligned to OSD: arrival_date_utc, source.ip_address,
  from, subject, reply_to, authentication_results.
- Tolerate null headers in the base_search filter (was: parsed_sample.headers.From=*
  required field to exist; LinkedIn RUF sample with null From was filtered out).

SMTP TLS dashboard:
- Reorder metrics to OSD order (successful before failed).
- Domains panel: add policy_type bucket.
- Failure details: replace search-time `failed_session_count>0` (which
  doesn't evaluate against multivalued JSON paths in Splunk) with
  `result_type=*` for presence + post-stats `where failed_sessions>0`.
  Drop _time/successful_sessions columns; reorder to match OSD.
- Wire the existing policy_type input into all three searches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add dashboard-dev bootstrap script and VSCode task

dashboard-dev-bootstrap.sh brings up docker-compose.dashboard-dev.yml,
seeds parsedmarc sample data into ES + OS + Splunk via parsedmarc-dev.ini,
and re-imports every dashboard into Kibana, OpenSearch Dashboards, Grafana,
and Splunk. Idempotent: existence checks skip provisioning that's already
done; only the dashboard imports re-run unconditionally on every invocation
(that's the point of running it after a dashboard edit).

Notable provisioning quirks the script handles:
- Splunk's auto-created HEC token (from the SPLUNK_HEC_TOKEN env) ships
  with indexes=[] and index=default; rewrites it to allow the email index.
- ES 8.x rejects wildcard DELETEs by default; RESEED=1 enumerates daily
  parsedmarc indexes via _cat/indices and deletes one at a time.
- Splunk has no clean-in-place REST endpoint for live indexes; RESEED=1
  deletes and recreates the email index (then re-applies the HEC token).
- OSD security plugin tenants: imports target global_tenant explicitly
  via the securitytenant header so they're visible to the shared workspace
  rather than landing in the API user's private tenant. Override with
  OSD_TENANT=<name>.
- Splunk ships an in-product announcement view (scheduled_export_dashboard)
  with sharing=global; the script narrows it to sharing=app so it stops
  showing up in every app's dashboards list.

Adds a "Dev Dashboard: Bootstrap" task to .vscode/tasks.json that runs
the script.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* CHANGELOG: 9.10.3 entry for the dashboard metric fix and alignment work

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Bump version to 9.10.3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* CHANGELOG: warn against the "Create new objects with unique IDs" import mode

OSD's import dialog has two modes: the default "Check for existing objects"
(which honors saved-object IDs and overwrites in place when "Automatically
overwrite conflicts" is on) and "Create new objects with unique IDs" (which
imports under fresh UUIDs and leaves the buggy originals untouched). Picking
the second one means the dashboards keep rendering the wrong numbers because
the originals are never replaced. Spell that out so users don't fall into
the trap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* OSD: label the metric column "messages" instead of "Sum of message_count"

OSD's table column header defaults to "Sum of message_count" when the
metric agg has no customLabel. "messages" reads better and matches what
the panels are actually counting.

Applies to all 15 aggregate-DMARC visualizations that use sum(message_count).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* CHANGELOG: tighten the 9.10.3 entry — clearer and more actionable

Trim the verbose technical exposition; lead each fix with the user-visible
symptom. Move the action-required call out to its own header in upgrade
notes so the re-import instructions don't get lost in a wall of text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Move per-tool dashboard exports under a single dashboards/ directory

Consolidates the four sibling top-level folders (kibana/, opensearch/,
grafana/, splunk/) into dashboards/{kibana,opensearch,grafana,splunk}/.
Updates the only path references in tracked files: bootstrap script (5
lines), CHANGELOG.md (1 line), and the kibana/export.ndjson raw URL in
docs/source/elasticsearch.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* OSD: restore the "DKIM alignment" panel title on the aggregate dashboard

The DKIM alignment panel had no title override in panelsJSON, so OSD fell
back to the visualization's own name ("Aggregate DMARC DKIM alignment").
Every other pie/table on the same dashboard sets a clean title (SPF
alignment, Passed DMARC, etc.) — this was a stray regression. Set the
panel title to "DKIM alignment" to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Splunk: color the message-disposition timechart by severity

Reject is red, quarantine is yellow, none is green — same semantic
mapping as the SPF/DKIM/Passed-DMARC pies and the DMARC-passage
timechart, applied via charting.fieldColors. Matches OSD's existing
color overrides on the equivalent viz.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* CHANGELOG: clarify that "Create new objects with unique IDs" is the default

The OSD import dialog defaults to that mode — users have to actively
switch away from it, not just avoid picking it. Reword the upgrade note
to lead with the switch and explain why the default would silently
preserve the bug.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sean Whalen <seanthegeek@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 00:40:01 -04:00
..

===================
Splunk Installation
===================

Install Splunk for use with Docker
----------------------------------

Download latest Splunk image::

  docker pull splunk/splunk:latest

Run Splunk with Docker
----------------------

Listen on all network interfaces::

  docker run -d -p 8000:8000 -p 8088:8088 -e "SPLUNK_START_ARGS=--accept-license" -e "SPLUNK_PASSWORD=password1234" -e "SPLUNK_HEC_TOKEN=hec-token-1234" --name splunk splunk/splunk:latest

Listen on localhost for use with reverse proxy with base URL ``/splunk``::

  docker run -d -p 127.0.0.1:8000:8000 -p 127.0.0.1:8088:8088 -e "SPLUNK_START_ARGS=--accept-license" -e "SPLUNK_PASSWORD=password1234" -e "SPLUNK_HEC_TOKEN=hec-token-1234" -e "SPLUNK_ROOT_ENDPOINT=/splunk" --name splunk splunk/splunk:latest

Set up reverse proxy, e.g. Apache2::

  ProxyPass /splunk http://127.0.0.1:8000/splunk
  ProxyPassReverse /splunk http://127.0.0.1:8000/splunk

Splunk Configuration
--------------------

Access web UI at http://127.0.0.1:8000 and log in with ``admin:password1234``.

Create App and Index
~~~~~~~~~~~~~~~~~~~~

- Settings > Data > Indexes: New Index

  - Index name: "email"

- HEC token ``hec-token-1234`` should be already set up. 

  - Check under Settings > Data > Data inputs: HTTP Event Collector

- Apps > Manage Apps: Create app

  - Name: "parsedmarc"
  - Folder name: "parsedmarc"

Create Dashboards
~~~~~~~~~~~~~~~~~

1. Navigate to the app you want to add the dashboards to, or create a new app called DMARC
2. Click Dashboards
3. Click Create New Dashboard
4. Use a descriptive title, such as "Aggregate DMARC Data"
5. Click Create Dashboard
6. Click on the Source button
7. Paste the content of ''dmarc_aggregate_dashboard.xml`` into the source editor
8. If the index storing the DMARC data is not named email, replace index="email" accordingly
9. Click Save
10. Click Dashboards
11. Click Create New Dashboard
12. Use a descriptive title, such as "Forensic DMARC Data"
13. Click Create Dashboard
14. Click on the Source button
15. Paste the content of ''dmarc_forensic_dashboard.xml`` into the source editor
16. If the index storing the DMARC data is not named email, replace index="email" accordingly
17. Click Save

==============
Example Config 
==============

parsedmarc.ini::

  [splunk_hec]
  url = https://127.0.0.1:8088/
  token = hec-token-1234
  index = email
  skip_certificate_verification = True

Note that ``skip_certificate_verification = True`` disables security checks.

Run parsedmarc::

  python3 -m parsedmarc.cli -c parsedmarc.ini