From 0bf3c7ed83c72160bfde660faa2b7e0c14cac318 Mon Sep 17 00:00:00 2001 From: Julien Mailleret <8582351+jmlrt@users.noreply.github.com> Date: Tue, 12 Oct 2021 13:11:30 +0200 Subject: [PATCH] [elasticsearch] use security by default (#1384) This commit update Elasticsearch chart to use security by default. - Adds a new Secret template for Elasticsearch password with a randomized password if `secret.password` isn't defined. - Adds instructions to retrieve the password in Elasticsearch chart deployment notes. - Also, remove usage of `ELASTIC_USERNAME` variable because it don't seem to be supported anymore by Elasticsearch The other charts will be updated in follow-up PRs to use the proper credentials Relates to https://github.com/elastic/helm-charts/issues/1375 --- elasticsearch/README.md | 13 ++-- elasticsearch/templates/NOTES.txt | 6 +- elasticsearch/templates/secret.yaml | 23 +++++++ elasticsearch/templates/statefulset.yaml | 18 ++++- elasticsearch/values.yaml | 85 +++++++++++++----------- 5 files changed, 96 insertions(+), 49 deletions(-) create mode 100644 elasticsearch/templates/secret.yaml diff --git a/elasticsearch/README.md b/elasticsearch/README.md index 9999956f..b30f6c0e 100644 --- a/elasticsearch/README.md +++ b/elasticsearch/README.md @@ -157,6 +157,8 @@ support multiple versions with minimal changes. | `resources` | Allows you to set the [resources][] for the StatefulSet | see [values.yaml][] | | `roles` | A list with the specific [roles][] for the `nodeGroup` | see [values.yaml][] | | `schedulerName` | Name of the [alternate scheduler][] | `""` | +| `secret.enabled` | Enable Secret creation for Elasticsearch credentials | `true` | +| `secret.password` | Initial password for the elastic user | `""` (generated randomly) | | `secretMounts` | Allows you easily mount a secret as a file inside the StatefulSet. Useful for mounting certificates and other secrets. See [values.yaml][] for an example | `[]` | | `securityContext` | Allows you to set the [securityContext][] for the container | see [values.yaml][] | | `service.annotations` | [LoadBalancer annotations][] that Kubernetes will use for the service. This will configure load balancer if `service.type` is `LoadBalancer` | `{}` | @@ -260,9 +262,12 @@ sufficient. ### How to deploy clusters with security (authentication and TLS) enabled? -This Helm chart can use existing [Kubernetes secrets][] to setup -credentials or certificates for examples. These secrets should be created -outside of this chart and accessed using [environment variables][] and volumes. +This Helm chart can generate a [Kubernetes Secret][] or use an existing one to +setup Elastic credentials. + +This Helm chart can use existing [Kubernetes Secret][] to setup Elastic +certificates for example. These secrets should be created outside of this chart +and accessed using [environment variables][] and volumes. An example of Elasticsearch cluster using security can be found in [examples/security][]. @@ -438,7 +443,7 @@ about our development and testing process. [jvm heap size]: https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html [hostAliases]: https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ [kind]: https://github.com/elastic/helm-charts/tree/master/elasticsearch/examples/kubernetes-kind -[kubernetes secrets]: https://kubernetes.io/docs/concepts/configuration/secret/ +[kubernetes secret]: https://kubernetes.io/docs/concepts/configuration/secret/ [labels]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ [lifecycle hooks]: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/ [loadBalancer annotations]: https://kubernetes.io/docs/concepts/services-networking/service/#ssl-support-on-aws diff --git a/elasticsearch/templates/NOTES.txt b/elasticsearch/templates/NOTES.txt index 88b5dd5b..752526f3 100755 --- a/elasticsearch/templates/NOTES.txt +++ b/elasticsearch/templates/NOTES.txt @@ -1,6 +1,8 @@ 1. Watch all cluster members come up. $ kubectl get pods --namespace={{ .Release.Namespace }} -l app={{ template "elasticsearch.uname" . }} -w -{{- if .Values.tests.enabled -}} -2. Test cluster health using Helm test. +2. Retrieve elastic user's password. + $ kubectl get secrets --namespace={{ .Release.Namespace }} {{ template "elasticsearch.uname" . }}-credentials -ojsonpath='{.data.password}' | base64 -d +{{- if .Values.tests.enabled }} +3. Test cluster health using Helm test. $ helm --namespace={{ .Release.Namespace }} test {{ .Release.Name }} {{- end -}} diff --git a/elasticsearch/templates/secret.yaml b/elasticsearch/templates/secret.yaml new file mode 100644 index 00000000..cbdcbbaf --- /dev/null +++ b/elasticsearch/templates/secret.yaml @@ -0,0 +1,23 @@ +{{- if .Values.secret.enabled -}} +{{- $passwordValue := (randAlphaNum 16) | b64enc | quote }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "elasticsearch.uname" . }}-credentials + labels: + heritage: {{ .Release.Service | quote }} + release: {{ .Release.Name | quote }} + chart: "{{ .Chart.Name }}" + app: "{{ template "elasticsearch.uname" . }}" + {{- range $key, $value := .Values.labels }} + {{ $key }}: {{ $value | quote }} + {{- end }} +type: Opaque +data: + username: {{ "elastic" | b64enc }} + {{- if .Values.secret.password }} + password: {{ .Values.secret.password | b64enc }} + {{- else }} + password: {{ $passwordValue }} + {{- end }} +{{- end }} diff --git a/elasticsearch/templates/statefulset.yaml b/elasticsearch/templates/statefulset.yaml index 3d4e5015..bad72ee1 100644 --- a/elasticsearch/templates/statefulset.yaml +++ b/elasticsearch/templates/statefulset.yaml @@ -231,6 +231,13 @@ spec: - -c - | #!/usr/bin/env bash -e + + # Exit if ELASTIC_PASSWORD in unset + if [ -z "${ELASTIC_PASSWORD}" ]; then + echo "ELASTIC_PASSWORD variable is missing, exiting" + exit 1 + fi + # If the node is starting up wait for the cluster to be ready (request params: "{{ .Values.clusterHealthCheckParams }}" ) # Once it has started only check that the node itself is responding START_FILE=/tmp/.es_start_file @@ -248,9 +255,7 @@ spec: set -- "$@" $args fi - if [ -n "${ELASTIC_USERNAME}" ] && [ -n "${ELASTIC_PASSWORD}" ]; then - set -- "$@" -u "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" - fi + set -- "$@" -u "elastic:${ELASTIC_PASSWORD}" curl --output /dev/null -k "$@" "{{ .Values.protocol }}://127.0.0.1:{{ .Values.httpPort }}${path}" } @@ -313,6 +318,13 @@ spec: value: "{{ .Values.clusterName }}" - name: network.host value: "{{ .Values.networkHost }}" + {{- if .Values.secret.enabled }} + - name: ELASTIC_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "elasticsearch.uname" . }}-credentials + key: password + {{- end }} {{- if .Values.esJavaOpts }} - name: ES_JAVA_OPTS value: "{{ .Values.esJavaOpts }}" diff --git a/elasticsearch/values.yaml b/elasticsearch/values.yaml index 11b70a58..0efde9c0 100755 --- a/elasticsearch/values.yaml +++ b/elasticsearch/values.yaml @@ -49,6 +49,11 @@ envFrom: [] # - configMapRef: # name: config-map +# Disable it to use your own elastic-credential Secret. +secret: + enabled: true + password: "" # generated randomly if not defined + # A list of secrets and their paths to mount inside the pod # This is useful for mounting certificates for security and for mounting # the X-Pack license @@ -69,7 +74,7 @@ imageTag: "8.0.0-SNAPSHOT" imagePullPolicy: "IfNotPresent" podAnnotations: {} - # iam.amazonaws.com/role: es-cluster +# iam.amazonaws.com/role: es-cluster # additionals labels labels: {} @@ -85,17 +90,17 @@ resources: memory: "2Gi" initResources: {} - # limits: - # cpu: "25m" - # # memory: "128Mi" - # requests: - # cpu: "25m" - # memory: "128Mi" +# limits: +# cpu: "25m" +# # memory: "128Mi" +# requests: +# cpu: "25m" +# memory: "128Mi" networkHost: "0.0.0.0" volumeClaimTemplate: - accessModes: [ "ReadWriteOnce" ] + accessModes: ["ReadWriteOnce"] resources: requests: storage: 30Gi @@ -133,23 +138,23 @@ persistence: annotations: {} extraVolumes: [] - # - name: extras - # emptyDir: {} +# - name: extras +# emptyDir: {} extraVolumeMounts: [] - # - name: extras - # mountPath: /usr/share/extras - # readOnly: true +# - name: extras +# mountPath: /usr/share/extras +# readOnly: true extraContainers: [] - # - name: do-something - # image: busybox - # command: ['do', 'something'] +# - name: do-something +# image: busybox +# command: ['do', 'something'] extraInitContainers: [] - # - name: do-something - # image: busybox - # command: ['do', 'something'] +# - name: do-something +# image: busybox +# command: ['do', 'something'] # This is the PriorityClass settings as defined in # https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass @@ -207,7 +212,7 @@ podSecurityContext: securityContext: capabilities: drop: - - ALL + - ALL # readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1000 @@ -241,8 +246,8 @@ tolerations: [] ingress: enabled: false annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" hosts: - host: chart-example.local paths: @@ -257,24 +262,24 @@ fullnameOverride: "" healthNameOverride: "" lifecycle: {} - # preStop: - # exec: - # command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"] - # postStart: - # exec: - # command: - # - bash - # - -c - # - | - # #!/bin/bash - # # Add a template to adjust number of shards/replicas - # TEMPLATE_NAME=my_template - # INDEX_PATTERN="logstash-*" - # SHARD_COUNT=8 - # REPLICA_COUNT=1 - # ES_URL=http://localhost:9200 - # while [[ "$(curl -s -o /dev/null -w '%{http_code}\n' $ES_URL)" != "200" ]]; do sleep 1; done - # curl -XPUT "$ES_URL/_template/$TEMPLATE_NAME" -H 'Content-Type: application/json' -d'{"index_patterns":['\""$INDEX_PATTERN"\"'],"settings":{"number_of_shards":'$SHARD_COUNT',"number_of_replicas":'$REPLICA_COUNT'}}' +# preStop: +# exec: +# command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"] +# postStart: +# exec: +# command: +# - bash +# - -c +# - | +# #!/bin/bash +# # Add a template to adjust number of shards/replicas +# TEMPLATE_NAME=my_template +# INDEX_PATTERN="logstash-*" +# SHARD_COUNT=8 +# REPLICA_COUNT=1 +# ES_URL=http://localhost:9200 +# while [[ "$(curl -s -o /dev/null -w '%{http_code}\n' $ES_URL)" != "200" ]]; do sleep 1; done +# curl -XPUT "$ES_URL/_template/$TEMPLATE_NAME" -H 'Content-Type: application/json' -d'{"index_patterns":['\""$INDEX_PATTERN"\"'],"settings":{"number_of_shards":'$SHARD_COUNT',"number_of_replicas":'$REPLICA_COUNT'}}' sysctlInitContainer: enabled: true