TLS on both hops keeps log events, credentials, and API calls from crossing the network in clear text, which matters when Filebeat shippers, Logstash pipeline hosts, and Elasticsearch nodes are separated by VLANs, proxies, or other shared infrastructure.
In this path, Filebeat opens a Beats connection to Logstash, and Logstash sends bulk requests to Elasticsearch over the HTTP API. Each client validates the certificate chain presented by the server it connects to, so the Logstash listener needs its own server certificate and Elasticsearch needs an HTTPS certificate chain that the Logstash host trusts.
Current Logstash releases reject older TLS option names such as ssl and ssl_verify_mode, and package-based 9.x workflows should validate the pipeline as the logstash service account because superuser runs are blocked by default. The certificate names must match the host names that Filebeat and Logstash actually use, and any private CA in the chain must be copied to each client host that needs to trust it.
Hostname verification fails when Filebeat or Logstash connects to a name that is missing from the certificate subjectAltName extension. Use an IP SAN when the connection target is an IP address.
$ sudo install -d -o root -g logstash -m 750 /etc/logstash/certs $ sudo install -o root -g root -m 644 /tmp/http-ca.crt /etc/logstash/certs/http-ca.crt
Current self-managed Elasticsearch package installs usually create /etc/elasticsearch/certs/http_ca.crt on first startup when security auto-configuration enables HTTPS. If port 9200 is still plain HTTP, complete the Elasticsearch HTTP TLS workflow first and copy the resulting HTTP CA file here.
$ sudo install -o root -g root -m 644 /tmp/logstash.crt /etc/logstash/certs/logstash.crt
The certificate chain presented on port 5044 should include any intermediate CA certificates that Filebeat must trust.
$ sudo install -o root -g logstash -m 640 /tmp/logstash.pkcs8.key /etc/logstash/certs/logstash.pkcs8.key
Current beats input releases expect a PEM PKCS8 key. Convert an older PEM key when needed with openssl pkcs8 -inform PEM -in /tmp/logstash.key -topk8 -nocrypt -out /tmp/logstash.pkcs8.key.
Overly broad private-key permissions weaken the listener identity, while an unreadable key prevents the Logstash pipeline from starting.
input {
beats {
port => 5044
ssl_enabled => true
ssl_certificate => "/etc/logstash/certs/logstash.crt"
ssl_key => "/etc/logstash/certs/logstash.pkcs8.key"
}
}
Add ssl_certificate_authorities plus ssl_client_authentication ⇒ “required” only when the design also requires mutual TLS from the Filebeat side.
output {
elasticsearch {
hosts => ["https://elasticsearch.example.net:9200"]
ssl_enabled => true
ssl_certificate_authorities => ["/etc/logstash/certs/http-ca.crt"]
user => "logstash_internal"
password => "${LOGSTASH_INTERNAL_PASSWORD}"
ilm_enabled => false
index => "logs-%{+YYYY.MM.dd}"
}
}
Use a dedicated write user such as logstash_internal instead of the built-in elastic superuser, and keep the password in the Logstash keystore or another secret manager instead of in cleartext pipeline files.
ilm_enabled ⇒ false keeps the explicit daily logs-YYYY.MM.dd pattern in effect. Remove that line when the pipeline should write through ILM or to a data stream instead.
$ sudo -u logstash /usr/share/logstash/bin/logstash --path.settings /etc/logstash --path.data /tmp/logstash-configtest --config.test_and_exit Using bundled JDK: /usr/share/logstash/jdk Configuration OK [2026-04-07T08:03:34,546][INFO ][logstash.runner ] Using config.test_and_exit mode. Config Validation Result: OK. Exiting Logstash
The temporary --path.data directory must be writable by the logstash user and keeps the validation away from the service data directory in /var/lib/logstash.
Current 9.x packages default allow_superuser to false, so running this check as root fails unless that setting was changed explicitly.
$ sudo systemctl restart logstash.service
Restarting Logstash pauses every active pipeline while inputs reopen, filters recompile, and outputs reconnect.
$ sudo journalctl --unit logstash --since "5 minutes ago" --no-pager
Apr 07 14:24:11 host logstash[21457]: [2026-04-07T14:24:11,351][INFO ][logstash.outputs.elasticsearch][main] Elasticsearch pool URLs updated {:changes=>{:removed=>[], :added=>[https://elasticsearch.example.net:9200/]}}
Apr 07 14:24:11 host logstash[21457]: [2026-04-07T14:24:11,884][INFO ][logstash.javapipeline ][main] Pipeline started {"pipeline.id"=>"main"}
##### snipped #####
401, 403, or index privilege errors in this stage mean the TLS path to Elasticsearch is up and the remaining problem is credentials, privileges, or index management. Certificate trust, hostname, or protocol failures are logged separately as TLS errors.
$ sudo install -d -m 750 /etc/filebeat/certs $ sudo install -m 644 /tmp/logstash-ca.crt /etc/filebeat/certs/logstash-ca.crt
If the same internal CA signed both the Logstash listener certificate and any optional Filebeat client certificate, one CA file can cover both trust checks.
output.logstash: hosts: ["logstash.example.net:5044"] ssl.certificate_authorities: ["/etc/filebeat/certs/logstash-ca.crt"] # ssl.certificate: "/etc/filebeat/certs/filebeat.crt" # ssl.key: "/etc/filebeat/certs/filebeat.key"
Keep only one output.* block enabled in /etc/filebeat/filebeat.yml, or Filebeat fails to start with conflicting publisher settings.
Uncomment ssl.certificate and ssl.key only when the Logstash beats input is configured for mutual TLS with ssl_client_authentication.
$ sudo filebeat test config -c /etc/filebeat/filebeat.yml Config OK
Related: How to test a Filebeat configuration
$ sudo filebeat test output -c /etc/filebeat/filebeat.yml
logstash: logstash.example.net:5044...
connection...
parse host... OK
dns lookup... OK
addresses: 192.0.2.25
dial up... OK
TLS...
security: server's certificate chain verification is enabled
handshake... OK
TLS version: TLSv1.3
dial up... OK
talk to server... OK
This one-shot check proves the saved host, port, CA file, and certificate name all align before a live event is shipped.
Unknown CA errors usually mention certificate signed by unknown authority, while host name mismatches usually mention the requested host not appearing in the certificate.
$ sudo systemctl restart filebeat.service
$ sudo journalctl --unit=filebeat --since "5 min ago" --no-pager --lines=20
Apr 08 02:14:56 web-01 filebeat[2147]: {"log.level":"info","@timestamp":"2026-04-08T02:14:56.197Z","log.logger":"publisher_pipeline_output","message":"Connecting to backoff(async(tcp://logstash.example.net:5044))","service.name":"filebeat","ecs.version":"1.6.0"}
Apr 08 02:14:56 web-01 filebeat[2147]: {"log.level":"info","@timestamp":"2026-04-08T02:14:56.316Z","log.logger":"publisher_pipeline_output","message":"Connection to backoff(async(tcp://logstash.example.net:5044)) established","service.name":"filebeat","ecs.version":"1.6.0"}
The journal confirms that the running service, not only the one-shot test command, reconnected to the configured Logstash output after the restart.
If this line never appears, review the Logstash journal at the same time for listener, certificate, or pipeline startup failures.
$ curl --silent --show-error --output /dev/null --write-out "%{http_code}\n" --cacert /etc/logstash/certs/http-ca.crt https://elasticsearch.example.net:9200/
401
A validated 401 response proves that the TLS handshake succeeded and that the endpoint is enforcing authentication instead of serving plaintext HTTP.