Security Features in Kibana
Kibana's security features control who can access your data and what they can do with it. This includes authentication, authorization via roles, space-level isolation, and audit logging.
Security Architecture
┌─────────────────────────────────────────────────────────────┐
│ User │
│ │ │
│ Authentication │
│ (Who are you?) │
│ │ │
│ ┌────▼────┐ │
│ │ Roles │ │
│ └────┬────┘ │
│ │ │
│ ┌────────────┼────────────┐ │
│ │ │ │ │
│ ┌──────▼──────┐ ┌──▼──┐ ┌──────▼──────┐ │
│ │ Cluster │ │Index│ │ Kibana │ │
│ │ Privileges │ │Privs│ │ Privileges │ │
│ │(manage, │ │(r/w │ │(spaces, │ │
│ │ monitor) │ │index│ │ features) │ │
│ └─────────────┘ │data)│ └─────────────┘ │
│ └─────┘ │
└─────────────────────────────────────────────────────────────┘
Enabling Security
Security is enabled by default in Kibana 8.x. Verify in kibana.yml:
# kibana.yml - security settings
xpack.security.enabled: true
# Encryption key for saved objects (required for alerts, actions)
xpack.encryptedSavedObjects.encryptionKey: "min-32-character-encryption-key-here!"
# Session settings
xpack.security.session.idleTimeout: "1h"
xpack.security.session.lifespan: "24h"
# Audit logging
xpack.security.audit.enabled: true
# elasticsearch.yml - must also have security enabled
xpack.security.enabled: true
xpack.security.enrollment.enabled: true
Authentication
Built-in Authentication
Kibana uses Elasticsearch's authentication system. The default is username/password via the native realm.
Authentication Methods
| Method | Description | Config Level |
|---|---|---|
| Native realm | Username/password in Elasticsearch | Default |
| LDAP | Active Directory/LDAP integration | elasticsearch.yml |
| SAML | SSO via SAML 2.0 (Okta, Azure AD) | elasticsearch.yml + kibana.yml |
| OIDC | OpenID Connect (Google, Auth0) | elasticsearch.yml + kibana.yml |
| PKI | Client certificate authentication | elasticsearch.yml |
| Kerberos | Windows domain authentication | elasticsearch.yml |
| API keys | Programmatic access | Elasticsearch API |
| Token | Bearer token authentication | Elasticsearch API |
Configuring SAML (SSO)
Elasticsearch configuration:
# elasticsearch.yml
xpack.security.authc.realms.saml.saml1:
order: 2
idp.metadata.path: "saml/idp-metadata.xml"
idp.entity_id: "https://idp.example.com/"
sp.entity_id: "https://kibana.example.com"
sp.acs: "https://kibana.example.com/api/security/saml/callback"
sp.logout: "https://kibana.example.com/logout"
attributes.principal: "nameid"
attributes.groups: "groups"
Kibana configuration:
# kibana.yml
xpack.security.authc.providers:
saml.saml1:
order: 0
realm: saml1
description: "Log in with SSO"
basic.basic1:
order: 1
description: "Log in with username/password"
API Keys
Create API keys for programmatic access:
# Create an API key
curl -X POST "localhost:5601/api/security/api_key" \
-H "kbn-xsrf: true" \
-H "Content-Type: application/json" \
-u elastic:password \
-d '{
"name": "dashboard-reader-key",
"expiration": "30d",
"role_descriptors": {
"dashboard_reader": {
"cluster": [],
"indices": [
{
"names": ["logs-*"],
"privileges": ["read"]
}
]
}
}
}'
# Response
{
"id": "abc123",
"name": "dashboard-reader-key",
"api_key": "xyz789...",
"encoded": "YWJj..."
}
# Use the API key
curl -H "Authorization: ApiKey YWJj..." \
"localhost:5601/api/status"
Managing API Keys
View and revoke keys at Stack Management → Security → API Keys.
API Keys:
┌──────────────────────────┬──────────┬─────────────┬─────────┐
│ Name │ Status │ Expires │ Actions │
│ dashboard-reader-key │ Active │ Feb 14 2024 │ [Revoke]│
│ ci-pipeline-key │ Active │ Never │ [Revoke]│
│ temp-debug-key │ Expired │ Jan 1 2024 │ [Delete]│
└──────────────────────────┴──────────┴─────────────┴─────────┘
Users and Roles
Built-in Users
Elasticsearch ships with system users:
| User | Purpose | Use In |
|---|---|---|
elastic | Superuser | Initial setup, emergencies |
kibana_system | Kibana ↔ Elasticsearch | kibana.yml only |
logstash_system | Logstash monitoring | logstash.yml |
beats_system | Beats monitoring | beats configs |
apm_system | APM server | apm-server.yml |
remote_monitoring_user | Remote monitoring | Cross-cluster |
Never use the elastic superuser for daily work. Create dedicated users with appropriate roles.
Creating a User
Via UI:
- Go to Stack Management → Security → Users
- Click "Create user"
- Fill in:
Username: sarah.jones
Full name: Sarah Jones
Email: sarah@example.com
Password: ********
Roles: [dashboard_viewer, logs_reader]
Via API:
curl -X POST "localhost:9200/_security/user/sarah.jones" \
-H "Content-Type: application/json" \
-u elastic:password \
-d '{
"password": "secure-password-123",
"full_name": "Sarah Jones",
"email": "sarah@example.com",
"roles": ["dashboard_viewer", "logs_reader"]
}'
Built-in Roles
| Role | Access |
|---|---|
superuser | Full access to everything |
kibana_admin | Full Kibana access (no Elasticsearch management) |
kibana_system | System role for Kibana server |
monitoring_user | Read access to monitoring data |
reporting_user | Can generate reports |
viewer | Read-only access to all Kibana features |
editor | Create/edit dashboards, visualizations |
Creating Custom Roles
Via UI:
- Go to Stack Management → Security → Roles
- Click "Create role"
- Configure:
Role name: logs_analyst
Elasticsearch:
Cluster privileges: [monitor]
Index privileges:
Indices: filebeat-*, metricbeat-*
Privileges: [read, view_index_metadata]
Field security: (optional) restrict visible fields
Document security: (optional) restrict visible documents
Kibana:
Space: Production
Privileges:
Discover: Read
Dashboard: Read
Visualize: Read
Canvas: None
Maps: Read
Machine Learning: None
Stack Management: None
Via API:
curl -X PUT "localhost:9200/_security/role/logs_analyst" \
-H "Content-Type: application/json" \
-u elastic:password \
-d '{
"cluster": ["monitor"],
"indices": [
{
"names": ["filebeat-*", "metricbeat-*"],
"privileges": ["read", "view_index_metadata"],
"field_security": {
"grant": ["@timestamp", "message", "level", "service.*", "host.*"]
},
"query": "{\"bool\":{\"filter\":[{\"term\":{\"environment\":\"production\"}}]}}"
}
],
"applications": [
{
"application": "kibana-.kibana",
"privileges": ["feature_discover.read", "feature_dashboard.read"],
"resources": ["space:production"]
}
]
}'
Index-Level Security
Field-Level Security
Restrict which fields a role can see:
# Role that can only see specific fields
curl -X PUT "localhost:9200/_security/role/limited_viewer" \
-H "Content-Type: application/json" \
-d '{
"indices": [
{
"names": ["orders-*"],
"privileges": ["read"],
"field_security": {
"grant": [
"order_id",
"order_date",
"category",
"total_price"
],
"except": [
"customer_ssn",
"customer_credit_card"
]
}
}
]
}'
Result: Users with this role see order data but never see SSN or credit card fields.
Document-Level Security
Restrict which documents a role can see:
# Role that only sees US region data
curl -X PUT "localhost:9200/_security/role/us_analyst" \
-H "Content-Type: application/json" \
-d '{
"indices": [
{
"names": ["sales-*"],
"privileges": ["read"],
"query": {
"bool": {
"filter": [
{ "term": { "region": "us" } }
]
}
}
}
]
}'
Result: Users with this role only see documents where region is "us", even though the index contains data for all regions.
Combining Field and Document Security
# European marketing analyst: only EU data, no PII
curl -X PUT "localhost:9200/_security/role/eu_marketing" \
-H "Content-Type: application/json" \
-d '{
"indices": [
{
"names": ["customers-*"],
"privileges": ["read"],
"field_security": {
"grant": ["*"],
"except": ["email", "phone", "address", "ip_address"]
},
"query": {
"bool": {
"filter": [
{ "term": { "region": "eu" } }
]
}
}
}
]
}'
Kibana Spaces Security
Feature Privileges per Space
Each role can have different privileges in different spaces:
Role: team_lead
Space: Production
├── Discover: Read
├── Dashboard: Read
├── Visualize: Read
├── Alerts: All
└── Stack Management: None
Space: Development
├── Discover: All
├── Dashboard: All
├── Visualize: All
├── Alerts: All
└── Stack Management: All
Space: Marketing (no access)
Privilege Levels
| Level | Can Do |
|---|---|
| None | No access (feature hidden) |
| Read | View only (cannot create or edit) |
| All | Full access (create, edit, delete) |
Setting Up Team-Based Access
Example organization:
Engineering Team:
Users: alice, bob, charlie
Role: engineering_team
Spaces: Engineering (All), Production (Read)
Marketing Team:
Users: diana, eve
Role: marketing_team
Spaces: Marketing (All)
Executive Team:
Users: frank
Role: executive_viewer
Spaces: All spaces (Read only)
Admin Team:
Users: grace
Role: kibana_admin
Spaces: All spaces (All)
Creating the engineering role:
curl -X PUT "localhost:9200/_security/role/engineering_team" \
-H "Content-Type: application/json" \
-d '{
"cluster": ["monitor"],
"indices": [
{
"names": ["logs-*", "metrics-*", "apm-*"],
"privileges": ["read", "view_index_metadata"]
}
],
"applications": [
{
"application": "kibana-.kibana",
"privileges": [
"feature_discover.all",
"feature_dashboard.all",
"feature_visualize.all",
"feature_actions.all",
"feature_alerting.all"
],
"resources": ["space:engineering"]
},
{
"application": "kibana-.kibana",
"privileges": [
"feature_discover.read",
"feature_dashboard.read"
],
"resources": ["space:production"]
}
]
}'
Session Management
Session Configuration
# kibana.yml
xpack.security.session:
# Idle timeout - logout after inactivity
idleTimeout: "1h"
# Maximum session lifespan
lifespan: "24h"
# Cleanup interval for expired sessions
cleanupInterval: "1h"
Session Settings by Use Case
Office environment:
idleTimeout: "2h"
lifespan: "8h"
High-security environment:
idleTimeout: "15m"
lifespan: "1h"
Wall display / kiosk:
idleTimeout: "24h"
lifespan: "7d"
API access:
Use API keys instead of sessions
Audit Logging
Track who did what in Kibana.
Enabling Audit Logs
# elasticsearch.yml
xpack.security.audit.enabled: true
xpack.security.audit.logfile.events.include:
- "access_granted"
- "access_denied"
- "authentication_failed"
- "authentication_success"
- "connection_denied"
- "realm_authentication_failed"
Audit Log Format
{
"@timestamp": "2024-01-15T10:30:00.000Z",
"event.action": "access_granted",
"user.name": "sarah.jones",
"user.roles": ["logs_analyst"],
"authentication.type": "realm",
"url.path": "/api/saved_objects/dashboard/abc123",
"request.method": "GET",
"kibana.space_id": "production"
}
Monitoring Audit Logs
Ingest audit logs into Elasticsearch for analysis:
# filebeat.yml - ship audit logs
filebeat.inputs:
- type: log
paths:
- /var/log/elasticsearch/*_audit.json
json.keys_under_root: true
json.add_error_key: true
Then create a dashboard to monitor:
- Failed authentication attempts
- Access denied events
- Unusual access patterns
- After-hours activity
Encrypted Communications
TLS/SSL Configuration
# kibana.yml - HTTPS for browser connections
server.ssl.enabled: true
server.ssl.certificate: "/path/to/kibana-server.crt"
server.ssl.key: "/path/to/kibana-server.key"
# Kibana → Elasticsearch TLS
elasticsearch.ssl.certificateAuthorities: ["/path/to/ca.crt"]
elasticsearch.ssl.verificationMode: full
# elasticsearch.yml - Transport and HTTP TLS
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: http-certificate.p12
Generating Certificates
# Generate CA and certificates using elasticsearch-certutil
bin/elasticsearch-certutil ca --out elastic-stack-ca.p12
# Generate node certificates
bin/elasticsearch-certutil cert \
--ca elastic-stack-ca.p12 \
--out elastic-certificates.p12
# Generate PEM files for Kibana
bin/elasticsearch-certutil cert \
--ca elastic-stack-ca.p12 \
--pem \
--out kibana-certs.zip
Practical Examples
Example 1: Read-Only Dashboard User
For stakeholders who only need to view dashboards:
# Create role
curl -X PUT "localhost:9200/_security/role/dashboard_viewer" \
-H "Content-Type: application/json" \
-d '{
"indices": [
{
"names": ["kibana_sample_data_*", "business-*"],
"privileges": ["read"]
}
],
"applications": [
{
"application": "kibana-.kibana",
"privileges": ["feature_dashboard.read"],
"resources": ["space:default"]
}
]
}'
# Create user
curl -X POST "localhost:9200/_security/user/viewer01" \
-H "Content-Type: application/json" \
-d '{
"password": "viewer-password-123",
"full_name": "Dashboard Viewer",
"roles": ["dashboard_viewer"]
}'
Example 2: Multi-Tenant Setup
Different teams see only their own data:
# Team A role - only sees team_a data
curl -X PUT "localhost:9200/_security/role/team_a_role" \
-H "Content-Type: application/json" \
-d '{
"indices": [
{
"names": ["logs-*"],
"privileges": ["read"],
"query": {"term": {"team": "team_a"}}
}
],
"applications": [
{
"application": "kibana-.kibana",
"privileges": ["feature_discover.all", "feature_dashboard.all"],
"resources": ["space:team-a"]
}
]
}'
# Team B role - only sees team_b data
curl -X PUT "localhost:9200/_security/role/team_b_role" \
-H "Content-Type: application/json" \
-d '{
"indices": [
{
"names": ["logs-*"],
"privileges": ["read"],
"query": {"term": {"team": "team_b"}}
}
],
"applications": [
{
"application": "kibana-.kibana",
"privileges": ["feature_discover.all", "feature_dashboard.all"],
"resources": ["space:team-b"]
}
]
}'
Example 3: Alerting-Only User
For on-call engineers who manage alerts but don't edit dashboards:
curl -X PUT "localhost:9200/_security/role/oncall_engineer" \
-H "Content-Type: application/json" \
-d '{
"cluster": ["monitor"],
"indices": [
{
"names": ["logs-*", "metrics-*"],
"privileges": ["read"]
}
],
"applications": [
{
"application": "kibana-.kibana",
"privileges": [
"feature_discover.read",
"feature_dashboard.read",
"feature_alerting.all",
"feature_actions.read"
],
"resources": ["space:production"]
}
]
}'
Common Issues
"Not authorized" errors
Cause: User lacks required privilege.
Debug:
- Check user's roles: Stack Management → Users → click user
- Check role permissions: Stack Management → Roles → click role
- Verify index privileges include the correct index pattern
- Check Kibana feature privileges for the correct space
Can't see data in Discover
Causes:
- Index privilege missing for the data view's index pattern
- Document-level security filtering out all documents
- Wrong space (data view doesn't exist in this space)
Alerts not sending (permission error)
Fix: The user who created the rule needs:
executeprivilege on connectors- Read access to the relevant indices
- The rule runs as the creating user's permissions
Session expires too quickly
Fix: Adjust kibana.yml:
xpack.security.session:
idleTimeout: "4h" # Increase idle timeout
lifespan: "12h" # Increase max session
Security Checklist
Authentication:
✅ Don't use elastic superuser for daily work
✅ Enable SSO/SAML for enterprise environments
✅ Set strong password policies
✅ Use API keys for automation (not user credentials)
✅ Configure session timeouts
Authorization:
✅ Follow least-privilege principle
✅ Use custom roles (avoid superuser)
✅ Implement field-level security for sensitive data
✅ Use document-level security for multi-tenant data
✅ Separate spaces per team/environment
Encryption:
✅ Enable TLS for browser → Kibana
✅ Enable TLS for Kibana → Elasticsearch
✅ Set xpack.encryptedSavedObjects.encryptionKey
✅ Rotate certificates before expiry
Monitoring:
✅ Enable audit logging
✅ Monitor failed authentication attempts
✅ Review access patterns regularly
✅ Alert on suspicious activity
Summary
In this chapter, you learned:
- ✅ Security architecture: authentication, authorization, encryption
- ✅ Authentication methods: native, SAML/SSO, API keys
- ✅ Creating users and custom roles with granular privileges
- ✅ Field-level and document-level security for data isolation
- ✅ Space-based access control for team separation
- ✅ Session management and audit logging
- ✅ TLS/SSL configuration for encrypted communication
- ✅ Practical multi-tenant and team-based access patterns
Next: Using machine learning features for anomaly detection and forecasting!