Skip to main content

 

Splunk Lantern

Making third-party apps RBAC-aware in Splunk Enterprise Security 8.6

Splunk Enterprise Security (ES) 8.6 gives developers of custom dashboards and applications that work with ES two complementary ways to integrate with the ES role-based access control (RBAC) model. Apps can publish permissions and role templates that administrators use to create custom roles, and apps can route dashboard searches through an RBAC-aware endpoint that returns only the findings and investigations the current user is allowed to see. 

You need both integrations when your app adds user-facing capabilities and displays queue-scoped ES data. A custom role determines what a user can do. Team-based queue permissions determine which findings and investigations that user can see or act on. Implementing only one side can leave an app with correctly gated features but misleading dashboards, or correctly filtered dashboards whose users cannot complete the related workflow. 

In a soon-to-be released feature, we will add a user interface that will enable the creation of custom roles via role templates. Role templates are a way to offer a slate of permissions, capabilities and ACLs (access control lists) that can be granted to a role as a package that guarantees a level of usability for users that possess that role. 

This article explains how to make both sides work together and provides a practical migration pattern for apps designed to run in ES 8.6 and higher. 

Team-based queues in brief 

Before changing an app, it is useful to understand the ES team-based queue model that these integrations are designed to preserve. Team-based queues divide the ES analyst workload into focused workspaces that reflect how a security operations center assigns responsibility. An organization might create queues by geography, business unit, security domain, analyst tier, or another operational boundary. 

Queue conditions evaluate fields on a finding and automatically route it to the appropriate team. Because the rules are evaluated in order, the first matching rule determines the queue. Findings that do not match a configured team queue remain in the default analyst queue for triage. The queue_id field identifies the security boundary associated with a finding or investigation, and the queue's access policy also governs related objects such as notes, attachments, and response plans. 

Roles determine access within each queue. The same custom role can have update access in its primary queue, read-only access in an oversight queue, and no access to a sensitive queue. ES expresses these controls as create, read, update, delete, and execute permissions assigned per role and per queue. 

When RBAC lockdown is enabled, queues are more than a way to organize work. Direct non-admin access to protected indexes and collections is restricted so that non-admin users cannot bypass queue policy with a raw search. ES UI and API requests use the current user's roles and queue permissions to return only findings and investigations the user’s role allows. Third-party dashboards must preserve this boundary instead of querying protected data directly. 

Readers who are new to the feature should first review Understanding team-based queues in Splunk Enterprise Security. That article explains the purpose of team-based queues, the queue_id security boundary, automated sorting, the default analyst queue, API enforcement, and operational best practices. The rest of this article applies that model to third-party app roles and dashboards. 

Understand the RBAC layers 

Splunk ES 8.5 introduced granular permissions for team-based queues. With that queue model in mind, there is a feature available in ES 8.6 that extends RBAC with a unified custom-role workflow and an RBAC-aware dashboard search endpoint. This feature can be made available by toggling a feature flag and will officially release as generally available in an upcoming ES release. 

For an integrated app, the complete access model has the following layers: 

Layer What it controls Where the app integrates
Splunk capability A low-level API, UI action, or product function  authorize.conf 
ES permission A workflow-oriented bundle of capabilities and any required ACL or KV Store grants  app_permissions.conf 
Role template A minimum-permission starting point for a persona. This set of permissions should enable success for the roles created from the template.   role_templates.conf 
Queue permission What a role can do in a particular team-based queue  ES team queue configuration 
Dashboard data scope Which queue-owned rows a panel returns to the current user 

savedsearches.conf

mc_dashboard_searches.conf

The Dashboard Studio definition 

Do not treat these layers as interchangeable. In particular: 

  • app_permissions.conf does not replace your capability definitions in authorize.conf. It organizes capabilities into administrator-friendly permissions. 
  • role_templates.conf references permission stanza names, not raw capabilities. 
  • A role template does not grant access to a team queue. An administrator still assigns the resulting custom role to queues. 
  • A role template can be additive and combined with an existing custom role to yield a union of the two permission sets. 
  • Queue access does not automatically make a direct search of the notable index safe. Queue-scoped dashboards must use the RBAC-aware search path. 

Plan the app's RBAC contract 

Start with the workflows your app supports, not with a list of configuration files. For each persona, document: 

  1. The app pages, API operations, and actions the persona must use. 
  2. The app-owned capabilities required for those operations. 
  3. The REST endpoint, knowledge object, or KV Store ACLs required in addition to the capabilities. 
  4. The ES-owned workflows the persona also needs, such as viewing findings and investigations or reading risk scores. 
  5. The team queues the customer is expected to grant to the role and the create, read, update, delete, or execute permissions required in each queue. 
  6. Every dashboard panel that reads findings, investigations, or another resource affected by RBAC lockdown. 

This inventory prevents a common failure mode: granting a capability but omitting a supporting ACL or creating a useful role while leaving the app's dashboard searches outside the queue security boundary. 

Publish app permissions 

Add an app_permissions.conf file to your app package: 

<your-app>/default/app_permissions.conf 

Each stanza defines the permission administrators see in the ES Roles and capabilities page. Use globally unique, lower-case stanza names. Prefix app-specific concepts with your app identifier so another app cannot accidentally define the same key. 

The following example exposes separate view and edit permissions for an example app: 

[acme_view_findings] 
capabilities = ["acme_findings_read"] 
description  = View Acme enrichment and finding context. 
display_name = View Acme finding context 
category     = Findings & Investigations 
messages     = 
metadata     = 
 
[acme_edit_detection_rules] 
capabilities = ["acme_detection_rules_read", "acme_detection_rules_write"] 
description  = Create and update Acme detection rules. 
display_name = Edit Acme detection rules 
category     = Detections 
messages     = 
metadata     = 

Use an existing ES category when one fits the workflow, such as Detections, Findings & Investigations, Threat Intelligence, Assets & Identities, Integrations, General settings, or Search settings. Create a new category only when the app represents a genuinely separate product domain. 

Include every dependency needed to make the permission functional. If a workflow also needs access to an app-owned REST endpoint, saved search, macro, or KV Store collection, add the required metadata or kvstore_perms entry rather than expecting administrators to repair the access model manually. 

Do not publish the following items as assignable permissions: 

  • Capabilities intended only for administrators or internal service accounts. 
  • Splunk platform capabilities owned by Splunk Core. 
  • Permissions owned by ES, Mission Control, or another app. Reference those permissions from a role template instead of redefining them. 

Register the custom configuration file for hot reload in your app's default/app.conf

[triggers] 
reload.app_permissions = simple 

Splunk's public app-development guidance also recommends shipping configuration and dashboard files in the app's default directory rather than packaging a local directory. See Anatomy of a Splunk app and the app.conf reference. 

To see the full set of supported configuration options for app_permissions.conf, see the following:
/opt/splunk/etc/apps/SA-Utils/README/app_permissions.conf.spec
/opt/splunk/etc/apps/SA-Utils/README/app_permissions.conf.example

Provide least-privilege role templates 

Add one template for each common non-admin persona your app supports: 

<your-app>/default/role_templates.conf 

For example: 

[acme_analyst]
display_name = Acme Analyst
description  = Investigates Acme findings and reviews related ES risk context.
permissions = ["acme_view_findings", "view_findings_and_investigations", "mc_risk_score_read"]
 
[acme_detection_engineer] 
display_name = Acme Detection Engineer 
description  = Maintains Acme detection rules and investigates their findings. 
permissions  = ["acme_view_findings", "acme_edit_detection_rules", "view_findings_and_investigations"] 

The example combines app-owned permissions with permission keys supplied by ES or Mission Control. Every key must resolve to a stanza in the merged app_permissions.conf inventory when an administrator applies the template. 

Use the following design principles: 

  • Model a daily workflow. Include only permissions without which the persona's normal work is blocked. 
  • Keep templates composable. A customer can select more than one template for a hybrid role; ES applies the union of the permissions. 
  • Do not create an admin template. Administrative access should remain an explicit decision. 
  • Do not copy permissions from another app. Reference their published keys. 
  • Remember that templates are presets. Applying a template copies its permissions into a custom role. It does not create role inheritance, and a later template change does not automatically modify roles already created from it. 

Register the custom configuration file for hot reload in your app's default/app.conf

[triggers] 
reload.role_templates = simple

Administrators can review capabilities in Configure > All configurations > Roles and capabilities. The public ES documentation describes the existing page and capability model in Manage capabilities for a role and the Capability reference for Splunk Enterprise Security

To see the full set of supported configuration options for role_templates.conf, see the following:
/opt/splunk/etc/apps/missioncontrol/README/role_templates.conf.spec
/opt/splunk/etc/apps/missioncontrol/README/role_templates.conf.example

Identify panels that need queue-aware searches 

RBAC lockdown is optional in ES, but apps must be designed to work when a customer enables it. Lockdown removes direct non-admin access to the notable and test_notable indexes and to sensitive Mission Control KV Store collections. As a result, a dashboard that queries these resources directly can return empty or incomplete results even when the same user can see findings in the Mission Control UI. For more information, see Role-based access control lockdown for Splunk Enterprise Security

Review each panel and choose the correct treatment: 

Panel source Recommended treatment
Findings or notable-event rows Use $queue_filter$ before aggregation. 
Investigation rows from mc_investigations_lookup or mc_investigations_stats  Use $queue_filter_investigations$; those rows already carry queue_id
tstats over an accelerated finding data model  Run tstats, map a joinable identifier such as event_id, then apply $queue_filter$ to the resulting rows. 
A lookup with no queue ID and cannot be associated with a queue through a stable identifier.  Keep it global and label it clearly; do not imply that it is queue-scoped. 
Data unrelated to a locked or queue-owned resource  Keep the direct search unless another access-control requirement applies. The proxy adds unnecessary dispatch overhead otherwise. 

The placement of the queue filter is a security and correctness requirement. Apply it to individual rows before stats, timechart, or another aggregation. Filtering aggregated results can expose totals derived from queues the user cannot access. 

Move panel SPL into a saved search 

Create a hidden, unscheduled saved search in your app's default/savedsearches.conf

[Acme App - Findings by Severity] 
search = | `es_notable_events` \ 
| search app_context="acme" \ 
$queue_filter$ \ 
| stats count by severity 
dispatchAs = user 
is_visible = false 
enableSched = 0 

The search must be app-owned by nobody and shared so the Mission Control endpoint can resolve it. Apply sharing at the individual-object level rather than exporting every saved search in the app. If your packaging process does not already establish the required metadata, add an object entry in metadata/default.meta while preserving your app's existing access policy: 

[savedsearches/Acme%20App%20-%20Findings%20by%20Severity] 
export = system 
owner = nobody 

Splunk documents object-specific sharing and export = system in Set permissions for knowledge objects. Verify the final owner and sharing state after installing the packaged app rather than relying only on a development instance's local.meta file. 

The available dashboard-search placeholders include: 

Placeholder in saved search SPL Use
$queue_filter$  Filter finding or notable rows by the queues the current user can read.
$queue_filter_investigations$  Filter investigation rows that already contain queue_id
$suppressed_notables$  Include or exclude suppressed findings based on the request's include_suppressed parameter. 
$period_earliest$  Override the earliest time for period-comparison searches. 

The suppression option is a special naming case: the dashboard request and allowlist use include_suppressed, while the saved-search SPL uses $suppressed_notables$. Do not use $include_suppressed$ in the SPL. 

Register an allowlisted dashboard search 

Add mc_dashboard_searches.conf to your app's default directory: 

[acme_findings_by_severity] 
savedsearch_name = Acme App - Findings by Severity 
allowed_tokens = queue_filter 

The stanza key is the public search_name used by the dashboard. Use lower-case snake case and prefix it with the app name or domain to avoid collisions after Splunk merges configuration from multiple apps. 

Allow only parameters the saved search needs. For example, a search that supports queue filtering and the suppression toggle uses: 

allowed_tokens = queue_filter, include_suppressed 

The allowlist is part of the endpoint's security model. Do not expose a general-purpose search or permit arbitrary SPL fragments through a token. 

Replace the direct Dashboard Studio query 

In the Dashboard Studio source, replace the direct search with a call to the public dashboard endpoint. Always include splunk_server=local and search_format=true, and remove the fields that the rest command adds automatically. 

Before: 

"ds_findings_by_severity": { 
  "type": "ds.search", 
  "options": { 
    "query": "| `es_notable_events` | search app_context=\"acme\" | stats count by severity" 
  } 
}

After: 

"ds_findings_by_severity": { 
  "type": "ds.search", 
  "options": { 
    "query": "| rest splunk_server=local /servicesNS/nobody/missioncontrol/public/v2/search/dashboards search_format=true search_name=\"acme_findings_by_severity\" earliest=\"-24h@h\" latest=\"now\" | fields - splunk_server _span _spandays" 
  } 
} 

Use the dashboard's existing time tokens in place of the static example when appropriate. Dashboard Studio supports source-level ds.search definitions, query parameters, inputs, and tokens; see Create search-based visualizations with ds.search and Tokens

The endpoint applies the following behavior: 

  • An administrator receives unfiltered results across queues. 
  • A non-admin user with queue access receives results filtered to readable queues. 
  • A non-admin user with access to the default queue can also receive rows with no queue assignment, according to ES default-queue behavior. 
  • A user with no readable queues receives an empty result without dispatching the saved search. 

For time-series panels, the REST result is a flat table. Reshape it after the endpoint call, for example with untable followed by timechart

Add an optional queue selector 

If a dashboard should let users narrow their view to one or more of their accessible queues, populate the input from the RBAC-filtered queues endpoint: 

"ds_queues_options": { 
  "type": "ds.search", 
  "name": "Accessible Queues", 
  "options": { 
    "query": "| rest splunk_server=local /servicesNS/nobody/missioncontrol/public/v2/queues search_format=true | table id, title | sort title", 
    "queryParameters": { "earliest": "-1s", "latest": "now" } 
  } 
} 

Use an input.multiselect whose values are queue IDs and whose labels are queue titles. An All queues value can use * as a sentinel. Convert the selected values into an empty string for all accessible queues or a comma-separated list for explicit queue selection: 

| eval selected_queues_raw="$selected_queues$" 
| eval queue_list=split(selected_queues_raw, ",") 
| eval queue_filtered=mvfilter(queue_list!="*" AND queue_list!="") 
| eval queue_id_param=if(mvcount(queue_filtered)>0, mvjoin(queue_filtered, ","), "") 

Then pass the result to an eligible panel: 

queue_id="$ds_input_tokens:result.queue_id_param$" 

The endpoint validates every requested queue ID against the current user's read access. A user cannot select a queue that the user is not authorized to read. 

Only pass queue_id to a saved search that contains $queue_filter$ or $queue_filter_investigations$. If the backend validates the queue ID but the saved search has no place to apply it, the panel can silently return global data while appearing to be filtered. This is more dangerous than omitting the selector because users might make decisions based on mixed scopes. Splunk's Dashboard Studio documentation explains how to populate a dropdown or multiselect from a data source.

We will be addressing this queue ID issue in a future release with a bugfix to ensure that the general risk described here is mitigated.

Validate the complete access path 

Test the complete access path in two phases. First, validate the app’s permissions, roles, queue assignments, and basic workflows with RBAC lockdown disabled. This establishes a working baseline and makes configuration problems easier to diagnose. Then enable lockdown and repeat the access and dashboard tests to verify that protected data cannot be accessed directly and that RBAC-aware dashboards return only authorized results. 

Use the following sequence on an ES 8.6 test environment: 

Phase 1: Establish a baseline without lockdown 

  1. Install the packaged app and verify that its permissions appear in the expected categories under Configure > All configurations > Roles and capabilities
  2. Create a custom role from each app role template. Confirm that the resulting role contains the expected underlying capabilities and no unrelated administrative capabilities. 
  3. Assign the custom roles to representative test users. 
  4. Under Configure > Findings and investigations > Team queues, grant each role access to the appropriate test queues and assign the intended queue permissions. Public instructions are available in Assign role-based access to a queue and Permissions for team-based queues
  5. Confirm that each persona can use the app features the template is intended to enable and cannot use excluded features. 
  6. Perform a dashboard smoke test as an administrator and representative non-admin user. Record the expected results before lockdown is enabled. 

This sequence confirms that the app, role templates, and queue policies are configured correctly. Do not interpret successful dashboard results in this phase as proof that the dashboard is lockdown-safe, because non-admin users might still have direct access to the underlying resources. 

Phase 2: Validate enforcement with lockdown enabled 

  1. Activate RBAC lockdown after confirming that the role and queue policies are in place. 
  2. Verify that a non-admin user can no longer query protected indexes and collections directly. 
  3. Repeat the app workflow tests for each persona. 
  4. Test every migrated dashboard as: 
    • An administrator. 
    • A user with access to one queue. 
    • A user with access to several queues. 
    • A user with no readable queues. 
  5. Test an explicit queue_id containing an unauthorized queue and verify that the request is rejected. 
  6. Test an unknown search_name and verify that the allowlist rejects it. 
  7. Confirm that global panels remain clearly identified and that queue-filtered panels contain only authorized data. 
  8. If appropriate for the test environment, deactivate lockdown and confirm that the change is reversible. 

The most important comparison is between the failed direct search and the successful RBAC-aware dashboard request for the same non-admin user. This demonstrates that the app is using the protected API path rather than relying on direct access to locked resources. 

Lockdown enforcement test matrix 

Test identity or input Expected result
admin or sc_admin  Full results across queues. 
Non-admin with one readable queue  Only rows and aggregates derived from that queue. 
Non-admin with several readable queues Union of the authorized queues. 
Non-admin with no readable queues  Empty result, not a permission error or leaked global count. 
queue_id containing an unauthorized queue Request rejected with error code MC_0232.
Unknown search_name  Request rejected, confirming the allowlist is active. 
Direct notable search as a non-admin after lockdown  Empty or unavailable, while the migrated dashboard still renders authorized data. 

It is important to verify the zero-result experience as well. A panel that correctly returns no rows should explain that the user has no matching data in accessible queues rather than suggesting that ES has no findings at all. 

Recommended development practices 

Design permissions around complete workflows 

Bundle capabilities that always travel together so an administrator cannot create a role that appears valid but fails halfway through a task. Keep optional or advanced operations in separate permissions so they can be granted deliberately. 

Keep role and queue policy explicit 

Assign queue permissions to the final custom role, not to a broadly inherited parent role. The public ES guidance recommends this approach because using the parent role can unintentionally grant queue access to every role that inherits from it. See Best practices for assigning permissions in team-based queues

Prefer fail-safe behavior 

No queue access should produce no data. Do not fall back to an unfiltered search, a cached administrator result, or a direct notable query when the endpoint returns an empty response. 

Separate queue-scoped and global metrics 

Some panels cannot be tied to a queue because their data has no queue_id and no joinable finding or investigation identifier. Keep those panels global, state that scope in the title or description, and avoid placing them beside queue-filtered totals without an explanation. 

Version the app intentionally 

The public dashboard endpoint is an ES 8.6 feature. If the same app package must support older ES releases, do not replace a working legacy dashboard with a call to an endpoint that does not exist there. Either establish ES 8.6 as the minimum version for the new app release or provide a tested version-aware experience. 

Treat configuration names as public contracts 

Permission keys can be referenced by customer roles and other apps. Dashboard search_name values can be referenced by shipped dashboard definitions. Prefix names, avoid renaming without a migration plan, and document additions or removals in release notes. 

Reviewing your app before release 

Before releasing an ES 8.6-compatible app, verify the following: 

Permissions and templates 

  • Every non-admin app capability needed by a user workflow appears in one appropriate app_permissions.conf stanza. 
  • Required endpoint, knowledge-object, and KV Store grants are included. 
  • No administrator-only or Splunk Core capability is exposed as an app permission. 
  • Every permission key in role_templates.conf resolves in the merged inventory. 
  • Each template represents a minimum viable persona and contains no unnecessary administrative access. 
  • reload.app_permissions = simple is present in default/app.conf

Dashboards 

  • Every panel that reads a locked or queue-owned resource has been classified. 
  • Queue filtering occurs before aggregation. 
  • Each proxied panel has a hidden saved search owned by nobody, an allowlist entry, and a public REST endpoint call with search_format=true
  • Each allowlist stanza permits only the parameters the search requires. 
  • A queue selector is connected only to searches that consume a queue-filter placeholder. 
  • Global panels are clearly labeled as global. 

Testing and documentation 

  • Tests cover administrators, partial queue access, no queue access, and unauthorized queue selection. 
  • The app's role templates are tested together with queue CRUD+X permissions. 
  • The minimum supported ES version and Mission Control requirement are documented. 
  • Upgrade notes explain that role templates are copied presets and do not retroactively update existing custom roles. 

Additional resources