Daten aus KeyCloak exportieren

Die Exportfunktion in dem KeyCloak, also über die Web UI, kann leider keinen Datenbank Dump durchführen. D.h. es werden keine Secrets exportiert, was einen vollständigen Import beim Starten eines Clusters nicht möglich macht.

Dennoch gibt es von der Konsole aus die Möglichkeit einen Export durchzuführen. Dazu muss man das Binary standalone.sh mit der Aktion keycloak.migration.action=export aufrufen. Dann lässt sich ein Dump durchführen. In diesem Artikel beschreibe ich folgende Situation. Es soll aus einem laufenden KeyCloak Container die vorgenommenen Einstellungen gesichert werden, sodass diese bei einem Neustart wiederhergestellt werden. Ich setze hier einen K3D Kubernetes Cluster mit KeyCloak ein.

Auf die Einstellungen des REALMs gehe ich hier jetzt nicht weiter ein und spielt auch keine Rolle welche REALMs vorhanden sind.

Das Vorgehen ist wie folgt: Es muss der Name des PODs ermittelt werden. In dem Beispiel gehe ich davon aus, dass der KeyCloak in einem eigenen Namespane keycloak läuft und das mit dem Label app=keycloak versehen ist. Haben wir die Namen des PODs ermittelt, dann können wir eine zweite Instanz in dem Container von außen starten und den Export anstoßen. Leider beendet der KeyCloak sich nicht nach dem Export, sodass wir eine CTRL+C Handler installieren müssen. Die vollständige Dokumentation für die KeyCloak Im-/Export-Funktion finden sie hier.

Wichtig ist für den Start der neuen Instanz, dass ein beliebiger freier Port gewählt wird, da sonst der KeyCloak nicht starten würde. Dieses gilt für den HTTP und den Management Port.

Ist der KeyCloak hochgefahren, dann kann er mit STRG+C beendet werden und der Export wird aus dem Container in lokale Verzeichnis kopiert.

Das Skript

Zusammengefasst ergibt sich folgendes export.sh Skript:

#!/bin/bash

#
# to copy export file from container stop standalone.sh with CTRL+c
#


# retrieve POD name
POD_NAME=`kubectl get pod -n keycloak -lapp=keycloak --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}'`

# add CTRL-C Handler
trap trap_catch INT


function trap_catch() {
  # copy exported config from container to local storage
  kubectl cp keycloak/$POD_NAME:/tmp/keycloak-export.json test.json
}

# start new server 
kubectl exec -n keycloak $POD_NAME bash -- /opt/jboss/keycloak/bin/standalone.sh -Dkeycloak.migration.action=export -Dkeycloak.migration.provider=singleFile -Dkeycloak.migration.file=/tmp/keycloak-export.json -Djboss.http.port=8888 -Djboss.https.port=9999 -Djboss.management.http.port=7777

Netdata

Netdata beitet ähnlich zu dem Gespann Prometheus/Grafana eine umfassende Monitoring Lösunng an.

Installation

Die Installation von Netdata ist unter dem Kubernetes Cluster mit K3D mit Fallstricken versehen.

Nach erfolgreicher Basisinstallation stellt sich Netdata wie folgt vor:

Worauf muss man nun genau bei der Installation achten? Wer keine weiteren Anpassungen bei dem Kreieren des K3D Cluster vornimmt, der installiert automatisch Traefik v1.7 in den Cluster. Dieses verursacht mit dem bereitgestellten Helm Chart Probleme, da es auf NginX als Ingress-Controller ausgerichtet ist.

Traefik als Ingress-Controller verwenden

Unter Kubernetes lässt sich der zu verwendende Ingress-Controller bestimmen. Dazu muss die kubernetes.io/ingress.class auf traefik gesetzt werden.

Mit Helm 3 hat sich die Syntax geändert, sodass viele Informationen die man im Internet zu dem Thema findet schlicht veraltet sind und somit zu einer Fehlermeldung für. Wie kann man nun die ingress.class mit Helm setzen?

--set ingress.annotations."kubernetes\.io/ingress\.class"=traefik

Es muss die Annotation in Anführungsstrichen gesetzt werden und Punkte müssen mit dem „\“ Escaped werden. Also wird somit aus kubernetes.io/ingress.class=traefik folgendes „kubernetes.io/ingress.class“=traefik

DockerHub Registry verwenden

Da es beim 1. Test Probleme mit der Registry gab, habe ich kurzerhand die offizielle Docker Hub Registry verwendet. Diese wird über registry.hub.docker.com angesprochen. Die Registry lässt sich über image.registry steuern.

--set image.registry=registry.hub.docker.com

Danach konnte das image sofort heruntergeladen werden.

Das Repo zu Helm hinzufügen

Die Helm Charts sind auf GitHub unter https://netdata.github.io/helmchart/ gehostet.

helm repo add netdata https://netdata.github.io/helmchart/
helm repo update

Zusammenfassung

Faskst man alles zusammen ergibt sich folgende Befehlsreihenfolge:

helm repo add netdata https://netdata.github.io/helmchart/
helm repo update

helm install netdata netdata/netdata \
  -n netdata \
  --create-namespace \
  --set ingress.hosts[0]=netdata.k3d.duckdns.org \
  --set image.registry=registry.hub.docker.com \
  --set ingress.annotations."kubernetes\.io/ingress\.class"=traefik

Nach dem Deployment kann man Netdata unter https://netdata.k3d.duckddns.org aufrufen,

K3D Kubernetes Cluster

Ein Kubernetes Cluster ist nach außen hin für die laufenden Services abgeschottet. Das heißt man kann nicht von außen auf die laufenden Dieste zugreifen.

Um Dienste zugänglich zu machen, kann man Ports exposen oder ein Ingress verwenden. Mit Ingress (auf einem LoadBalancer) kann über den Host im Header der Service identifiziert werden. Erfolgt nun eine Namensauflösung z.B. https://demo.k3d.duckdns.org auf ihre öffentliche IP Adressen, dann kann ein Reverse Proxy die Anfragen entgegennehmen und an den Cluster intern weiterleiten.

Der Ingress-Controller wertet nun die Information in dem Header aus und entscheidet das Routing innerhalb des Clusters.

In diesem Artikel möchte ich zeigen, wie man mit NginX, Duckdns und K3D eine flexible Lösung erstellt, in der automatisch alle Endpoints über TLS gesichert sind.

NGinX

Damit das Ganze funktioniert sind ein paar Voraussetzungen zu erfüllen:

  • Es muss ein gültiges Zertifikat für die Domain vorliegen (siehe Wildcard Zertifikat mit Lets Encrypt)
  • Eine DynDNS oder entsprechend konfigurierter DNS Server oder Host Einträge müssen vorliegen
  • Der Server Block in NginX muss die Anfrage matchen
  • Ein paar nötige Header müssen gesetzt werden

HTTP auf HTTPS umlenken

Mit einer 301 Antwort kann den Traffic auf HTTPS umlenken.

    # Redirect http -> https
    server {
        listen 80;
        server_name "~^.+\.k3d\.duckdns\.org$";
        return 301 https://$host$request_uri;
    }

Wichtig: Es darf hier nicht $server_name verwendet werden! Es muss $host genommen werden, da es sonst zu Fehlern im Browser kommt. Dann enthält der Header location die RegEx und der Browser spuckt eine Fehlermeldung aus.

Server_Name matching

Da ich nicht für jede neue Anwendung eine eigenständige Konfiguration für NginX schreiben möchte, habe ich NginX so konfiguriert, dass es Pattern Matching in dem Server_Name betreibt.

Ich habe für den lokalen Kubernetes Cluster das Schema https://keycloak.k3d.duckdns.org verwendet.

server {
    listen 443 ssl http2;

    server_name "~^+\.k3d\.duckdns\.org$";
...
}

Die RegEx nimmt alle Anfragen für https://XXXXXX.k3d.duckdns.org entgegen.

Header setzen

Im NGINX Server Block müssen folgende Header gesetzt sein, damit der Ingress-Controller das Routing vornehmen kann.

proxy_set_header X-Forwarded-For $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection ‘upgrade’;
proxy_set_header Host $host;

Die PROXY_PASS Anweisung leitet nun Anfragen an den Cluster weiter.

proxy_pass http://10.10.10.10:4000;

Der Cluster läuft in diesem Fall auf dem Server mit der IP Adresse 10.10.10.10 und lauscht auf dem Port 4000.

TLS konfigurieren

Für die Absicherung der Verbindung mit TLS, kommt folgender Block in der Konfiguration zum Einsatz. Per Default werden die von dem Cert-Bot aktualiesierten SSL Zertifikate unter halb den Verzeichnisses /etc/letsencryp/live/DOMAIN/ abgelegt. Die Fullchain und der private key werden eingebunden und somit der Endpoint mit TLS abgesichert.

# SSL
ssl_certificate /etc/letsencrypt/live/k3d.duckdns.org/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/k3d.duckdns.org/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

Zusammenfassung der Konfiguration von NginX

Hier nun die gesamte Konfiguration von NginX.

###################################
#                                 #
# Kubernetes Cluster              #
#                                 #
#                                 #
###################################

# Redirect http -> https
server {
    listen 80;
    server_name "~^.+\.k3d\.duckdns\.org$";
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;

    server_name "~^-.+\.k3d\.duckdns\.org$";

    # logging
    error_log /var/log/nginx/kubernetes_error.log debug;
    access_log /var/log/nginx/kubernetes_access.log;

    location / {
        proxy_pass http://10.10.10.10:4000;
        proxy_set_header Connection ""; # to keep alive

        proxy_set_header X-Forwarded-For $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection ‘upgrade’;
        proxy_set_header Host $host;
    }

    # SSL
    include ssl/*.k3d.duckdns.org;
}

In einem K3D Cluster das Kubernetes Dashboard deployen

Hat man einen K3D Cluster aufgesetzt, dann liegt es gerade in den Anfängen nahe das Dashboard zu installieren. Für das Kubernetes Dashboard gibt es sogar ein Helm Chart.

Die Installation mit Helm ist ja an und für sich nicht schwierig, nur wird man nach der Installation eine Überraschung erleben. Folgt man der Anleitung:

# Add kubernetes-dashboard repository
helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
# Deploy a Helm Release named "my-release" using the kubernetes-dashboard chart
helm install kubernetes-dashboard/kubernetes-dashboard --name my-release

Nach der Installation wird man anders als gedacht, von einem freundlichen internal server error empfangen.

Ingress

Der Ingress-Controler steuert das Routing anhand der vorhandenen Host Informationen im Header. Bei K3S/K3D wird implitziet Traefik v1.7 installiert.

Diese sorg für das genannte Problem und daher müssen wir ein paar Änderungen vornehmen, damit wir das Dashboard mit dem K3D Cluster zum laufen bringen.

Variante 1

Man übergibt direkt in den ingress.annotations die ingress.class=traefik und setzt den Host direkt.

--set ingress.annotations."kubernetes\.io/ingress\.class"=traefik \
--set ingress.enabled=true \
--set ingress.hosts[0]=dashboard.k3d.duckdns.org

Variante 2

Bei der Variante 2 wird das Ingress in dem Helm Chart nicht genutzt und das Ingress klassisch als Kubernetes Manifest gesetzt.

Ingress in dem Helm Chart deaktivieren

Wie bereits angesprochen, müssen wir zunächst das Ingress in dem Helm Chart deaktivieren. Das geschied über den Parameter **–set ingress.enabled=false **

 --set ingress.enabled=false

Traefik Ingress per Kubernetes Manifest

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  labels:
    name: kubernetes-dashboard 
  name: kubernetes-dashboard-secure
  namespace: default
  annotations:
   kubernetes.io/ingress.class: traefik
spec:
  rules:
  - host: dashboard.k3d.duckdns.org
    http:
      paths:
      - backend:
          serviceName: dashboard-kubernetes-dashboard
          servicePort: 8080

Das Skript create_cluster_admin.sh

Das Skript create_cluster_admin.sh verwende ich, um einen Service Account mit der Rolle cluster-admin.

create_cluster_admin dashboard

Installation

Insgesamt ergeben sich nun daraus folgende Installationsschritte.

# add repo
helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/

# add SA for Dashboard
create_cluster_admin.sh dashboard

# install chart
helm install dashboard kubernetes-dashboard/kubernetes-dashboard --set protocolHttp=true --set metricsScraper.enabled=true --set service.externalPort=8080 --set serviceAccount.name=dashboard --set serviceAccount.create=false

# set traefik ingress
kubectl apply -f ./ingress.yaml

Sonarqube im Kubernetes Cluster

Sonarqube ist ein Open Source Tool, um die Verbesserung der Qualität der Software zu erhöhen. Sonarqube bedindt sich der statischen Codeanalyse und kann verhindern, dass übliche Fehler sich in den Quelltext einschleichen.

Für die Installation sollte zunächst ein K3D aufgesetz werden.

Da es im moment kein offizielles Helm Chart für die Installation gibt, es aber im Stable Repository auf oteemocharts verwiesen wird, nutze ich das Repository.

Repository hinzufügen

Zunächst muss man das Repository von oteemocharts hinzufügen.

helm repo add oteemocharts https://oteemo.github.io/charts

Installation

Die Installation ist recht einfach. Es sind für den Basisbetrieb zunächst nur wenige Parameter nötig. Wie üblich sollte man die Anwendung in einem eigenen Namespace laufen lassen. Ich verwende hier den Namespace sonarqube.

kubectl create namespace sonarqube
helm install sonarqube --namespace sonarqube oteemocharts/sonarqube --set ingress.enabled=true --set ingress.hosts[0].name=sonarqube.xxx.org

Nach dem Download dauer es jedoch einen kleinen Augenblick bis Sonarqube gestartet ist. Es muss zunächst die Datenbank initialisiert werden und der Startup nimmt einige Zeit in Anspruch.

Anmeldung

Ist die Anwendung gestartet, dann kann man sich mit den Standard admin und admin Credentials anmelden.

Krew

Krew ist ein Tool für die Verwaltung von Plugins für kubectl. Es gibt bereits mehr als 100 Plugins für Krew. Mit Krew lassen sich die Plugins stöbern, installieren und aktuell halten.

Installation von Krew

Die Installation ist relativ einfach. Es muss nur das Paket kubectl-krew installiert werden.

yay -S --noconfirm kubectl-krew

Pfade konfigurieren

Um die Installation abzuschließen, müssen die Pfade noch angepasst werden. Mit einem Texteditor die ~./bashrc bearbeiten. Es muss der Pfad ~/.krew/bin mit aufgenommen werden.

export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"

Plugin Index aktualisieren

Bevor man die Lister der Plugins anschauen kann, muss zuvor der Index lokal aktualisiert werden. Hierfür muss man krew update ausführen.

 k krew update

Tipp: Ich habe immer ein Alias k für kubectl auf meinen Maschinen, sodass die Verwendung einfacher ist.

Krew Search – Die Liste der Plugins anzeigen

Mit krew search kan man die Liste der verfügbaren Plugins für kubectl ausgeben lassen.

Installation eines Plugins

Als Beispiel verwende ich hier das Plugin rolesum. Rolesum gibt auf der Konsole eine Übersicht der Rollen zu einem Serviceaccount aus.

k krew install rolesum

Nach der Installation kann man rolesum verwenden.

k rolesum kubeapps-admin

Upgrade – Die installierten Plugins aktuell halten

Mit krew upgrade kann eine Aktualisierung der installierten Plugin durchgeführt werden.

KubeApps

Mit Kubeapps lassen sich Anwendungen per Operator oder Helm Chart sehr schnell und einfach deployen. Es können beliebige Repository hinzugefügt werden, sodass sich schnell eine Vielzahl von Anwendungen deployen lassen.

Vorteile von Kubeapps

Einer ger großen Vorteile von Kubeapps ist, dass man eine Basiskonfiguration von Helm Charts per UI vornehmen kann. Die Values werden dann im Chart entsprechend gesetzt. Das macht es gerade auch für Anfänger einfacher einen Überblick über die unzähligen Parameter zu gewinnen.

Installation

Die Installation von Kubeapps geschiet seinerseits wiederum der Helm Chart. Besondere Voraussetzungen sind nicht zu erfüllen.

helm install kubeapps --namespace kubeapps bitnami/kubeapps --set ingress.enabled=true --set ingress.hostname=kubeapps.xxx.org

Für eine Installation empfehle ich immer mit Ingress zu arbeiten. Dazu muss ein Reverse Proxy vorhanden sein, der die Anfragen weiterleitet und ein entsprechend konfigurierter DNS. Auch ein Zertifikat z.B. mit DuckDNS beseitigt durch TLS viele Probleme.

Kubernetes mit K3D

Mit K3D lässt sich schnell ein Cluster aufsetzen. Wer also noch keinen hat, der sollte hier nicht zurückschrecken. Es lässt sich mit K3D schnell auch auf älterer Hardware ein Cluster aufsetzen. Es lohnt sich.

Serviceaccout für den Cluster-Admin erstellen

Damit man nun mit Kubeapps Anwendungen deployen kann, benötigt man ein Token. Diese sollte zu einem Serviceaccout mit cluster-admin Rechten gehören. In dem Artikel Cluster-Admin Serviceaccount beschreibe ich wie das geht.

Mit dem nun erzeugten Token könnt ihr euch nun in Kubeapps anmelden und das Erste Deployment vornehmen.

Einen Cluster Admin Account erstellen

Hat man K3d aufgesetzt, so braucht man oftmals noch ein Token um zum Beispiel den Cluster über dritte Anwendungen zu administrieren.

Einen Kubernetes Serviceaccount erstellen

Zunächst benötigen wir einen Serviceaccout. Diesen kann man kubectl create serviceaccout einfach erstellen. Es werden keine weiteren Parameter benötigt.

kubectl create serviceaccount admin

RBAC – Clusterrolebinding

In Kubernetes werden die Berechtigung über RBAC gesteuert. Es gibt hier 2 Arten der Bindung. Eine Bindung die eine Rolle innerhalb eines Namespaces zuweist oder halt eben das Clusterrolebinding welches die Rolle Clusterweit zuweist.

Das Clusterrolebinding wird mit kubectl create clusterrolebinding kreiert. Als Parameter sind ein Name, die Rolle (hier also cluster-admin) und der zuvor erstellte Serviceaccountname nötig.

kubectl create clusterrolebinding admin --clusterrole=cluster-admin --serviceaccount=default:admin

Das Token extrahieren

Jetzt wird es etwas komplizierter, da wir zunächst den Namen des Serviceaccount benötigen. Dann können wir das Token mit kubectl get secret aus dem Serviceaccount auslesen.

Namen des Serviceaccount ermitteln

 kubectl get serviceaccount admin -o jsonpath='{range .secrets[*]}{.name}{"\n"}{end}' | grep kubeapps-operator-token
 admin-token-bfj2q

Das Base64 kodierte Token auslesen

Den Names des Serviceaccounts verwenden wir nun, um das Base64 kodierte Token mit der Hilfe von go-template zu dekodieren.

kubectl get secret admin-token-bfj2q -o go-template='{{.data.token | base64decode}}'

Bash Skript

Hier habe ich das Ganze als kurze Bash Skript zusammengefasst. Es benötigt nur einen Parameter. Es muss der Name für den Cluster Administrator angegeben werden.

#!/bin/bash

if [ $# -eq 0 ]; then
  echo "give $0 a parameter name" 
  exit
fi

NAME=$1

kubectl create serviceaccount $NAME
kubectl create clusterrolebinding $NAME --clusterrole=cluster-admin --serviceaccount=default:$NAME
kubectl get secret $(kubectl get serviceaccount $NAME -o jsonpath='{range .secrets[*]}{.name}{"\n"}{end}' | grep $NAME-token) -o go-template='{{.data.token | base64decode}}' && echo

MDV

Das kleine Programm mdv kann Markdown auf der Konsole rendern, sodass ein vereinfachtes lesen gegenüber des Quelltextes auf der Konsole möglich wird.

Installation

Der Terminal Markdown Viewer lässt sich einfach per yay aus dem AUR Repository installieren.

 yay -S --noconfirm terminal_markdown_viewer

Markdown anzeigen

Das Programm befindet sich nach der Installation in /usr/bin/mdv und steht daher mit dem Paket pyhton-markdown im Konflikt.

Mit Scrolling

Man kann den Output von mdv nach less pipen, aber dann werden in der Standardeinstellung die Escape Colorsequenzen nicht angewendet und man hat nur Zeichensalat. Damit less nun diese interpretiert, muss der Parameter -r angegeben werden. Daraus ergibt sich nun folgender Aufruf von mdv:

mdv readme.md | less -r

Eine Vereinfachung ist, wenn man sich einen simplen Wrapper dafür anlegt. Dieser muss in dem ausführbaren Pfad liegen und vor /usr/bin eingebunden sein.

#!/bin/bash
mdv $0 | less -r

Midnight Commander

Mit F3 kann man im MC Dateien anschauen. Die Dateierweiterungen lassen sich konfigurieren, sodass man für die Endung .md für Markdown Dateien einen neuen externen Viewer definieren kann.

Hier kommt nun wieder less -R ins Spiel. Definiert man folgenden Regulären Ausdruck in der Datei ~/.config/mc/mc.ext welche sich über Befehl -> Erweiterungsdatei bearbeiten editieren lässt:

# Markdown
regex/i/\.md?$
    View=/usr/bin/mdv %f | less -R

dann kann man mit F3 auf die Markdown Datei die sich im Hintergrund bei MC anzeigen lassen.

Speicherung der Credentials für GIT auf der Komandozeile

Oft ist es so, dass man auf einem Server (wo man nur die Kommandozeile zur Verfügung hat) sehr mühselig jedes Mal beim Pushen oder Pullen die Credentials einzugeben. GIT kann diese dauerhaft speichern, was den Komfort deutlich erhöhen kann. Ob dieses zu einem Sicherheitsrisiko führt, ist hier nicht Thema und wird nicht weiter betrachtet.

File Storage einrichten

git config --global credential.helper 'store --file ~/.my-credentials'