Skip to content

Instantly share code, notes, and snippets.

@agoodkind
Created October 28, 2025 03:30
Show Gist options
  • Save agoodkind/394e59b16aa42d5bf4c773a38383d498 to your computer and use it in GitHub Desktop.
Save agoodkind/394e59b16aa42d5bf4c773a38383d498 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2025 Alex Goodkind
# Author: Alex Goodkind (agoodkind)
# License: Apache-2.0
# Source: https://www.elastic.co/elk-stack
APP="ELK-Stack"
var_tags="${var_tags:-logging;elasticsearch;kibana;logstash}"
var_cpu="${var_cpu:-4}"
var_ram="${var_ram:-8192}"
var_disk="${var_disk:-32}"
var_os="${var_os:-ubuntu}"
var_version="${var_version:-24.04}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /usr/share/elasticsearch ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_info "Updating ${APP}"
$STD apt-get update
$STD apt-get -y upgrade elasticsearch logstash kibana
$STD systemctl restart elasticsearch logstash kibana
msg_ok "Updated Successfully"
exit
}
start
build_container
description
msg_ok "Completed Successfully!\n"
msg_info " Installing Dependencies"
$STD apt-get install -y curl wget gnupg apt-transport-https ca-certificates openjdk-11-jre-headless vim net-tools htop unzip openssl
msg_ok "Installing Dependencies"
msg_info " Adding Elastic Repository"
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" > /etc/apt/sources.list.d/elastic-8.x.list
msg_ok "Adding Elastic Repository"
msg_info " Installing ELK Stack (Elasticsearch, Logstash, Kibana)"
$STD apt-get update
$STD apt-get install -y elasticsearch logstash kibana
msg_ok "Installing ELK Stack (Elasticsearch, Logstash, Kibana)"
msg_info " Configuring Elasticsearch"
cat >> /etc/elasticsearch/elasticsearch.yml << 'ELKEOF'
# Elasticsearch network configuration
# Prefer IPv6 and listen on all interfaces
network.host: [_::, 0.0.0.0]
http.port: 9200
# Cluster and node settings
cluster.name: elk-cluster
node.name: ${HOSTNAME}
# Disable security and SSL for demo/dev use
xpack.security.enabled: false
xpack.security.enrollment.enabled: false
xpack.security.http.ssl.enabled: false
xpack.security.transport.ssl.enabled: false
ELKEOF
cat > /etc/elasticsearch/jvm.options.d/heap.options << 'ELKEOF'
# JVM heap settings for Elasticsearch
-Xms2g
-Xmx2g
ELKEOF
msg_ok "Configuring Elasticsearch"
msg_info " Configuring Logstash"
mkdir -p /etc/logstash/conf.d
cat > /etc/logstash/conf.d/00-input.conf << 'ELKEOF'
# Logstash input configuration
input {
beats {
port => 5044
}
tcp {
port => 5000
codec => json
}
udp {
port => 5140
}
}
ELKEOF
cat > /etc/logstash/conf.d/30-output.conf << 'ELKEOF'
# Logstash output configuration
output {
elasticsearch {
# Prefer IPv6 loopback for Elasticsearch
hosts => ["http://[::1]:9200"]
index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
}
}
ELKEOF
cat > /etc/logstash/jvm.options.d/heap.options << 'ELKEOF'
# JVM heap settings for Logstash
-Xms1g
-Xmx1g
ELKEOF
msg_ok "Configuring Logstash"
msg_info " Configuring Kibana"
cat >> /etc/kibana/kibana.yml << 'ELKEOF'
# Kibana server configuration
server.port: 5601
# Prefer IPv6 and listen on all interfaces
server.host: "::"
# Elasticsearch connection
# Prefer IPv6 loopback
elasticsearch.hosts: ["http://[::1]:9200"]
ELKEOF
msg_ok "Configuring Kibana"
msg_info " Initializing Keystores"
/usr/share/kibana/bin/kibana-keystore create
chown kibana:kibana /etc/kibana/kibana.keystore
chmod 660 /etc/kibana/kibana.keystore
/usr/share/logstash/bin/logstash-keystore create
chown logstash:logstash /etc/logstash/logstash.keystore
chmod 660 /etc/logstash/logstash.keystore
msg_ok "Initializing Keystores"
msg_info " Enabling Services"
systemctl enable elasticsearch logstash kibana
msg_ok "Enabling Services"
msg_info "Creating Management Scripts"
cat > /root/elk-configure-security.sh << 'EOFSCRIPT'
# Reconfigure SSH server for new host keys
dpkg-reconfigure -f noninteractive openssh-server
# Prompt for SSL configuration
echo ""
echo "=== ELK Stack Security Configuration ==="
echo ""
read -p "Enable SSL for Elasticsearch backend? (y/N): " -n 1 -r ENABLE_BACKEND_SSL
echo ""
read -p "Enable SSL for Kibana frontend? (y/N): " -n 1 -r ENABLE_FRONTEND_SSL
echo ""
# Configure Elasticsearch
ES_CONFIG="/etc/elasticsearch/elasticsearch.yml"
KIBANA_CONFIG="/etc/kibana/kibana.yml"
# Always enable xpack security for password protection
sed -i 's/xpack.security.enabled: false/xpack.security.enabled: true/' "$ES_CONFIG"
# Configure backend SSL
if [[ $ENABLE_BACKEND_SSL =~ ^[Yy]$ ]]; then
echo "Configuring Elasticsearch SSL with auto-generated certificates..."
sed -i 's/xpack.security.http.ssl.enabled: false/xpack.security.http.ssl.enabled: true/' "$ES_CONFIG"
sed -i 's/xpack.security.transport.ssl.enabled: false/xpack.security.transport.ssl.enabled: true/' "$ES_CONFIG"
# Add SSL certificate configuration
cat >> "$ES_CONFIG" << 'EOF'
# Auto-generated SSL certificates
xpack.security.http.ssl.keystore.path: certs/http.p12
xpack.security.transport.ssl.keystore.path: certs/transport.p12
xpack.security.transport.ssl.truststore.path: certs/transport.p12
xpack.security.transport.ssl.verification_mode: certificate
EOF
ES_URL="https://localhost:9200"
else
echo "Elasticsearch SSL disabled"
ES_URL="http://localhost:9200"
fi
# Start Elasticsearch
systemctl start elasticsearch
echo "Waiting for Elasticsearch to start..."
sleep 30
# Generate SSL certificates if backend SSL enabled
if [[ $ENABLE_BACKEND_SSL =~ ^[Yy]$ ]]; then
echo "Generating SSL certificates..."
/usr/share/elasticsearch/bin/elasticsearch-certutil cert --silent --pem --out /tmp/certs.zip
unzip -q /tmp/certs.zip -d /tmp/certs
mkdir -p /etc/elasticsearch/certs
# Convert PEM to PKCS12 for Elasticsearch
openssl pkcs12 -export -in /tmp/certs/instance/instance.crt -inkey /tmp/certs/instance/instance.key -out /etc/elasticsearch/certs/http.p12 -name "http" -passout pass:
cp /etc/elasticsearch/certs/http.p12 /etc/elasticsearch/certs/transport.p12
chown -R elasticsearch:elasticsearch /etc/elasticsearch/certs
chmod 660 /etc/elasticsearch/certs/*.p12
# Restart Elasticsearch with SSL
systemctl restart elasticsearch
sleep 30
# Copy certs for Kibana and Logstash
mkdir -p /etc/kibana/certs /etc/logstash/certs
cp /tmp/certs/ca/ca.crt /etc/kibana/certs/
cp /tmp/certs/ca/ca.crt /etc/logstash/certs/
if [[ $ENABLE_FRONTEND_SSL =~ ^[Yy]$ ]]; then
cp /tmp/certs/instance/instance.crt /etc/kibana/certs/
cp /tmp/certs/instance/instance.key /etc/kibana/certs/
chmod 640 /etc/kibana/certs/*
else
chmod 640 /etc/kibana/certs/ca.crt
fi
chown -R kibana:kibana /etc/kibana/certs
chown -R logstash:logstash /etc/logstash/certs
chmod 640 /etc/logstash/certs/ca.crt
rm -rf /tmp/certs /tmp/certs.zip
fi
# Set elastic user password and store in keystore
ELASTIC_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 20)
echo "Setting elastic user password..."
if [[ $ENABLE_BACKEND_SSL =~ ^[Yy]$ ]]; then
curl -k -X POST "$ES_URL/_security/user/elastic/_password" -u "elastic:changeme" -H "Content-Type: application/json" -d "{\"password\":\"$ELASTIC_PASSWORD\"}" 2>/dev/null || \
/usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -b -s -i <<< "$ELASTIC_PASSWORD" > /dev/null
else
curl -X POST "$ES_URL/_security/user/elastic/_password" -u "elastic:changeme" -H "Content-Type: application/json" -d "{\"password\":\"$ELASTIC_PASSWORD\"}" 2>/dev/null || \
/usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -b -s -i <<< "$ELASTIC_PASSWORD" > /dev/null
fi
# Create Kibana system user for service account
echo "Creating Kibana system user..."
if [[ $ENABLE_BACKEND_SSL =~ ^[Yy]$ ]]; then
KIBANA_TOKEN=$(curl -k -X POST "$ES_URL/_security/service/elastic/kibana/credential/token/kibana_token" -u "elastic:$ELASTIC_PASSWORD" -H "Content-Type: application/json" 2>/dev/null | grep -o '"value":"[^"]*' | cut -d'"' -f4)
else
KIBANA_TOKEN=$(curl -X POST "$ES_URL/_security/service/elastic/kibana/credential/token/kibana_token" -u "elastic:$ELASTIC_PASSWORD" -H "Content-Type: application/json" 2>/dev/null | grep -o '"value":"[^"]*' | cut -d'"' -f4)
fi
# If service token creation fails, create API key for Kibana
if [ -z "$KIBANA_TOKEN" ]; then
echo "Creating Kibana API key..."
if [[ $ENABLE_BACKEND_SSL =~ ^[Yy]$ ]]; then
KIBANA_KEY_RESPONSE=$(curl -k -X POST "$ES_URL/_security/api_key" -u "elastic:$ELASTIC_PASSWORD" -H "Content-Type: application/json" -d '{"name":"kibana_api_key","role_descriptors":{"kibana_system":{"cluster":["monitor","manage_index_templates","manage_ingest_pipelines","manage_ilm"],"indices":[{"names":["*"],"privileges":["all"]}]}}}' 2>/dev/null)
KIBANA_API_KEY=$(echo "$KIBANA_KEY_RESPONSE" | grep -o '"encoded":"[^"]*' | cut -d'"' -f4)
else
KIBANA_KEY_RESPONSE=$(curl -X POST "$ES_URL/_security/api_key" -u "elastic:$ELASTIC_PASSWORD" -H "Content-Type: application/json" -d '{"name":"kibana_api_key","role_descriptors":{"kibana_system":{"cluster":["monitor","manage_index_templates","manage_ingest_pipelines","manage_ilm"],"indices":[{"names":["*"],"privileges":["all"]}]}}}' 2>/dev/null)
KIBANA_API_KEY=$(echo "$KIBANA_KEY_RESPONSE" | grep -o '"encoded":"[^"]*' | cut -d'"' -f4)
fi
fi
# Create Logstash writer API key
echo "Creating Logstash API key..."
if [[ $ENABLE_BACKEND_SSL =~ ^[Yy]$ ]]; then
LOGSTASH_KEY_RESPONSE=$(curl -k -X POST "$ES_URL/_security/api_key" -u "elastic:$ELASTIC_PASSWORD" -H "Content-Type: application/json" -d '{"name":"logstash_writer","role_descriptors":{"logstash_writer":{"cluster":["monitor","manage_index_templates","manage_ilm"],"indices":[{"names":["logs-*","logstash-*","ecs-*"],"privileges":["write","create","create_index","manage","manage_ilm"]}]}}}' 2>/dev/null)
LOGSTASH_API_KEY=$(echo "$LOGSTASH_KEY_RESPONSE" | grep -o '"encoded":"[^"]*' | cut -d'"' -f4)
else
LOGSTASH_KEY_RESPONSE=$(curl -X POST "$ES_URL/_security/api_key" -u "elastic:$ELASTIC_PASSWORD" -H "Content-Type: application/json" -d '{"name":"logstash_writer","role_descriptors":{"logstash_writer":{"cluster":["monitor","manage_index_templates","manage_ilm"],"indices":[{"names":["logs-*","logstash-*","ecs-*"],"privileges":["write","create","create_index","manage","manage_ilm"]}]}}}' 2>/dev/null)
LOGSTASH_API_KEY=$(echo "$LOGSTASH_KEY_RESPONSE" | grep -o '"encoded":"[^"]*' | cut -d'"' -f4)
fi
# Configure Kibana connection to Elasticsearch using keystore
sed -i "s|elasticsearch.hosts:.*|elasticsearch.hosts: [\"$ES_URL\"]|" "$KIBANA_CONFIG"
# Use service token or API key for Kibana
if [ -n "$KIBANA_TOKEN" ]; then
echo "Configuring Kibana with service token..."
echo "$KIBANA_TOKEN" | /usr/share/kibana/bin/kibana-keystore add elasticsearch.serviceAccountToken --stdin --force
else
echo "Configuring Kibana with API key..."
echo "$KIBANA_API_KEY" | /usr/share/kibana/bin/kibana-keystore add elasticsearch.apiKey --stdin --force
fi
chown kibana:kibana /etc/kibana/kibana.keystore
# Configure Logstash to use API key from keystore
echo "Configuring Logstash with API key..."
echo "$LOGSTASH_API_KEY" | /usr/share/logstash/bin/logstash-keystore add ELASTICSEARCH_API_KEY --stdin --force
chown logstash:logstash /etc/logstash/logstash.keystore
LOGSTASH_OUTPUT="/etc/logstash/conf.d/30-output.conf"
# Update Logstash output to use API key from keystore
cat > "$LOGSTASH_OUTPUT" << EOF
# Logstash output configuration
output {
elasticsearch {
hosts => ["$ES_URL"]
api_key => "\${ELASTICSEARCH_API_KEY}"
index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
EOF
# Add SSL CA if backend SSL enabled
if [[ $ENABLE_BACKEND_SSL =~ ^[Yy]$ ]]; then
cat >> "$LOGSTASH_OUTPUT" << 'EOF'
ssl => true
cacert => "/etc/logstash/certs/ca.crt"
EOF
fi
cat >> "$LOGSTASH_OUTPUT" << 'EOF'
}
}
EOF
# Clear API key variables from memory
unset KIBANA_TOKEN KIBANA_API_KEY LOGSTASH_API_KEY KIBANA_KEY_RESPONSE LOGSTASH_KEY_RESPONSE
# Configure frontend SSL if enabled
if [[ $ENABLE_FRONTEND_SSL =~ ^[Yy]$ ]]; then
echo "Configuring Kibana SSL..."
echo "server.ssl.enabled: true" >> "$KIBANA_CONFIG"
echo "server.ssl.certificate: /etc/kibana/certs/instance.crt" >> "$KIBANA_CONFIG"
echo "server.ssl.key: /etc/kibana/certs/instance.key" >> "$KIBANA_CONFIG"
if [[ $ENABLE_BACKEND_SSL =~ ^[Yy]$ ]]; then
echo "elasticsearch.ssl.certificateAuthorities: [\"/etc/kibana/certs/ca.crt\"]" >> "$KIBANA_CONFIG"
fi
KIBANA_PROTOCOL="https"
else
KIBANA_PROTOCOL="http"
fi
# Start Logstash and Kibana
systemctl start logstash kibana
# Wait for Kibana to start
sleep 15
# Display connection information
IP_ADDR=$(hostname -I | awk '{print $1}')
echo ""
echo "============================================"
echo "ELK Stack Deployment Complete"
echo "============================================"
echo ""
echo "Connection Info:"
echo " Elasticsearch: $ES_URL"
echo " Kibana: ${KIBANA_PROTOCOL}://${IP_ADDR}:5601"
echo ""
echo "Admin Credentials:"
echo " Username: elastic"
echo " Password: $ELASTIC_PASSWORD"
echo ""
echo "Security Configuration:"
echo " Backend SSL: $(if [[ $ENABLE_BACKEND_SSL =~ ^[Yy]$ ]]; then echo "Enabled"; else echo "Disabled"; fi)"
echo " Frontend SSL: $(if [[ $ENABLE_FRONTEND_SSL =~ ^[Yy]$ ]]; then echo "Enabled"; else echo "Disabled"; fi)"
echo " Kibana keystore: /etc/kibana/kibana.keystore"
echo " Logstash keystore: /etc/logstash/logstash.keystore"
echo ""
echo "IMPORTANT: Save this password now"
echo "It will not be displayed again or stored on disk"
echo ""
echo "Management:"
echo " Rotate API keys: /root/rotate-api-keys.sh"
echo " Reset elastic password: /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic"
echo "============================================"
echo ""
# Clear sensitive variables from memory
unset ELASTIC_PASSWORD
EOFSCRIPT
chmod +x /root/elk-configure-security.sh
msg_ok "Created Security Configuration Script"
msg_info "Creating API Key Rotation Script"
cat > /root/elk-rotate-api-keys.sh << 'EOFSCRIPT'
# Script to rotate API keys for Kibana and Logstash
# Run this script when you need to rotate credentials
if [[ $EUID -ne 0 ]]; then
echo "Run as root"
exit 1
fi
# Detect SSL configuration
if grep -q "xpack.security.http.ssl.enabled: true" /etc/elasticsearch/elasticsearch.yml; then
ES_URL="https://localhost:9200"
CURL_OPTS="-k"
else
ES_URL="http://localhost:9200"
CURL_OPTS=""
fi
# Prompt for elastic password
read -sp "Enter elastic user password: " ELASTIC_PASSWORD
echo ""
# Revoke old Logstash API key
echo "Revoking old Logstash API key..."
OLD_KEY_ID=$(curl $CURL_OPTS -s -X GET "$ES_URL/_security/api_key" -u "elastic:$ELASTIC_PASSWORD" -H "Content-Type: application/json" | grep -o '"id":"[^"]*","name":"logstash_writer"' | cut -d'"' -f4 | head -1)
if [ -n "$OLD_KEY_ID" ]; then
curl $CURL_OPTS -s -X DELETE "$ES_URL/_security/api_key" -u "elastic:$ELASTIC_PASSWORD" -H "Content-Type: application/json" -d "{\"ids\":[\"$OLD_KEY_ID\"]}" > /dev/null
echo "Old key revoked"
fi
# Create new Logstash API key
echo "Creating new Logstash API key..."
LOGSTASH_KEY_RESPONSE=$(curl $CURL_OPTS -s -X POST "$ES_URL/_security/api_key" -u "elastic:$ELASTIC_PASSWORD" -H "Content-Type: application/json" -d '{"name":"logstash_writer","role_descriptors":{"logstash_writer":{"cluster":["monitor","manage_index_templates","manage_ilm"],"indices":[{"names":["logs-*","logstash-*","ecs-*"],"privileges":["write","create","create_index","manage","manage_ilm"]}]}}}')
LOGSTASH_API_KEY=$(echo "$LOGSTASH_KEY_RESPONSE" | grep -o '"encoded":"[^"]*' | cut -d'"' -f4)
if [ -z "$LOGSTASH_API_KEY" ]; then
echo "Failed to create API key"
exit 1
fi
# Update Logstash keystore with new API key
echo "Updating Logstash keystore..."
echo "$LOGSTASH_API_KEY" | /usr/share/logstash/bin/logstash-keystore add ELASTICSEARCH_API_KEY --stdin --force
chown logstash:logstash /etc/logstash/logstash.keystore
# Clear sensitive variables from memory
unset ELASTIC_PASSWORD LOGSTASH_API_KEY LOGSTASH_KEY_RESPONSE OLD_KEY_ID
# Restart Logstash
echo "Restarting Logstash..."
systemctl restart logstash
echo ""
echo "API key rotation complete"
echo "New Logstash API key generated and stored in keystore"
EOFSCRIPT
chmod +x /root/elk-rotate-api-keys.sh
msg_ok "Created API Key Rotation Script"
msg_info "Starting ELK Services (without security)"
systemctl start elasticsearch
sleep 10
systemctl start logstash kibana
msg_ok "Started ELK Services"
msg_ok "Completed Successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Services are running without security (no authentication)${CL}"
echo -e "${INFO}${YW} To configure security (SSL, passwords, API keys):${CL}"
echo -e "${TAB}${GATEWAY}${BGN}/root/elk-configure-security.sh${CL}"
echo -e "${INFO}${YW} Access Kibana using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5601${CL}"
echo -e "${INFO}${YW} After security configuration, use the displayed credentials${CL}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment