Can't connect to Elasticsearch with Node.Js on Kubernetes (self signed certificate in certificate chain)
The solution is to configure SSL and the Elastic user when creating the Client
const client = new elasticsearch.Client({ node: process.env.elasticsearch_node, auth: { username: "elastic", password: process.env.elasticsearch_password || "changeme", }, ssl: { ca: process.env.elasticsearch_certificate, rejectUnauthorized: false, },});
The password and the certificate are provided by Elastic. They are stored inside Kubernetes secrets.So I've just passed the password and the certificate into my NodeJs service via environment variables like this:
apiVersion: apps/v1kind: Deploymentmetadata: name: search-deploymentspec: selector: matchLabels: app: search replicas: 1 template: metadata: labels: app: search spec: containers: - name: search image: search:placeholder_name imagePullPolicy: Always env: - name: elasticsearch_node value: https://elasticsearch-es-http.default.svc.cluster.local:9200 - name: elasticsearch_certificate valueFrom: secretKeyRef: name: elasticsearch-es-http-ca-internal key: tls.crt - name: elasticsearch_password valueFrom: secretKeyRef: name: elasticsearch-es-elastic-user key: elastic
I'd like to build on top of @Florian Ludewig's answer on 2 points, since I struggled myself to have it work on my side.
1. Don't turn off rejectUnauthorized
const client = new elasticsearch.Client({ node: 'node httpS url here', ssl: { ca: process.env.elasticsearch_certificate, rejectUnauthorized: true, // <-- this is important },});
If you set rejectUnauthorized
to false, the underlying nodejs https agent will bypass the certificate check. Of course if you are confident in the security of your cluster, you could disable it, but it renders the idea of providing the CA cert useless in the first place.
2. Make sure you provide a PEM CA cert - not the base64 encoded version
Perhaps you are providing the CA cert from your own config file without using Kubernetes' secret injection - possibly because the ES client application is in a different namespace and thus cannot access the CA secret.
In this case you may find it useful to store the CA cert as a base64 string, in a config file, but you should not forget to provide a decoded string to your client:
const config = loadConfigFromFile('config.yml');const caCertificate = Buffer.from(config.base64CaCertificate, 'base64').toString();const client = new elasticsearch.Client({ node: 'node httpS url here', ssl: { ca: caCertificate, rejectUnauthorized: true },});
In order to resolve your issue you will need to trust the CA, you should be able to do so using the following. Also found the following Question here.
If you wish to import the CA as a env variable as discussed you might be able to do something like:
- name: NODE_EXTRA_CA_CERTS valueFrom: secretKeyRef: name: elasticsearch-ca key: tls.crt
Note: I've not tried the above, an alternative would be to mound the secret as a volume and import it that way :)
Please note that If you wish to disable TLS on your Elasticsearch deployment you can do so as follows:
spec: http: tls: selfSignedCertificate: disabled: true
Please note that disabling TLS isn't recommended.