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

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!