ELK Stack — RBAC
Introduction
Role-Based Access Control (RBAC) is the mechanism that determines who can do what in Elasticsearch and Kibana. In a multi-team environment, you need to ensure that developers can only see their application’s logs, security analysts have access to SIEM features, and executives can view dashboards without accidentally modifying alerting rules.
Kibana RBAC works hand-in-hand with Elasticsearch security, combining Kibana privileges (which features and Spaces a user can access) with Elasticsearch privileges (which indices and cluster operations they can perform).
RBAC Architecture
The privilege hierarchy works in layers:
User
└── Role(s)
├── Kibana Privileges
│ ├── Space: Default → Feature access (read/all)
│ ├── Space: Platform → Feature access (read/all)
│ └── Global → Feature access across all spaces
└── Elasticsearch Privileges
├── Cluster privileges (monitor, manage, etc.)
└── Index privileges (read, write, manage, etc.)
├── Field-level security
└── Document-level security
A user can have multiple roles, and the effective privilege is the union of all assigned roles.
Built-in Roles
Elasticsearch and Kibana ship with several built-in roles:
| Role | Scope | Permissions |
|---|---|---|
superuser |
Full | Complete access to all Elasticsearch and Kibana features |
kibana_admin |
Kibana | Full access to all Kibana features in all Spaces |
kibana_system |
Internal | Used by the Kibana server process — do not assign to users |
viewer |
Read-only | Read access to all Kibana features in all Spaces |
editor |
Read-write | Read-write access to Kibana features (cannot manage security) |
monitoring_user |
Monitoring | Read access to cluster monitoring data |
ingest_admin |
Ingest | Manage ingest pipelines and index templates |
machine_learning_admin |
ML | Full access to Machine Learning features |
machine_learning_user |
ML | Read-only access to ML results |
Best practice: Never assign superuser to human users. Create custom roles with the minimum required privileges.
Creating Custom Roles
Via the UI
- Open Stack Management → Roles
- Click Create role
- Enter the role name — e.g.,
platform-readonly - Configure Elasticsearch privileges (see sections below)
- Configure Kibana privileges (see sections below)
- Click Create role
Via the API
curl -X PUT "https://localhost:9200/_security/role/platform-readonly" \
-H "Content-Type: application/json" \
-u elastic:changeme \
-d '{
"cluster": ["monitor"],
"indices": [
{
"names": ["logs-platform-*", "metrics-platform-*"],
"privileges": ["read", "view_index_metadata"],
"field_security": {
"grant": ["*"],
"except": ["user.email", "user.ip"]
},
"query": "{\"match\": {\"environment\": \"production\"}}"
}
],
"applications": [
{
"application": "kibana-.kibana",
"privileges": ["feature_discover.read", "feature_dashboard.read"],
"resources": ["space:platform"]
}
]
}'
Elasticsearch Cluster Privileges
Cluster privileges control cluster-wide operations:
| Privilege | Description |
|---|---|
monitor |
Read-only access to cluster state, health, and stats |
manage |
Full cluster management (indices, pipelines, snapshots) |
manage_security |
Manage users, roles, and API keys |
manage_index_templates |
Create and delete index templates |
manage_ingest_pipelines |
Create and delete ingest pipelines |
manage_ml |
Full Machine Learning management |
manage_pipeline |
Manage ingest pipelines |
transport_client |
Required for transport client connections |
all |
Unrestricted cluster access |
Recommendation: Grant monitor to most read-only roles. Reserve manage for admin roles.
Elasticsearch Index Privileges
Index privileges control access to specific indices:
| Privilege | Description |
|---|---|
read |
Read documents and search |
write |
Index, update, and delete documents |
create_index |
Create new indices |
delete_index |
Delete indices |
manage |
Full index management (settings, mappings, aliases) |
view_index_metadata |
View index metadata (required for Discover) |
monitor |
Read index stats and status |
all |
Unrestricted index access |
Pattern matching: Index names support wildcards — e.g., logs-platform-* grants access to all platform log indices.
Field-Level Security
Field-level security restricts which fields a user can see within documents. This is critical for privacy and compliance.
Grant Specific Fields
{
"indices": [
{
"names": ["logs-*"],
"privileges": ["read"],
"field_security": {
"grant": ["@timestamp", "message", "log.level", "host.name", "service.name"]
}
}
]
}
The user can only see the five granted fields — all other fields are invisible.
Exclude Specific Fields
{
"indices": [
{
"names": ["logs-*"],
"privileges": ["read"],
"field_security": {
"grant": ["*"],
"except": ["user.email", "user.ssn", "client.ip"]
}
}
]
}
The user sees all fields except the excluded PII fields.
Document-Level Security
Document-level security restricts which documents a user can see based on a query filter. The filter is applied transparently — the user never sees documents that don’t match.
Example — Only Production Documents
{
"indices": [
{
"names": ["logs-*"],
"privileges": ["read"],
"query": "{\"match\": {\"environment\": \"production\"}}"
}
]
}
Example — Only Documents from User’s Team
{
"indices": [
{
"names": ["logs-*"],
"privileges": ["read"],
"query": "{\"match\": {\"team\": \"platform\"}}"
}
]
}
Combining Field-Level and Document-Level Security
You can apply both simultaneously:
{
"indices": [
{
"names": ["logs-*"],
"privileges": ["read", "view_index_metadata"],
"field_security": {
"grant": ["*"],
"except": ["user.email"]
},
"query": "{\"match\": {\"environment\": \"production\"}}"
}
]
}
This role can see production documents only, with the user.email field redacted.
Kibana Privileges
Kibana privileges control access to Kibana features within a Space.
Feature Privileges
| Level | Description |
|---|---|
all |
Full read-write access to the feature |
read |
Read-only access to the feature |
none |
No access to the feature (hidden from navigation) |
Available Features
| Feature ID | Name |
|---|---|
discover |
Discover |
dashboard |
Dashboards |
visualize |
Visualizations |
canvas |
Canvas |
maps |
Maps |
ml |
Machine Learning |
alerting |
Alerting (Rules) |
actions |
Connectors (Actions) |
stackAlerts |
Stack Alerts |
siem |
Security (SIEM) |
dev_tools |
Dev Tools |
advancedSettings |
Advanced Settings |
savedObjectsManagement |
Saved Objects Management |
Setting Kibana Privileges via UI
- In the role editor, scroll to Kibana privileges
- Click Add Kibana privilege
- Select the Space(s) — one specific space, multiple spaces, or all spaces
- Set the base privilege:
- All — read-write access to all features
- Read — read-only access to all features
- Custom — set per-feature access
- If Custom, toggle each feature individually
Setting Kibana Privileges via API
{
"applications": [
{
"application": "kibana-.kibana",
"privileges": [
"feature_discover.read",
"feature_dashboard.all",
"feature_alerting.read"
],
"resources": ["space:platform"]
},
{
"application": "kibana-.kibana",
"privileges": ["feature_dashboard.read"],
"resources": ["space:executive"]
}
]
}
Assigning Roles to Users
Via the UI
- Open Stack Management → Users
- Click Create user or edit an existing user
- In the Roles field, add one or more roles
- Click Save
Via the API
curl -X POST "https://localhost:9200/_security/user/jane.doe" \
-H "Content-Type: application/json" \
-u elastic:changeme \
-d '{
"password": "s3cur3Pa$$w0rd",
"roles": ["platform-readonly", "kibana_admin"],
"full_name": "Jane Doe",
"email": "jane.doe@company.com"
}'
Listing Users
curl -X GET "https://localhost:9200/_security/user" \
-u elastic:changeme
Spaces + RBAC — Putting It All Together
The combination of Spaces and RBAC gives you a complete multi-tenant setup:
Example — Three-Team Setup
Role 1: platform-team
curl -X PUT "https://localhost:9200/_security/role/platform-team" \
-H "Content-Type: application/json" \
-u elastic:changeme \
-d '{
"cluster": ["monitor"],
"indices": [
{
"names": ["logs-platform-*", "metrics-platform-*"],
"privileges": ["read", "view_index_metadata"]
}
],
"applications": [
{
"application": "kibana-.kibana",
"privileges": [
"feature_discover.all",
"feature_dashboard.all",
"feature_alerting.all",
"feature_dev_tools.all"
],
"resources": ["space:platform"]
}
]
}'
Role 2: security-team
curl -X PUT "https://localhost:9200/_security/role/security-team" \
-H "Content-Type: application/json" \
-u elastic:changeme \
-d '{
"cluster": ["monitor"],
"indices": [
{
"names": ["logs-*", "filebeat-*", ".siem-*"],
"privileges": ["read", "view_index_metadata"]
}
],
"applications": [
{
"application": "kibana-.kibana",
"privileges": [
"feature_discover.all",
"feature_dashboard.all",
"feature_maps.all",
"feature_ml.all",
"feature_siem.all",
"feature_alerting.all"
],
"resources": ["space:security"]
}
]
}'
Role 3: executive-viewer
curl -X PUT "https://localhost:9200/_security/role/executive-viewer" \
-H "Content-Type: application/json" \
-u elastic:changeme \
-d '{
"cluster": [],
"indices": [
{
"names": ["metrics-business-*"],
"privileges": ["read", "view_index_metadata"]
}
],
"applications": [
{
"application": "kibana-.kibana",
"privileges": [
"feature_dashboard.read",
"feature_canvas.read"
],
"resources": ["space:executive"]
}
]
}'
Each team is isolated — platform engineers cannot see security dashboards, security analysts cannot access Dev Tools, and executives have a clean read-only Canvas experience.
Quick Reference
| Task | Method |
|---|---|
| Create role (UI) | Stack Management → Roles → Create role |
| Create role (API) | PUT /_security/role/<name> |
| Create user (API) | POST /_security/user/<username> |
| Assign roles to user | User editor → Roles field |
| Field-level security | Role → Index privileges → field_security |
| Document-level security | Role → Index privileges → query |
| Kibana feature privileges | Role → Kibana privileges → Custom → per-feature |
| Space-scoped privileges | "resources": ["space:<space-id>"] |
| List all roles | GET /_security/role |
| List all users | GET /_security/user |
| Delete a role | DELETE /_security/role/<name> |
Next: Practice Exercises →