Skip to content
This repository has been archived by the owner on May 16, 2023. It is now read-only.

Commit

Permalink
[kibana] create a secret with es token (#1720)
Browse files Browse the repository at this point in the history
* [kibana] create a secret with es token

* fix pre-post-install jobs

* fix clean command + optimize js script

* fix upgrade test

* fix a few bugs

* Update kibana/templates/configmap-helm-scripts.yaml

Co-authored-by: Rory Hunter <pugnascotia@users.noreply.github.com>

* check if vars exist

* handle both payload and extra status code

* return promises

* add a note to retrieve the kibana token

* exit when catching an error

* add more retries instead of sleep

* fixup! add a note to retrieve the kibana token

* Update kibana/templates/configmap-helm-scripts.yaml

Co-authored-by: Rory Hunter <pugnascotia@users.noreply.github.com>

* Update kibana/templates/configmap-helm-scripts.yaml

Co-authored-by: Rory Hunter <pugnascotia@users.noreply.github.com>

Co-authored-by: Rory Hunter <pugnascotia@users.noreply.github.com>
  • Loading branch information
jmlrt and pugnascotia authored Nov 15, 2022
1 parent 8565b30 commit 90f59ef
Show file tree
Hide file tree
Showing 11 changed files with 349 additions and 52 deletions.
2 changes: 2 additions & 0 deletions kibana/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
$ kubectl get pods --namespace={{ .Release.Namespace }} -l release={{ .Release.Name }} -w
2. Retrieve the elastic user's password.
$ kubectl get secrets --namespace={{ .Release.Namespace }} {{ .Values.elasticsearchCredentialSecret }} -ojsonpath='{.data.password}' | base64 -d
3. Retrieve the kibana service account token.
$ kubectl get secrets --namespace={{ .Release.Namespace }} {{ template "kibana.fullname" . }}-es-token -ojsonpath='{.data.token}' | base64 -d
176 changes: 165 additions & 11 deletions kibana/templates/configmap-helm-scripts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,172 @@ kind: ConfigMap
metadata:
name: {{ template "kibana.fullname" . }}-helm-scripts
labels: {{ include "kibana.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade,post-delete
"helm.sh/hook-delete-policy": hook-succeeded
{{- if .Values.annotations }}
{{- range $key, $value := .Values.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
data:
kibana-entrypoint.sh: |
#!/bin/bash
set -euo pipefail
manage-es-token.js: |
const https = require('https');
const fs = require('fs');
echo "export ELASTICSEARCH_SERVICEACCOUNTTOKEN=$({{ template "kibana.home_dir" . }}/node/bin/node {{ template "kibana.home_dir" . }}/helm-scripts/parse-token.js {{ template "kibana.home_dir" . }}/config/tokens/{{ template "kibana.fullname" . }}.json)" > $HOME/.elasticsearch-serviceaccounttoken
source $HOME/.elasticsearch-serviceaccounttoken
// Read environment variables
function getEnvVar(name) {
if (!process.env[name]) {
throw new Error(name + ' environment variable is missing')
}
return process.env[name]
}
# https://github.com/elastic/dockerfiles/blob/a405a4d692031b72cefcb8523bd464aa3221ec47/kibana/Dockerfile#L131
exec /bin/tini -- /usr/local/bin/kibana-docker "$@"
// Elasticsearch API
const esPath = '_security/service/elastic/kibana/credential/token/{{ template "kibana.fullname" . }}';
const esUrl = '{{ .Values.elasticsearchHosts }}' + '/' + esPath
const esUsername = getEnvVar('ELASTICSEARCH_USERNAME');
const esPassword = getEnvVar('ELASTICSEARCH_PASSWORD');
const esAuth = esUsername + ':' + esPassword;
const esCaFile = getEnvVar('ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES');
const esCa = fs.readFileSync(esCaFile);
parse-token.js: |
let dataFile = process.argv[2];
let dataContent = require(dataFile.toString());
console.log(dataContent.token.value);
// Kubernetes API
const k8sHostname = getEnvVar('KUBERNETES_SERVICE_HOST');
const k8sPort = getEnvVar('KUBERNETES_SERVICE_PORT_HTTPS');
const k8sPostSecretPath = 'api/v1/namespaces/{{ .Release.Namespace }}/secrets';
const k8sDeleteSecretPath = 'api/v1/namespaces/{{ .Release.Namespace }}/secrets/{{ template "kibana.fullname" . }}-es-token';
const k8sPostSecretUrl = `https://${k8sHostname}:${k8sPort}/${k8sPostSecretPath}`;
const k8sDeleteSecretUrl = `https://${k8sHostname}:${k8sPort}/${k8sDeleteSecretPath}`;
const k8sBearer = fs.readFileSync('/run/secrets/kubernetes.io/serviceaccount/token');
const k8sCa = fs.readFileSync('/run/secrets/kubernetes.io/serviceaccount/ca.crt');
// Post Data
const esTokenDeleteOptions = {
method: 'DELETE',
auth: esAuth,
ca: esCa,
};
const esTokenCreateOptions = {
method: 'POST',
auth: esAuth,
ca: esCa,
};
const secretCreateOptions = {
method: 'POST',
ca: k8sCa,
headers: {
'Authorization': 'Bearer ' + k8sBearer,
'Accept': 'application/json',
'Content-Type': 'application/json',
}
};
const secretDeleteOptions = {
method: 'DELETE',
ca: k8sCa,
headers: {
'Authorization': 'Bearer ' + k8sBearer,
'Accept': 'application/json',
'Content-Type': 'application/json',
}
};
// With thanks to https://stackoverflow.com/questions/57332374/how-to-chain-http-request
function requestPromise(url, httpsOptions, extraOptions = {}) {
return new Promise((resolve, reject) => {
const request = https.request(url, httpsOptions, response => {
console.log('statusCode:', response.statusCode);
let isSuccess = undefined;
if (typeof(extraOptions.extraStatusCode) != "undefined" && extraOptions.extraStatusCode != null) {
isSuccess = response.statusCode >= 200 && response.statusCode < 300 || response.statusCode == extraOptions.extraStatusCode;
} else {
isSuccess = response.statusCode >= 200 && response.statusCode < 300;
}
let data = '';
response.on('data', chunk => data += chunk); // accumulate data
response.once('end', () => isSuccess ? resolve(data) : reject(data)); // resolve promise here
});
request.once('error', err => {
// This won't log anything for e.g. an HTTP 404 or 500 response,
// since from HTTP's point-of-view we successfully received a
// response.
console.log(`${httpsOptions.method} ${httpsOptions.path} failed: `, err.message || err);
reject(err); // if promise is not already resolved, then we can reject it here
});
if (typeof(extraOptions.payload) != "undefined") {
request.write(extraOptions.payload);
}
request.end();
});
}
function createEsToken() {
// Chaining requests
console.log('Cleaning previous token');
// 404 status code is accepted if there is no previous token to clean
return requestPromise(esUrl, esTokenDeleteOptions, {extraStatusCode: 404}).then(() => {
console.log('Creating new token');
requestPromise(esUrl, esTokenCreateOptions).then(response => {
const body = JSON.parse(response);
const token = body.token.value
// Encode the token in base64
const base64Token = Buffer.from(token, 'utf8').toString('base64');
// Prepare the k8s secret
secretData = JSON.stringify({
"apiVersion": "v1",
"kind": "Secret",
"metadata": {
"namespace": "{{ .Release.Namespace }}",
"name": "{{ template "kibana.fullname" . }}-es-token",
},
"type": "Opaque",
"data": {
"token": base64Token,
}
})
// Create the k8s secret
console.log('Creating K8S secret');
requestPromise(k8sPostSecretUrl, secretCreateOptions, {payload: secretData})
});
});
}
function cleanEsToken() {
// Chaining requests
console.log('Cleaning token');
return requestPromise(esUrl, esTokenDeleteOptions).then(() => {
// Create the k8s secret
console.log('Delete K8S secret');
requestPromise(k8sDeleteSecretUrl, secretDeleteOptions)
});
}
const command = process.argv[2];
switch (command) {
case 'create':
console.log('Creating a new Elasticsearch token for Kibana')
createEsToken().catch(err => {
console.error(err);
process.exit(1);
});
break;
case 'clean':
console.log('Cleaning the Kibana Elasticsearch token')
cleanEsToken().catch(err => {
console.error(err);
process.exit(1);
});
break;
default:
console.log('Unknown command');
process.exit(1);
}
39 changes: 6 additions & 33 deletions kibana/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ spec:
volumes:
- name: kibana-tokens
emptyDir: {}
- name: kibana-helm-scripts
configMap:
name: {{ template "kibana.fullname" . }}-helm-scripts
defaultMode: 0755
- name: elasticsearch-certs
secret:
secretName: {{ .Values.elasticsearchCertificateSecret }}
Expand Down Expand Up @@ -86,31 +82,6 @@ spec:
{{ toYaml .Values.imagePullSecrets | indent 8 }}
{{- end }}
initContainers:
- name: configure-kibana-token
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
imagePullPolicy: "{{ .Values.imagePullPolicy }}"
# TODO add retries
command:
- sh
- -c
- curl --output {{ template "kibana.home_dir" . }}/config/tokens/{{ template "kibana.fullname" . }}.json --fail -XPOST --cacert {{ template "kibana.home_dir" . }}/config/certs/{{ .Values.elasticsearchCertificateAuthoritiesFile }} -u "$(ELASTICSEARCH_USERNAME):$(ELASTICSEARCH_PASSWORD)" "{{ .Values.elasticsearchHosts }}/_security/service/elastic/kibana/credential/token/{{ template "kibana.fullname" . }}?pretty"
env:
- name: "ELASTICSEARCH_USERNAME"
valueFrom:
secretKeyRef:
name: {{ .Values.elasticsearchCredentialSecret }}
key: username
- name: "ELASTICSEARCH_PASSWORD"
valueFrom:
secretKeyRef:
name: {{ .Values.elasticsearchCredentialSecret }}
key: password
volumeMounts:
- name: elasticsearch-certs
mountPath: {{ template "kibana.home_dir" . }}/config/certs
readOnly: true
- name: kibana-tokens
mountPath: {{ template "kibana.home_dir" . }}/config/tokens
{{- if .Values.extraInitContainers }}
# Currently some extra blocks accept strings
# to continue with backwards compatibility this is being kept
Expand All @@ -127,8 +98,6 @@ spec:
{{ toYaml .Values.securityContext | indent 10 }}
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
imagePullPolicy: "{{ .Values.imagePullPolicy }}"
command: ["/bin/bash"]
args: ["-c","{{ template "kibana.home_dir" . }}/helm-scripts/kibana-entrypoint.sh"]
env:
{{- if .Values.elasticsearchURL }}
- name: ELASTICSEARCH_URL
Expand All @@ -141,6 +110,12 @@ spec:
value: "{{ template "kibana.home_dir" . }}/config/certs/{{ .Values.elasticsearchCertificateAuthoritiesFile }}"
- name: SERVER_HOST
value: "{{ .Values.serverHost }}"
- name: ELASTICSEARCH_SERVICEACCOUNTTOKEN
valueFrom:
secretKeyRef:
name: {{ template "kibana.fullname" . }}-es-token
key: token
optional: false
{{- if .Values.extraEnvs }}
{{ toYaml .Values.extraEnvs | indent 10 }}
{{- end }}
Expand Down Expand Up @@ -194,8 +169,6 @@ spec:
- name: kibana-tokens
mountPath: {{ template "kibana.home_dir" . }}/config/tokens
readOnly: true
- name: kibana-helm-scripts
mountPath: {{ template "kibana.home_dir" . }}/helm-scripts
{{- range .Values.secretMounts }}
- name: {{ .name }}
mountPath: {{ .path }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: batch/v1
kind: Job
metadata:
name: {{ template "kibana.fullname" . }}-post-delete
name: post-delete-{{ template "kibana.fullname" . }}
labels: {{ include "kibana.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": post-delete
Expand All @@ -20,14 +20,10 @@ spec:
- name: clean-kibana-token
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
imagePullPolicy: "{{ .Values.imagePullPolicy }}"
command: ["curl"]
command: ["{{ template "kibana.home_dir" . }}/node/bin/node"]
args:
- -XDELETE
- --cacert
- {{ template "kibana.home_dir" . }}/config/certs/{{ .Values.elasticsearchCertificateAuthoritiesFile }}
- -u
- "$(ELASTICSEARCH_USERNAME):$(ELASTICSEARCH_PASSWORD)"
- "{{ .Values.elasticsearchHosts }}/_security/service/elastic/kibana/credential/token/{{ template "kibana.fullname" . }}"
- {{ template "kibana.home_dir" . }}/helm-scripts/manage-es-token.js
- clean
env:
- name: "ELASTICSEARCH_USERNAME"
valueFrom:
Expand All @@ -39,11 +35,20 @@ spec:
secretKeyRef:
name: {{ .Values.elasticsearchCredentialSecret }}
key: password
- name: ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES
value: "{{ template "kibana.home_dir" . }}/config/certs/{{ .Values.elasticsearchCertificateAuthoritiesFile }}"
volumeMounts:
- name: elasticsearch-certs
mountPath: {{ template "kibana.home_dir" . }}/config/certs
readOnly: true
- name: kibana-helm-scripts
mountPath: {{ template "kibana.home_dir" . }}/helm-scripts
serviceAccount: post-delete-{{ template "kibana.fullname" . }}
volumes:
- name: elasticsearch-certs
secret:
secretName: {{ .Values.elasticsearchCertificateSecret }}
- name: kibana-helm-scripts
configMap:
name: {{ template "kibana.fullname" . }}-helm-scripts
defaultMode: 0755
20 changes: 20 additions & 0 deletions kibana/templates/post-delete-role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: post-delete-{{ template "kibana.fullname" . }}
labels: {{ include "kibana.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": post-delete
"helm.sh/hook-delete-policy": hook-succeeded
{{- if .Values.annotations }}
{{- range $key, $value := .Values.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- delete
21 changes: 21 additions & 0 deletions kibana/templates/post-delete-rolebinding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: post-delete-{{ template "kibana.fullname" . }}
labels: {{ include "kibana.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": post-delete
"helm.sh/hook-delete-policy": hook-succeeded
{{- if .Values.annotations }}
{{- range $key, $value := .Values.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
subjects:
- kind: ServiceAccount
name: post-delete-{{ template "kibana.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
roleRef:
kind: Role
name: post-delete-{{ template "kibana.fullname" . }}
apiGroup: rbac.authorization.k8s.io
13 changes: 13 additions & 0 deletions kibana/templates/post-delete-serviceaccount.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: post-delete-{{ template "kibana.fullname" . }}
labels: {{ include "kibana.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": post-delete
"helm.sh/hook-delete-policy": hook-succeeded
{{- if .Values.annotations }}
{{- range $key, $value := .Values.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
Loading

0 comments on commit 90f59ef

Please sign in to comment.