176 lines
6.1 KiB
YAML
176 lines
6.1 KiB
YAML
---
|
|
apiVersion: v1
|
|
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:
|
|
manage-es-token.js: |
|
|
const https = require('https');
|
|
const fs = require('fs');
|
|
|
|
// Read environment variables
|
|
function getEnvVar(name) {
|
|
if (!process.env[name]) {
|
|
throw new Error(name + ' environment variable is missing')
|
|
}
|
|
return process.env[name]
|
|
}
|
|
|
|
// 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);
|
|
|
|
// 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');
|
|
return 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
|
|
const 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');
|
|
return 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');
|
|
return 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);
|
|
}
|