Secure Elastic Stack: End-to-End TLS, RBAC, and Password‑Free Beats Setup

Learn how to build a three‑node Elasticsearch cluster with full TLS encryption, configure Kibana’s HTTPS settings, create minimal‑privilege Beats‑writer roles, and eliminate clear‑text passwords using Elastic Stack’s keystore, all within a reproducible Vagrant environment for secure, scalable monitoring.

DevOps Coach
DevOps Coach
DevOps Coach
Secure Elastic Stack: End-to-End TLS, RBAC, and Password‑Free Beats Setup

Prerequisites

The tutorial assumes the following software versions: Elastic Stack 7.8.0, macOS 10.15.5, Vagrant 2.2.9, VirtualBox 6.0, and CentOS 8.0. All configuration and test steps are based on the supplied Vagrantfile, which can be adapted to any comparable environment.

# -*- mode: ruby -*-
# vi: set ft=ruby :

# Every Vagrant development environment requires a box. You can search for
# boxes at https://atlas.hashicorp.com/search.
BOX_IMAGE = "bento/centos-8"
ES_COUNT = 3
NODE_COUNT = 4

Vagrant.configure("2") do |config|

  # Set all guests to use the same static DNS resolution /etc/hosts
  config.vm.provision :hosts, :sync_hosts => true
  # Use Vagrant default key pair for SSH login
  config.ssh.insert_key = false
  
  # Deploy Elasticsearch servers
  (1..ES_COUNT).each do |i|
    config.vm.define "es#{i}" do |es_config|
      es_config.vm.box = BOX_IMAGE
      es_config.vm.hostname = "es#{i}.zenlab.local"
      es_config.vm.network :private_network, ip: "192.168.50.#{i + 10}"
      es_config.vm.provision :hosts, :sync_hosts => true
      es_config.vm.provider :virtualbox do |vb|
        vb.memory = 2048
        vb.cpus = 1
      end
      es_config.vm.provision :shell, path: 'pre-install-ES.sh'
    end
  end
  
  # Deploy Kibana, Logstash, APM Server, Heartbeat and Packetbeat
  config.vm.define "lk" do |lk_config|
    lk_config.vm.box = BOX_IMAGE
    lk_config.vm.hostname = "lk.zenlab.local"
    lk_config.vm.network :private_network, ip: "192.168.50.20"
    lk_config.vm.network 'forwarded_port', guest: 5601, host: 5601
    lk_config.vm.provision :hosts, :sync_hosts => true
    lk_config.vm.provider :virtualbox do |vb|
      vb.memory = 1024
      vb.cpus = 1
    end
  end
  
  # Two managed nodes for Beats agents
  (1..NODE_COUNT).each do |i|
    config.vm.define "node#{i}" do |node_config|
      node_config.vm.box = BOX_IMAGE
      node_config.vm.hostname = "node#{i}.zenlab.local"
      node_config.vm.network :private_network, ip: "192.168.50.#{i + 20}"
      node_config.vm.provider :virtualbox do |vb|
        vb.memory = 1024
        vb.cpus = 1
      end
      node_config.vm.provision :shell, path: 'pre-install-beats.sh'
    end
  end

  config.vm.provision "shell", inline: <<-SHELL
    sh -c "echo 'Welcome to Elastic Stack!'"
  SHELL
end

Three‑node Elasticsearch cluster

Start the three Elasticsearch VMs with: vagrant up es1 es2 es3 Generate a certificate bundle for inter‑node TLS using elasticsearch‑certutil and the instance.yml seed file:

# instance.yml
instances:
  - name: 'es1'
    ip: ['192.168.50.11']
    dns: [ 'es1.zenlab.local' ]
  - name: "es2"
    ip: ['192.168.50.12']
    dns: [ 'es2.zenlab.local' ]
  - name: 'es3'
    ip: ['192.168.50.13']
    dns: [ 'es3.zenlab.local' ]
  - name: 'lk'
    ip: ['192.168.50.20']
    dns: [ 'lk.zenlab.local' ]
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil cert --ca --pem --in /vagrant/certs/instance.yml --out /vagrant/certs/certs.zip

Unzip the bundle and copy the CA certificate and each node’s certificate/key to /etc/elasticsearch/certs on the corresponding host.

sudo mkdir /etc/elasticsearch/certs
sudo cp /vagrant/certs/ca/ca.crt /etc/elasticsearch/certs
sudo cp /vagrant/certs/es1/* /etc/elasticsearch/certs   # repeat for es2 and es3

The certs directory now contains:

ca.crt – the root CA certificate

esX.crt – the node’s public certificate

esX.key – the node’s private key

Place these files in each Elasticsearch node’s configuration directory and enable TLS in elasticsearch.yml. Example for the first node:

# elasticsearch.yml
cluster.name: elk4devops
node.name: es1
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
network.host: es1.zenlab.local
cluster.initial_master_nodes: ["es1"]
discovery.seed_hosts: [ "es1.zenlab.local" ]

xpack.security.enabled: true
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.key: certs/es1.key
xpack.security.http.ssl.certificate: certs/es1.crt
xpack.security.http.ssl.certificate_authorities: certs/ca.crt

xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.key: certs/es1.key
xpack.security.transport.ssl.certificate: certs/es1.crt
xpack.security.transport.ssl.certificate_authorities: certs/ca.crt

xpack.monitoring.collection.enabled: true
action.auto_create_index: ".app-search-*-logs-*,-.app-search-*,+*"

Copy the file to /etc/elasticsearch/elasticsearch.yml, reload the systemd daemon and start the service:

sudo cp /vagrant/elasticsearch.yml /etc/elasticsearch/elasticsearch.yml
sudo systemctl daemon-reload
sudo systemctl start elasticsearch

Generate built‑in user passwords (the command prints random strong passwords):

sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto -u "https://es1.zenlab.local:9200" -b

Store the generated passwords securely. Verify HTTPS access with the CA certificate:

curl --cacert /vagrant/certs/ca/ca.crt -u elastic 'https://es1.zenlab.local:9200/_cat/nodes?v'

Adding the second and third nodes

Repeat the certificate copy and configuration steps for es2 and es3, using the respective elasticsearch2.yml and elasticsearch3.yml files (shown below). After copying each file to /etc/elasticsearch/elasticsearch.yml, reload and start the service, then verify that the node joins the cluster with the same curl command.

# elasticsearch2.yml (excerpt)
cluster.name: elk4devops
node.name: es2
network.host: es2.zenlab.local
cluster.initial_master_nodes: ["es1"]
discovery.seed_hosts: [ "es1.zenlab.local" ]

xpack.security.enabled: true
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.key: certs/es2.key
xpack.security.http.ssl.certificate: certs/es2.crt
xpack.security.http.ssl.certificate_authorities: certs/ca.crt

xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.key: certs/es2.key
xpack.security.transport.ssl.certificate: certs/es2.crt
xpack.security.transport.ssl.certificate_authorities: certs/ca.crt
# elasticsearch3.yml (excerpt)
cluster.name: elk4devops
node.name: es3
network.host: es3.zenlab.local
cluster.initial_master_nodes: ["es1"]
discovery.seed_hosts: [ "es1.zenlab.local" ]

xpack.security.enabled: true
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.key: certs/es3.key
xpack.security.http.ssl.certificate: certs/es3.crt
xpack.security.http.ssl.certificate_authorities: certs/ca.crt

xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.key: certs/es3.key
xpack.security.transport.ssl.certificate: certs/es3.crt
xpack.security.transport.ssl.certificate_authorities: certs/ca.crt

Kibana HTTPS configuration

Copy the CA certificate and the Kibana node’s certificate/key to /etc/kibana/certs:

sudo mkdir /etc/kibana/certs
sudo cp /vagrant/certs/ca/ca.crt /etc/kibana/certs
sudo cp /vagrant/certs/lk/* /etc/kibana/certs

Replace the default kibana.yml with the prepared version and start the service:

sudo cp /vagrant/kibna.yml /etc/kibana/kibana.yml
sudo systemctl start kibana

Monitor the log file until Kibana reports a successful start, then open https://lk.zenlab.local:5601 in a browser and log in with the elastic user.

Beats role and user for write‑only access

Create a role that grants write access to the filebeat and metricbeat indices, then create a user beats-writer and assign the role. (Screenshots omitted for brevity.)

Initial Beats node

Bring up a Beats node with: vagrant up node1 The provisioning script pre-install-beats.sh installs Filebeat, Metricbeat and Auditbeat RPMs and enables the services.

#!/bin/bash
elastic_version='7.8.0'
sudo rpm -ivh /vagrant/rpm/filebeat-$elastic_version-x86_64.rpm
sudo systemctl enable filebeat.service
sudo rpm -ivh /vagrant/rpm/metricbeat-$elastic_version-x86_64.rpm
sudo systemctl enable metricbeat.service
sudo rpm -ivh /vagrant/rpm/auditbeat-$elastic_version-x86_64.rpm
sudo systemctl enable auditbeat.service

Deploy the CA certificate to the system trust store:

sudo update-ca-trust enable
sudo cp /vagrant/certs/ca/ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract

Copy the prepared Beat configuration files and enable the system module for Filebeat:

sudo cp -f /vagrant/filebeat.yml /etc/filebeat/filebeat.yml
sudo cp -f /vagrant/metricbeat.yml /etc/metricbeat/metricbeat.yml
sudo filebeat modules enable system

Run the setup commands once to create the required indices, pipelines and Kibana objects:

filebeat setup
metricbeat setup

Start the Beats services and verify data flow with:

filebeat -e
metricbeat -e

Deploy Beats to additional nodes

The add-agent.sh script automates installation, CA deployment, configuration replacement, keystore population and service start‑up for new Beats nodes.

#!/bin/bash
elastic_version='7.8.0'
b_user='beats-writer'
b_pwd='DevOps1234'

sudo rpm -ivh /vagrant/rpm/filebeat-$elastic_version-x86_64.rpm
sudo systemctl enable filebeat.service
sudo filebeat modules enable system
sudo rpm -ivh /vagrant/rpm/metricbeat-$elastic_version-x86_64.rpm
sudo systemctl enable metricbeat.service

sudo update-ca-trust enable
sudo cp /vagrant/certs/ca/ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract

sudo cp -f /vagrant/filebeat-v1.yml /etc/filebeat/filebeat.yml
sudo cp -f /vagrant/metricbeat-v1.yml /etc/metricbeat/metricbeat.yml

echo $b_user | sudo filebeat keystore add BEATS_WRITER_USERNAME --stdin --force
echo $b_pwd  | sudo filebeat keystore add BEATS_WRITER_PW --stdin --force
echo $b_user | sudo metricbeat keystore add BEATS_WRITER_USERNAME --stdin --force
echo $b_pwd  | sudo metricbeat keystore add BEATS_WRITER_PW --stdin --force

sudo systemctl start metricbeat.service
sudo systemctl start filebeat.service

The accompanying filebeat-v1.yml and metricbeat.yml files configure Elasticsearch output to the three cluster nodes, use the keystore variables for credentials, enable host and geo metadata, and apply best‑practice settings such as logging.level: error.

Summary

The tutorial completes the following secure configurations:

TLS‑encrypted inter‑node communication for a three‑node Elasticsearch cluster and HTTPS for Kibana.

Beats agents with TLS‑encrypted transport and role‑based access control (RBAC) using a write‑only beats-writer role.

Removal of clear‑text passwords from configuration files by storing credentials in the Beats keystore.

Open items include monitoring the health of the "Bests" node, tuning index lifecycle management, and automating hot‑warm data migration.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

TLSRBACElastic StackBeatsVagrant
DevOps Coach
Written by

DevOps Coach

Master DevOps precisely and progressively.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.