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

MethodDescriptionConfig Level
Native realmUsername/password in ElasticsearchDefault
LDAPActive Directory/LDAP integrationelasticsearch.yml
SAMLSSO via SAML 2.0 (Okta, Azure AD)elasticsearch.yml + kibana.yml
OIDCOpenID Connect (Google, Auth0)elasticsearch.yml + kibana.yml
PKIClient certificate authenticationelasticsearch.yml
KerberosWindows domain authenticationelasticsearch.yml
API keysProgrammatic accessElasticsearch API
TokenBearer token authenticationElasticsearch 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 ManagementSecurityAPI 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:

UserPurposeUse In
elasticSuperuserInitial setup, emergencies
kibana_systemKibana ↔ Elasticsearchkibana.yml only
logstash_systemLogstash monitoringlogstash.yml
beats_systemBeats monitoringbeats configs
apm_systemAPM serverapm-server.yml
remote_monitoring_userRemote monitoringCross-cluster

Never use the elastic superuser for daily work. Create dedicated users with appropriate roles.

Creating a User

Via UI:

  1. Go to Stack ManagementSecurityUsers
  2. Click "Create user"
  3. 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

RoleAccess
superuserFull access to everything
kibana_adminFull Kibana access (no Elasticsearch management)
kibana_systemSystem role for Kibana server
monitoring_userRead access to monitoring data
reporting_userCan generate reports
viewerRead-only access to all Kibana features
editorCreate/edit dashboards, visualizations

Creating Custom Roles

Via UI:

  1. Go to Stack ManagementSecurityRoles
  2. Click "Create role"
  3. 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

LevelCan Do
NoneNo access (feature hidden)
ReadView only (cannot create or edit)
AllFull 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:

  1. Check user's roles: Stack ManagementUsers → click user
  2. Check role permissions: Stack ManagementRoles → click role
  3. Verify index privileges include the correct index pattern
  4. Check Kibana feature privileges for the correct space

Can't see data in Discover

Causes:

  1. Index privilege missing for the data view's index pattern
  2. Document-level security filtering out all documents
  3. Wrong space (data view doesn't exist in this space)

Alerts not sending (permission error)

Fix: The user who created the rule needs:

  • execute privilege 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

Next Steps

Continue to 12-machine-learning.md for machine learning features for anomaly detection and forecasting.