This repository has been archived by the owner on May 16, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
[kibana] create a secret with es token #1720
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
bacbce1
[kibana] create a secret with es token
jmlrt 2b7524b
fix pre-post-install jobs
jmlrt 827ddb3
fix clean command + optimize js script
jmlrt 753facd
fix upgrade test
jmlrt 547a349
fix a few bugs
jmlrt 81ce165
Update kibana/templates/configmap-helm-scripts.yaml
jmlrt ff4a1c6
check if vars exist
jmlrt 43c08a1
handle both payload and extra status code
jmlrt e3ef0c1
return promises
jmlrt 5b812e5
add a note to retrieve the kibana token
jmlrt 86386f0
exit when catching an error
jmlrt cb0d6bb
add more retries instead of sleep
jmlrt c08720a
fixup! add a note to retrieve the kibana token
jmlrt 47ebb41
Update kibana/templates/configmap-helm-scripts.yaml
jmlrt 84a955d
Update kibana/templates/configmap-helm-scripts.yaml
jmlrt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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; | ||||||
pugnascotia marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
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); | ||||||
pugnascotia marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
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({ | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. whoops I missed the merge, didn't refresh in time. Sorry about the confusion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (I'll follow up with a PR) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
"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'); | ||||||
jmlrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
process.exit(1); | ||||||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will
.Values.elasticsearchHosts
always only have a single value?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question, it seems it was supposed to support multiple values but a bug that was never fixed prevented it (#603).
As this Kibana chart is supposed to connect to an Elasticsearch cluster deployed via the Elastic Helm charts, all Elasticsearch pods are supposed to be available via a single K8S service that should be used in
.Values.elasticsearchHosts
.IMHO, It's not worth fixing #603 and making the script compatible with multiple URLs. I'll update the doc to mention that a single URL (Elasticsearch service URL) is expected for this value, to make it clear.