Implement comprehensive security controls in Kibana 8 with field-level restrictions, document-level security, and role-based access control for production Elasticsearch deployments.
Prerequisites
- Elasticsearch 8 installed and running
- Kibana 8 installed
- Administrative access to the server
- Basic understanding of Elasticsearch and Kibana
What this solves
Kibana 8 advanced security enables granular access control for Elasticsearch data through field-level restrictions, document-level security, and role-based access control (RBAC). This configuration prevents unauthorized users from viewing sensitive fields, restricts access to specific documents based on queries, and manages user permissions through customizable roles and spaces.
Step-by-step configuration
Install required packages
Update your system and install curl for API interactions with Elasticsearch.
sudo apt update
sudo apt install -y curl jq
Configure Elasticsearch security foundation
Enable security features in Elasticsearch configuration to support advanced authentication and authorization.
xpack.security.enabled: true
xpack.security.enrollment.enabled: true
xpack.security.http.ssl:
enabled: true
keystore.path: certs/http.p12
xpack.security.transport.ssl:
enabled: true
verification_mode: certificate
keystore.path: certs/transport.p12
truststore.path: certs/transport.p12
xpack.license.self_generated.type: basic
Restart Elasticsearch service
Apply the security configuration changes by restarting Elasticsearch.
sudo systemctl restart elasticsearch
sudo systemctl status elasticsearch
Set up built-in user passwords
Generate passwords for Elasticsearch built-in users including kibana_system for Kibana authentication.
sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto
Configure Kibana authentication
Set up Kibana to authenticate with Elasticsearch using the kibana_system user credentials.
server.port: 5601
server.host: "0.0.0.0"
elasticsearch.hosts: ["https://localhost:9200"]
elasticsearch.username: "kibana_system"
elasticsearch.password: "YOUR_KIBANA_SYSTEM_PASSWORD"
elasticsearch.ssl.certificateAuthorities: ["/etc/elasticsearch/certs/http_ca.crt"]
xpack.security.encryptionKey: "something_at_least_32_characters_long"
xpack.encryptedSavedObjects.encryptionKey: "something_at_least_32_characters_long"
xpack.reporting.encryptionKey: "something_at_least_32_characters_long"
Copy Elasticsearch CA certificate
Copy the Elasticsearch CA certificate to Kibana's accessible location for SSL verification.
sudo cp /etc/elasticsearch/certs/http_ca.crt /etc/kibana/
sudo chown kibana:kibana /etc/kibana/http_ca.crt
sudo chmod 644 /etc/kibana/http_ca.crt
Start Kibana service
Enable and start Kibana with the security configuration.
sudo systemctl enable --now kibana
sudo systemctl status kibana
Create custom roles with field-level security
Define a role that restricts access to specific fields in your indices. This role denies access to sensitive fields like SSN and credit card numbers.
curl -X PUT "https://localhost:9200/_security/role/sales_analyst" \
-H "Content-Type: application/json" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt \
-d '{
"cluster": [],
"indices": [
{
"names": ["sales-", "customer-"],
"privileges": ["read"],
"field_security": {
"except": ["ssn", "credit_card", "salary", "personal_info.*"]
},
"query": {
"bool": {
"must": {
"range": {
"@timestamp": {
"gte": "now-90d"
}
}
}
}
}
}
],
"applications": [],
"run_as": [],
"metadata": {
"description": "Sales analyst with field restrictions and 90-day data access"
},
"transient_metadata": {
"enabled": true
}
}'
Create role with document-level security
Configure a role that restricts access to documents based on a query filter, limiting users to see only data from their region.
curl -X PUT "https://localhost:9200/_security/role/regional_manager" \
-H "Content-Type: application/json" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt \
-d '{
"cluster": [],
"indices": [
{
"names": ["sales-", "orders-"],
"privileges": ["read", "view_index_metadata"],
"field_security": {
"grant": ["*"],
"except": ["internal_notes", "cost_price"]
},
"query": {
"template": {
"source": {
"term": {
"region.keyword": "{{#toJson}}_user.metadata.region{{/toJson}}"
}
}
}
}
}
],
"metadata": {
"description": "Regional manager with territory-based document filtering"
}
}'
Create read-only analyst role
Set up a basic read-only role for analysts with access to specific index patterns and Kibana features.
curl -X PUT "https://localhost:9200/_security/role/data_analyst" \
-H "Content-Type: application/json" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt \
-d '{
"cluster": ["monitor"],
"indices": [
{
"names": ["logs-", "metrics-"],
"privileges": ["read", "view_index_metadata"]
}
],
"applications": [
{
"application": "kibana-.kibana",
"privileges": ["feature_discover.read", "feature_visualize.read", "feature_dashboard.read"],
"resources": ["*"]
}
]
}'
Create users with custom roles
Create users and assign them the custom roles with field and document-level restrictions.
# Create sales analyst user
curl -X POST "https://localhost:9200/_security/user/jane_analyst" \
-H "Content-Type: application/json" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt \
-d '{
"password": "SecurePass123!",
"roles": ["sales_analyst", "kibana_user"],
"full_name": "Jane Smith",
"email": "jane.smith@example.com",
"metadata": {
"department": "Sales Analytics"
}
}'
Create regional manager user
curl -X POST "https://localhost:9200/_security/user/bob_manager" \
-H "Content-Type: application/json" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt \
-d '{
"password": "SecurePass456!",
"roles": ["regional_manager", "kibana_user"],
"full_name": "Bob Johnson",
"email": "bob.johnson@example.com",
"metadata": {
"department": "Regional Management",
"region": "west"
}
}'
Configure Kibana spaces for role isolation
Create dedicated Kibana spaces for different user groups to provide additional isolation.
curl -X POST "http://localhost:5601/api/spaces/space" \
-H "Content-Type: application/json" \
-H "kbn-xsrf: true" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
-d '{
"id": "sales-analytics",
"name": "Sales Analytics",
"description": "Space for sales team analysis and reporting",
"color": "#0078A0",
"initials": "SA",
"disabledFeatures": ["ml", "apm", "infrastructure"],
"imageUrl": ""
}'
curl -X POST "http://localhost:5601/api/spaces/space" \
-H "Content-Type: application/json" \
-H "kbn-xsrf: true" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
-d '{
"id": "operations",
"name": "Operations",
"description": "Operational monitoring and system metrics",
"color": "#D36086",
"initials": "OP",
"disabledFeatures": ["canvas", "graph"],
"imageUrl": ""
}'
Set up API keys for programmatic access
Create API keys with specific privileges for applications that need to access Elasticsearch data programmatically.
curl -X POST "https://localhost:9200/_security/api_key" \
-H "Content-Type: application/json" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt \
-d '{
"name": "analytics_app_key",
"expiration": "365d",
"role_descriptors": {
"analytics_access": {
"cluster": ["monitor"],
"indices": [
{
"names": ["analytics-*"],
"privileges": ["read"],
"field_security": {
"except": ["sensitive_data", "pii"]
}
}
]
}
},
"metadata": {
"application": "analytics-dashboard",
"environment": "production"
}
}'
Configure role mapping for LDAP integration
Set up role mapping to automatically assign roles based on LDAP group membership for enterprise environments.
curl -X POST "https://localhost:9200/_security/role_mapping/sales_team_mapping" \
-H "Content-Type: application/json" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt \
-d '{
"roles": ["sales_analyst", "kibana_user"],
"rules": {
"field": {
"groups": "cn=sales,ou=groups,dc=example,dc=com"
}
},
"enabled": true,
"metadata": {
"description": "Map LDAP sales group to sales analyst role"
}
}'
curl -X POST "https://localhost:9200/_security/role_mapping/manager_mapping" \
-H "Content-Type: application/json" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt \
-d '{
"roles": ["regional_manager", "kibana_user"],
"rules": {
"all": [
{
"field": {
"groups": "cn=managers,ou=groups,dc=example,dc=com"
}
},
{
"field": {
"dn": "*,ou=regional,dc=example,dc=com"
}
}
]
},
"enabled": true
}'
Test field-level security restrictions
Verify that field-level security is working by testing data access with restricted users.
# Test with sales analyst user (should not see restricted fields)
curl -X GET "https://localhost:9200/sales-*/_search?pretty" \
-H "Content-Type: application/json" \
-u "jane_analyst:SecurePass123!" \
--cacert /etc/elasticsearch/certs/http_ca.crt \
-d '{
"query": {
"match_all": {}
},
"size": 5
}'
Test with full access user (should see all fields)
curl -X GET "https://localhost:9200/sales-*/_search?pretty" \
-H "Content-Type: application/json" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt \
-d '{
"query": {
"match_all": {}
},
"size": 5
}'
Verify your setup
Test your Kibana security configuration and verify that roles and restrictions are working correctly.
# Check Elasticsearch cluster health
curl -X GET "https://localhost:9200/_cluster/health?pretty" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt
List all security roles
curl -X GET "https://localhost:9200/_security/role?pretty" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt
Check Kibana status
curl -X GET "http://localhost:5601/api/status" \
-H "kbn-xsrf: true" \
-u "elastic:YOUR_ELASTIC_PASSWORD"
List created users
curl -X GET "https://localhost:9200/_security/user?pretty" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt
Verify API key creation
curl -X GET "https://localhost:9200/_security/api_key?pretty" \
-u "elastic:YOUR_ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Kibana shows authentication error | Wrong kibana_system password in config | Update password in /etc/kibana/kibana.yml and restart Kibana |
| SSL certificate verification failed | Missing or wrong CA certificate path | Copy /etc/elasticsearch/certs/http_ca.crt to Kibana directory |
| User cannot see expected fields | Field security restrictions too broad | Check role field_security configuration and adjust grant/except arrays |
| Document-level security not working | Query template syntax error | Validate JSON query syntax and user metadata mapping |
| Role mapping not applying | LDAP group DN mismatch | Verify LDAP configuration and group distinguished names |
| API key authentication fails | Incorrect API key encoding | Use base64 encoding: echo -n "id:key" | base64 |
Next steps
- Configure Kibana LDAP authentication and RBAC with Active Directory integration
- Setup Elasticsearch 8 SSL/TLS encryption and advanced security hardening with authentication and access control
- Set up Kibana alerting and monitoring with Elasticsearch Watcher for automated threat detection
- Configure Elasticsearch cross-cluster search with security and field-level restrictions
- Implement Kibana audit logging and compliance monitoring for security requirements
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Global variables
SCRIPT_NAME=$(basename "$0")
KIBANA_PASSWORD=""
ELASTIC_PASSWORD=""
TEMP_FILES=()
# Usage message
usage() {
cat << EOF
Usage: $SCRIPT_NAME [OPTIONS]
Configure Kibana 8 advanced security with field-level restrictions and RBAC
OPTIONS:
-k, --kibana-password Kibana system user password (optional - will prompt if not provided)
-e, --elastic-password Elastic user password (optional - will prompt if not provided)
-h, --help Show this help message
Example:
$SCRIPT_NAME
$SCRIPT_NAME -k kibana_pass -e elastic_pass
EOF
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-k|--kibana-password)
KIBANA_PASSWORD="$2"
shift 2
;;
-e|--elastic-password)
ELASTIC_PASSWORD="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
echo -e "${RED}Error: Unknown option $1${NC}" >&2
usage >&2
exit 1
;;
esac
done
# Cleanup function
cleanup() {
local exit_code=$?
if [[ $exit_code -ne 0 ]]; then
echo -e "${RED}[ERROR] Script failed. Cleaning up...${NC}" >&2
for file in "${TEMP_FILES[@]}"; do
[[ -f "$file" ]] && rm -f "$file"
done
fi
exit $exit_code
}
trap cleanup ERR INT TERM
# Logging functions
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
# Check if running as root
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root or with sudo"
exit 1
fi
}
# Auto-detect distribution
detect_distro() {
if [[ ! -f /etc/os-release ]]; then
log_error "/etc/os-release not found. Cannot detect distribution."
exit 1
fi
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
log_info "Detected distribution: $PRETTY_NAME"
}
# Check prerequisites
check_prerequisites() {
local missing_services=()
if ! systemctl is-active elasticsearch >/dev/null 2>&1; then
missing_services+=("elasticsearch")
fi
if [[ ${#missing_services[@]} -gt 0 ]]; then
log_error "Required services not running: ${missing_services[*]}"
log_error "Please install and configure Elasticsearch first"
exit 1
fi
}
# Prompt for passwords if not provided
get_passwords() {
if [[ -z "$KIBANA_PASSWORD" ]]; then
read -s -p "Enter kibana_system password: " KIBANA_PASSWORD
echo
if [[ -z "$KIBANA_PASSWORD" ]]; then
log_error "Kibana password cannot be empty"
exit 1
fi
fi
if [[ -z "$ELASTIC_PASSWORD" ]]; then
read -s -p "Enter elastic user password: " ELASTIC_PASSWORD
echo
if [[ -z "$ELASTIC_PASSWORD" ]]; then
log_error "Elastic password cannot be empty"
exit 1
fi
fi
}
# Install required packages
install_packages() {
echo -e "${BLUE}[1/8]${NC} Installing required packages..."
$PKG_UPDATE
$PKG_INSTALL curl jq
log_success "Required packages installed"
}
# Configure Elasticsearch security
configure_elasticsearch_security() {
echo -e "${BLUE}[2/8]${NC} Configuring Elasticsearch security..."
local es_config="/etc/elasticsearch/elasticsearch.yml"
local backup_file="/etc/elasticsearch/elasticsearch.yml.backup.$(date +%s)"
if [[ ! -f "$es_config" ]]; then
log_error "Elasticsearch config not found at $es_config"
exit 1
fi
# Backup original config
cp "$es_config" "$backup_file"
TEMP_FILES+=("$backup_file")
# Add security configuration if not present
if ! grep -q "xpack.security.enabled" "$es_config"; then
cat >> "$es_config" << 'EOF'
# Security Configuration
xpack.security.enabled: true
xpack.security.enrollment.enabled: true
xpack.security.http.ssl:
enabled: true
keystore.path: certs/http.p12
xpack.security.transport.ssl:
enabled: true
verification_mode: certificate
keystore.path: certs/transport.p12
truststore.path: certs/transport.p12
xpack.license.self_generated.type: basic
EOF
fi
log_success "Elasticsearch security configuration updated"
}
# Restart Elasticsearch
restart_elasticsearch() {
echo -e "${BLUE}[3/8]${NC} Restarting Elasticsearch service..."
systemctl restart elasticsearch
# Wait for Elasticsearch to be ready
local count=0
while ! curl -k -s "https://localhost:9200" >/dev/null 2>&1; do
if [[ $count -ge 30 ]]; then
log_error "Elasticsearch failed to start within 30 seconds"
exit 1
fi
sleep 1
((count++))
done
log_success "Elasticsearch restarted successfully"
}
# Configure Kibana
configure_kibana() {
echo -e "${BLUE}[4/8]${NC} Configuring Kibana authentication..."
local kibana_config="/etc/kibana/kibana.yml"
local backup_file="/etc/kibana/kibana.yml.backup.$(date +%s)"
if [[ ! -f "$kibana_config" ]]; then
log_error "Kibana config not found at $kibana_config"
exit 1
fi
# Backup original config
cp "$kibana_config" "$backup_file"
TEMP_FILES+=("$backup_file")
# Generate encryption keys
local enc_key=$(openssl rand -base64 32)
local obj_key=$(openssl rand -base64 32)
local rep_key=$(openssl rand -base64 32)
# Create new Kibana configuration
cat > "$kibana_config" << EOF
server.port: 5601
server.host: "0.0.0.0"
elasticsearch.hosts: ["https://localhost:9200"]
elasticsearch.username: "kibana_system"
elasticsearch.password: "$KIBANA_PASSWORD"
elasticsearch.ssl.certificateAuthorities: ["/etc/kibana/http_ca.crt"]
xpack.security.encryptionKey: "$enc_key"
xpack.encryptedSavedObjects.encryptionKey: "$obj_key"
xpack.reporting.encryptionKey: "$rep_key"
EOF
chown kibana:kibana "$kibana_config"
chmod 640 "$kibana_config"
log_success "Kibana configuration updated"
}
# Copy CA certificate
copy_ca_certificate() {
echo -e "${BLUE}[5/8]${NC} Copying Elasticsearch CA certificate..."
if [[ -f "/etc/elasticsearch/certs/http_ca.crt" ]]; then
cp /etc/elasticsearch/certs/http_ca.crt /etc/kibana/
chown kibana:kibana /etc/kibana/http_ca.crt
chmod 644 /etc/kibana/http_ca.crt
log_success "CA certificate copied successfully"
else
log_error "CA certificate not found at /etc/elasticsearch/certs/http_ca.crt"
exit 1
fi
}
# Start Kibana service
start_kibana() {
echo -e "${BLUE}[6/8]${NC} Starting Kibana service..."
systemctl enable kibana
systemctl restart kibana
# Wait for Kibana to be ready
local count=0
while ! curl -s "http://localhost:5601/api/status" >/dev/null 2>&1; do
if [[ $count -ge 60 ]]; then
log_error "Kibana failed to start within 60 seconds"
exit 1
fi
sleep 1
((count++))
done
log_success "Kibana started successfully"
}
# Create security roles
create_security_roles() {
echo -e "${BLUE}[7/8]${NC} Creating custom security roles..."
# Create sales analyst role with field-level security
curl -X PUT "https://localhost:9200/_security/role/sales_analyst" \
-H "Content-Type: application/json" \
-u "elastic:$ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt \
-d '{
"cluster": [],
"indices": [
{
"names": ["sales-*", "customer-*"],
"privileges": ["read"],
"field_security": {
"except": ["ssn", "credit_card", "salary", "personal_info.*"]
},
"query": {
"bool": {
"must": {
"range": {
"@timestamp": {
"gte": "now-90d"
}
}
}
}
}
}
],
"metadata": {
"description": "Sales analyst with field restrictions and 90-day data access"
}
}' >/dev/null
# Create regional manager role with document-level security
curl -X PUT "https://localhost:9200/_security/role/regional_manager" \
-H "Content-Type: application/json" \
-u "elastic:$ELASTIC_PASSWORD" \
--cacert /etc/elasticsearch/certs/http_ca.crt \
-d '{
"cluster": ["monitor"],
"indices": [
{
"names": ["sales-*", "customer-*", "inventory-*"],
"privileges": ["read", "view_index_metadata"],
"query": {
"term": {
"region": "{{username}}"
}
}
}
],
"metadata": {
"description": "Regional manager with access to regional data only"
}
}' >/dev/null
log_success "Security roles created successfully"
}
# Verify installation
verify_installation() {
echo -e "${BLUE}[8/8]${NC} Verifying installation..."
local errors=0
# Check Elasticsearch
if ! curl -k -u "elastic:$ELASTIC_PASSWORD" "https://localhost:9200/_cluster/health" >/dev/null 2>&1; then
log_error "Elasticsearch health check failed"
((errors++))
fi
# Check Kibana
if ! curl -s "http://localhost:5601/api/status" | jq -e '.status.overall.level == "available"' >/dev/null 2>&1; then
log_error "Kibana status check failed"
((errors++))
fi
# Check roles
if ! curl -k -u "elastic:$ELASTIC_PASSWORD" "https://localhost:9200/_security/role/sales_analyst" >/dev/null 2>&1; then
log_error "Sales analyst role verification failed"
((errors++))
fi
if [[ $errors -eq 0 ]]; then
log_success "All verification checks passed"
echo -e "${GREEN}Kibana 8 advanced security configuration completed successfully!${NC}"
echo -e "${YELLOW}Access Kibana at: http://localhost:5601${NC}"
echo -e "${YELLOW}Login with elastic user and the provided password${NC}"
else
log_error "$errors verification checks failed"
exit 1
fi
}
# Main execution
main() {
log_info "Starting Kibana 8 advanced security configuration"
check_root
detect_distro
check_prerequisites
get_passwords
install_packages
configure_elasticsearch_security
restart_elasticsearch
configure_kibana
copy_ca_certificate
start_kibana
create_security_roles
verify_installation
log_success "Kibana 8 advanced security setup completed!"
}
main "$@"
Review the script before running. Execute with: bash install.sh