Do you know if your Filebeat client is connecting to a rogue Logstash server? Do you know if your Logstash server is accepting random logs from random devices? If you have answered “I don’t know” to either of these questions then this blog post is for you. The purpose of this blog post is to provide instructions on how to setup Logstash and Filebeat with mutual TLS (mTLS). The step-by-step instructions in this post, will demonstrate how to create the certificate chain of trust using Vault. Lastly, I will cover the Python script I created to automate constructing this logging certificate chain of trust.
Goals
- Generate an intermediate certificate for Logstash
- Generate leaf certificates for logging clients
- Setup Logstash to use the intermediate certificate to authenticate clients
- Setup Filebeat to use client/leaf certificate to authenticate itself to Logstash
- Use mTLS for communication between Logstash and Filebeat
DISCLAIMER
This blog post is a proof of concept (POC) for a homelab and does NOT implement best practices for an enterprise environment. Please review the Hashicorp Vault documentation for best practices.
DISCLAIMER
Background
What is Mutual TLS?
As stated by Cloudflare, “Mutual TLS (mTLS) authentication ensures that traffic is both secure and trusted in both directions between a client and server.” For a more thorough deep dive on mutual TLS please visit this blog post. However, I think for the purpose of this blog post the graphic below provides a good overview of the mutual TLS communication and why it is a powerful mechanism in information security.
What is Hashicorp Vault?
Vault is a tool for securely accessing secrets. A secret is anything that you want to tightly control access to, such as API keys, passwords, or certificates. Vault provides a unified interface to any secret, while providing tight access control and recording a detailed audit log. For this blog post, Vault will act as our certificate authority for our organization.
Assumptions
- The means to generate DNS A records for each service.
- Pre-existing Vault infrastructure
- If not please see my blog post here: Getting started with Hashicorp Vault v1.6.1
- Pre-existing Logstash infrastructure
- If not please see my blog post here: IR Tales: The Quest for the Holy SIEM: Elastic stack + Sysmon + Osquery
Network diagram
Certificate chain of trust diagram
The diagram below illustrates the certificate chain of trust that we are going to implement with Vault. For this blog post, I decided to do as much segmentation as possible when creating this certificate chain. As you can see from the diagram below, clients and servers have their own intermediate certificates. This means for servers, in our case Logstash, a certificate will need to be generated from the server intermediate certificate. For clients, a certificate will need to be generated from the clients intermediate certificate.
This segmentation enforces the flow of data from clients to servers. On the Filebeat endpoint we specify the client certificate, client private key, and the server intermediate certificate as a trusted authority. On the Logstash endpoint, we specify the server certificate, server private key, and the client intermediate certificate as a trusted authority. By doing this we are enforcing that only Filebeat client leaf certificates can send data to Logstash servers and Logstash server leaf certificates can ingest data from Filebeat clients.
Generate logging certificate chain using Vault CLI
Step 1: Generate logging intermediate certificate
git clone https://github.com/CptOfEvilMinions/BlogProjects.git
cd BlogProjects/logstash-mutual-tls
- Log into Vault
export LOGGING_DOMAIN=<domain>
- Define the domain you want to generate certificate for
vault secrets enable -path=logging_pki_int pki
- Enable the pki secrets engine at the
logging_pki_int/
path
- Enable the pki secrets engine at the
vault secrets tune -max-lease-ttl=61320h logging_pki_int
- Tune the
logging_pki_int/
secrets engine to issue certificates with a maximum time-to-live (TTL) of 61320 hours - 61320 hours = 7 years
- Tune the
vault write -format=json logging_pki_int/intermediate/generate/internal common_name="logging.${LOGGING_DOMAIN} Intermediate Authority" | jq -r '.data.csr' > conf/tls/loggint_int/logging_int.csr
- Generate an intermediate certificate and save the certificate signing request (CSR) to disk
vault write -format=json pki/root/sign-intermediate csr=@conf/tls/loggint_int/logging_int.csr format=pem_bundle ttl="61320h" | jq -r '.data.certificate' > conf/tls/loggint_int/logging_int.crt
- Sign the intermediate certificate with the root certificate and write the generated certificate to disk
openssl x509 -in conf/tls/loggint_int/logging_int.crt -text -noout | grep 'Subject:'
vault write logging_pki_int/intermediate/set-signed certificate=@conf/tls/loggint_int/logging_int.crt
- Import the signed certificate by the ROOT CA into Vault
vault write logging_pki_int/roles/logging-servers allowed_domains=servers.logging.${LOGGING_DOMAIN},clients.logging.${LOGGING_DOMAIN} allow_subdomains=false max_ttl=43800h
- Specify that the only domains allowed to be used are
clients.logging.${LOGGING_DOMAIN}
andservers.logging.${LOGGING_DOMAIN}
- Create a role that grants the ability for the server and client intermediate certificate to be generated
- 43800 hours = 5 years
- Specify that the only domains allowed to be used are
rm conf/tls/loggint_int/logging_int.csr
Step 2: Generate server logging intermediate certificate
vault secrets enable -path=servers_logging_pki_int pki
vault secrets tune -max-lease-ttl=43800h servers_logging_pki_int
- Tune the
servers_logging_pki_int/
secrets engine to issue certificates with a maximum time-to-live (TTL) of 43800 hours
43800 hours = 5 years
- Tune the
vault write -format=json servers_logging_pki_int/intermediate/generate/internal common_name="servers.logging.${LOGGING_DOMAIN} Intermediate Authority" | jq -r '.data.csr' > conf/tls/server_logging_int/server_logging_int.csr
- Generate an intermediate certificate and save the certificate signing request (CSR) to disk
vault write -format=json logging_pki_int/root/sign-intermediate csr=@conf/tls/server_logging_int/server_logging_int.csr format=pem_bundle ttl="43800h" | jq -r '.data.certificate' > conf/tls/server_logging_int/server_logging_int.crt
- Sign the intermediate certificate with the logging intermediate certificate and write the generated certificate to disk
openssl x509 -in conf/tls/server_logging_int/server_logging_int.crt -text -noout | grep 'Subject:'
vault write servers_logging_pki_int/intermediate/set-signed certificate=@conf/tls/server_logging_int/server_logging_int.crt
- Import the signed certificate by the logging intermediate into Vault
vault write servers_logging_pki_int/roles/logging-servers allowed_domains=servers.logging.${LOGGING_DOMAIN} allow_subdomains=true max_ttl=26280h
- Specify that the only domains allowed to be used by servers.*
- Create a role that grants the ability for server leaf certificates to be generated
- 26280h hours = 3 years
Step 3: Generate client logging intermediate certificate
vault secrets enable -path=clients_logging_pki_int pki
vault secrets tune -max-lease-ttl=43800h clients_logging_pki_int
- Tune the
clients_logging_pki_int/
secrets engine to issue certificates with a maximum time-to-live (TTL) of 43800 hours
43800 hours = 5 years
- Tune the
vault write -format=json clients_logging_pki_int/intermediate/generate/internal common_name="clients.logging.${LOGGING_DOMAIN} Intermediate Authority" | jq -r '.data.csr' > conf/tls/client_logging_int/clients_logging_int.csr
- Generate an intermediate certificate and save the certificate signing request (CSR) to disk
vault write -format=json logging_pki_int/root/sign-intermediate csr=@conf/tls/client_logging_int/clients_logging_int.csr format=pem_bundle ttl="43800h" | jq -r '.data.certificate' > conf/tls/client_logging_int/clients_logging_int.crt
- Sign the intermediate certificate with the logging intermediate certificate and write the generated certificate to disk
openssl x509 -in conf/tls/client_logging_int/clients_logging_int.crt -text -noout | grep 'Subject:'
vault write clients_logging_pki_int/intermediate/set-signed certificate=@conf/tls/client_logging_int/clients_logging_int.crt
- Import the signed certificate by the logging intermediate into Vault
vault write clients_logging_pki_int/roles/logging-clients allowed_domains=clients.logging.${LOGGING_DOMAIN} allow_subdomains=true max_ttl=26280h
- Specify that the only domains allowed to be used by clients.*
- Create a role that grants the ability for client leaf certificates to be generated
- 26280h hours = 3 years
Step 4: Generate Logstash leaf certificate
vault write -format=json servers_logging_pki_int/issue/logging-servers common_name="<logstash server hostname>.servers.logging.${LOGGING_DOMAIN}" ttl="26280h" private_key_format=pkcs8 > conf/tls/<logstash server hostname>_servers_logging_${LOGGING_DOMAIN}.json
- Request a new leaf certificate for the common_name specified above
- 26280 hours = 3 years
- Force the private key format to be PKCS8, which is the only format accepted by Logstash v7.0+
cat conf/tls/<logstash server hostname>_servers_logging_${LOGGING_DOMAIN}.json | jq -r '.data.private_key' > conf/tls/<logstash server hostname>_servers_logging_${LOGGING_DOMAIN}.key
- Extract private key from JSON blob
cat conf/tls/<logstash server hostname>_servers_logging_${LOGGING_DOMAIN}.json | jq -r '.data.certificate' > conf/tls/<logstash server hostname>_servers_logging_${LOGGING_DOMAIN}.crt
cat conf/tls/<logstash server hostname>_servers_logging_${LOGGING_DOMAIN}.json | jq -r '.data.ca_chain[]' >> conf/tls/<logstash server hostname>_servers_logging_${LOGGING_DOMAIN}.crt
- Extract public certificate chain from JSON blob
rm <logging client hostname>_servers_logging_<domain>.json
Step 5: Verify Logstash leaf certificate chain
curl -s -k https://vault.<domain>/v1/pki/ca/pem > /tmp/root_ca.crt
- Download the Vault root CA
openssl verify -verbose -CAfile /tmp/root_ca.crt conf/tls/logging_int.crt
- Verify logging intermediate with root CA
openssl verify -verbose -CAfile /tmp/root_ca.crt -untrusted conf/tls/loggint_int/logging_int.crt conf/tls/server_logging_int/server_logging_int.crt
- Verify the server logging intermediate, the logging intermediate, and the root CA
openssl verify -verbose -CAfile /tmp/root_ca.crt -untrusted <(cat conf/tls/loggint_int/logging_int.crt conf/tls/server_logging_int/server_logging_int.crt) conf/tls/<logstash server hostname>_servers_logging_${LOGGING_DOMAIN}.crt
- Verify the Logstash server leaf certificate, the server logging intermediate, the logging intermediate, and the root CA
Step 6: Generate logging client leaf certificate
vault write -format=json clients_logging_pki_int/issue/logging-clients common_name="<filebeat client hostname>.clients.logging.${LOGGING_DOMAIN}" ttl="26280h" private_key_format=pkcs8 > conf/tls/<filebeat client hostname>_clients_logging_${LOGGING_DOMAIN}.json
- Request a new leaf certificate for the common_name specified above
- 26280 hours = 3 years
- Force the private key format to be PKCS8, which is the only format accepted by Filebeat v7.0+
cat conf/tls/<filebeat client hostname>_clients_logging_${LOGGING_DOMAIN}.json | jq -r '.data.private_key' > conf/tls/<filebeat client hostname>_clients_logging_${LOGGING_DOMAIN}.key
- Extract private key from JSON blob
cat conf/tls/<filebeat client hostname>_clients_logging_${LOGGING_DOMAIN}.json | jq -r '.data.certificate' > conf/tls/<filebeat client hostname>_clients_logging_${LOGGING_DOMAIN}.crt
cat conf/tls/<filebeat client hostname>_clients_logging_${LOGGING_DOMAIN}.json | jq -r '.data.ca_chain[]' >> conf/tls/<filebeat client hostname>_clients_logging_${LOGGING_DOMAIN}.crt
- Extract public certificate chain from JSON blob
rm conf/tls/<filebeat client hostname>_clients_logging_${LOGGING_DOMAIN}.json
Step 7: Verify Filebeat leaf certificate chain
openssl verify -verbose -CAfile /tmp/root_ca.crt conf/tls/loggint_int/logging_int.crt
- Verify logging intermediate with root CA
openssl verify -verbose -CAfile /tmp/root_ca.crt -untrusted conf/tls/loggint_int/logging_int.crt conf/tls/client_logging_int/clients_logging_int.crt
- Verify the clients logging intermediate, the logging intermediate, and the root CA
openssl verify -verbose -CAfile /tmp/root_ca.crt -untrusted <( cat conf/tls/loggint_int/logging_int.crt conf/tls/client_logging_int/clients_logging_int.crt) conf/tls/<filebeat client hostname>_clients_logging_hackinglab.local.crt
- Verify the Logstash clients leaf certificate, the clients logging intermediate, the logging intermediate, and the root CA
Automate setting up Vault logging PKI
vault login
- Log into Vault
virtualenv -p python3 venv
source venv/bin/activate
pip3 install -r requirements.txt
python3 setup_logging_pki_int.py --setup_logging_pki --vault_addr https://vault.<domain> --logging_domain <logging domain>
- Setup logging PKI
python3 setup_logging_pki_int.py --create_leaf_cert --vault_addr https://vault.<domain> --logging_domain <logging domain> --server_leaf_cert_hostname <logstash server hostname>
python3 setup_logging_pki_int.py --create_leaf_cert --vault_addr https://vault.<domain> --logging_domain <logging domain> --client_leaf_cert_hostname <logstash server hostname>
Install/Setup Logstash v7.11 on Ubuntu 20.04
Install Logstash
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
sudo apt-get install apt-transport-https -y
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list
sudo apt-get update -y && sudo apt-get install logstash -y
Setup Logstash
- COPY
conf/tls/client_logging_int/client_logging_pki_int.crt
to/etc/ssl/certs/<domain>_client_logging_int.crt
- COPY
conf/tls/fielbeat01_clients_logging_<domain>.crt
to/etc/ssl/certs/conf/tls/fielbeat01_clients_logging_<domain>.crt
- COPY
conf/tls/fielbeat01_clients_logging_hackinglab.local.key
to/etc/ssl/private/fielbeat01_clients_logging_<domain>.key
chmod 644 /etc/ssl/certs/<domain>_client_logging_int.crt /etc/ssl/certs/conf/tls/fielbeat01_clients_logging_<domain>.crt
- Set the proper permissions for the certificates
chmod 600 /etc/ssl/private/fielbeat01_clients_logging_<domain>.key
- Set the proper permissions for the Logstash private key
curl https://raw.githubusercontent.com/CptOfEvilMinions/BlogProjects/master/logstash-mutual-tls/conf/logstash/02-input-beats.conf --output /etc/logstash/pipeline/02-input-beats.conf
- Download Beats input config
sed -i 's#{{ logstash_server_logging_crt_file_path }}#/etc/ssl/certs/fielbeat01_clients_logging_<domain>.crt#g' /etc/logstash/pipeline/02-input-beats.conf
- Set the file path for the Logstash server certificate
sed -i 's#{{ logstash_server_logging_key_file_path }}#/etc/ssl/private/fielbeat01_clients_logging_<domain>.key#g' /etc/logstash/pipeline/02-input-beats.conf
- Set the file path for the Logstash server private key
sed -i 's#{{ client_logging_crt_file_path }}#/etc/ssl/certs/<domain>_client_logging_int.crt#g' /etc/logstash/pipeline/02-input-beats.conf
- Set the file path for the client intermediate certificate
systemctl restart logstash
Install/Setup Filebeat v7.11 on Ubuntu 20.04
Install Filebeat
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
sudo apt-get install apt-transport-https -y
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list
sudo apt-get update -y && sudo apt-get install filebeat -y
Setup Filebeat
cp /etc/filebeat/filebeat.yml /etc/filebeat/filebeat.yml.bak
- Backup config
curl https://raw.githubusercontent.com/CptOfEvilMinions/BlogProjects/master/logstash-mutual-tls/conf/filebeat/filebeat.yml --output /etc/filebeat/filebeat.yml
- Download config
- COPY
conf/tls/server_logging_int/server_logging_int.crt
to/etc/ssl/certs/<domain>_server_logging_int.crt
- COPY
conf/tls/fielbeat01_clients_logging_<domain>.crt
to/etc/ssl/certs/conf/tls/fielbeat01_clients_logging_<domain>.crt
- COPY
conf/tls/fielbeat01_clients_logging_hackinglab.local.key
to/etc/ssl/private/fielbeat01_clients_logging_<domain>.key
chmod 644 /etc/ssl/certs/<domain>_server_logging_int.crt /etc/ssl/certs/conf/tls/fielbeat01_clients_logging_<domain>.crt
- Set the proper permissions for the certificates
chmod 600 /etc/ssl/private/fielbeat01_clients_logging_<domain>.key
- Set the proper permissions for the Filebeat private key
sed -i 's#{{ logstash_addr }}#<Logstash IP addr or FQDN> #g' /etc/filebeat/filebeat.yml
- Set the Logstash IP server IP address or FQDN
sed -i 's#{{ logstash_port }}#<Logstash Beats port>#g' /etc/filebeat/filebeat.yml
- Set the Logstash Beats port
sed -i 's#{{ server_logging_crt_file_path }}#/etc/ssl/certs/<domain>_server_logging_int.crt#g' /etc/filebeat/filebeat.yml
sed -i 's#{{ filebeat_client_logging_crt_file_path }}#/etc/ssl/certs/conf/tls/fielbeat01_clients_logging_<domain>.crt#g' /etc/filebeat/filebeat.yml
sed -i 's#{{ filebeat_client_logging_key_file_path }}#/etc/ssl/private/fielbeat01_clients_logging_<domain>.key#g' /etc/filebeat/filebeat.yml
Test Filebeat -> Logstash connection
filebeat test output
systemctl restart filebeat
Lessons learned
I am currently reading a book called “Cracking the Coding Interview” and it is a great book. One interesting part of the book is their matrix to describe projects you worked on and the matrix contains the following sections which are: challenges, mistakes/failures, enjoyed, leadership, conflicts, and what would you do differently. I am going to try and use this model at the end of my blog posts to summarize and reflect on the things I learn. I don’t blog to post things that I know, I blog to learn new things and to share the knowledge of my security research.
New skills/knowledge
- Learned how to generate intermediate and leaf certificates for a service
- How to connect to Vault using client certificate
- How to implement mTLS with Logstash and Filebeat
- Learned how to implement Python requests sessions
- Learned how to use a custom HTTP method with python requests
- Learned how to use Python requests with the Vault API
Challenges
- One of the libraries Logstash depends on updated and it only accepts base64-encoded PKCS8 private keys now. By default like most applications, Vault produces the traditional format known as PKCS1.
What You’d Do Differently
- Set up a Vault agent to auto-request and re-new a client certificate from Vault.
References
- Installing Logstash
- Ubuntu security – certificates
- INSTALL/SETUP VAULT FOR PKI + NGINX + DOCKER – BECOMING YOUR OWN CA
- Securing Kafka using Vault PKI
- Beats input plugin
- FileBeat configuration test with output
- Vault PKI Secrets Engine with Intermediate Signing Authority
- StackOverFlow – Verify a certificate chain using openssl verify