Neuigkeiten von trion.
Immer gut informiert.

Konfiguration von Kubernetes Admission Controller

Nachdem das Konzept von Kubernetes Admission Controllern in dem Beitrag Kubernetes Admission Controller beschrieben wurde und in Beispiel für Admission Controller gezeigt wurde, wie ein Admission Controller Webhook implementiert werden kann, geht es nun darum, diesen in Betrieb zu nehmen.

Der Webhook an sich kann sowohl in dem selben Kubernetes-Cluster betrieben werden, in dem er auch verwendet wird, als auch völlig unabhängig von Kubernetes.
Damit Kubernetes bei einem so mächtigen Element sicherstellen kann, dass mit dem korrekten Webhook kommuniziert wird, muss die Verbindung TLS gesichert sein und Kubernetes dem Zertifikat vertrauen. Für den Aufbau der Trust-Chain werden entweder die vom zugrundeliegenden Betriebssystem bereitgestellten CA-Zertifikate verwendet, oder es kann in der Webhook Konfiguration explizit ein CA Zertifikat angegeben werden. Um das Beispiel möglichst vollständig nachvollziehbar zu gestalten, wird mit Hilfe der internen Certificate Authority von Kubernetes ein Zertifikat erstellt, wie in diesem Artikel beschrieben: Verwendung von Kubernetes Zertifikaten.

Wird der Webhook in Kubernetes betrieben, so muss vorher natürlich ein Container-Image gebaut werden und für das Image ein Deployment samt entsprechendem Service in Kubernetes angelegt werden.

Der Webhook für den Admission Controller wird, wenn er in Kubernetes betrieben wird, typischwerweise lediglich in einem Namespace bereitgestellt. Unabhängig davon kann der Admission Controller in beliebigen Namespaces zur Verwendung konfiguriert werden.

In der folgenden Übersicht werden die relevante Daten und die im Beispiel verwendeten Namen der zugehörigen Objekte in Kubernetes aufgeführt:

  1. Namespace: drone

  2. TLS Secret: ttlmac-service-tls

  3. Image: ttlmac

  4. Deployment: ttlmac-deployment

  5. Service: ttlmac-service (FQN ttlmac-service.drone.svc.cluster.local)

  6. Webhook: ttl-job-mutator-webhook

Ob in einem Kubernetes Cluster die Admission Controller als Feature aktiviert sind, kann durch durch die Kubernetes API abgefragt werden. Dazu werden die verfügbaren APIs abgefragt und auf den Typ admissionregistration.k8s.io geprüft.

Verfügbarkeit der Admission Registration API prüfen
$ kubectl api-versions | grep admissionregistration.k8s.io
admissionregistration.k8s.io/v1beta1

Als nächstes wird ein TLS Zertifikat für den Webhook erzeugt. Dabei ist es nicht erforderlich, dass zwingend, wie im Beispiel, die Kubernetes-CA verwendet wird, auch ein self-signed Zertifikat kann verwendet werden.
Wichtig ist dabei, dass ein SAN DNS Eintrag mit dem Wert angelegt wird, über den später der Zugriff auf den zugehörigen Service erfolgt. Allgemein ist dies dann <service>.<ns>.svc.

Erzeugung von TLS Zertifikat für Mutating Admission Controller Webhook
$ mkdir certs
$ SANS="subjectAltName=DNS:ttlmac-service.drone.svc.cluster.local,DNS:ttlmac-service.drone.svc"
$ openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr -days 365 \
 -subj "/CN=ttlmac-service.drone.svc" -addext $(printf "$SANS")
$ cat <<EOF | kubectl create -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
  name: ttlmac-service.drone.svc
spec:
  groups:
  - system:authenticated
  request: $(cat certs/server.csr | base64 | tr -d '\n')
  usages:
  - digital signature
  - key encipherment
  - server auth
EOF
$ kubectl certificate approve ttlmac-service.drone.svc
$ sleep 5
$ kubectl get csr ttlmac-service.drone.svc -o jsonpath='{.status.certificate}'  | base64 --decode > certs/server.crt
$ kubectl delete certificatesigningrequest.certificates.k8s.io/ttlmac-service.drone.svc

Nach diesen Befehlen sollten die Dateien server.crt und server.key erstellt worden sein, die das Zertifikat und den zugehörigen privaten Schlüssel beinhalten.
Damit diese für den Webhook zur Verfügung stehen, wird daraus ein Kubernetes Secret vom Typ tls erzeugt.

Kubernetes Secret mit dem TLS Material
$ kubectl -n drone create secret tls --cert=certs/server.crt --key=certs/server.key ttlmac-service-tls

Nun kann der Webhook selbst als Deployment angelegt werden. Die Schritte zur Erzeugung eines Container-Images und Bereitstellung in einer geeigneten Registry werden an dieser Stelle als bereits erledigt angesehen.

Im Deployment wird das eben erzeugte Secret referenziert, um damit der Anwendung die für die TLS Absicherung benötigten Dateien bereitzustellen.

Deployment des Webkook und Service für den Zugriff
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ttlmac-deployment
  namespace: drone
  labels:
    app: ttlmac
spec:
  selector:
    matchLabels:
      app: ttlmac
  template:
    metadata:
      labels:
        app: ttlmac
        name: ttlmac
    spec:
      containers:
      - name: ttlmac-app
        image: localhost:5000/ttl-mac:3
        env:
        - name: PYTHONUNBUFFERED
          value: "1"
        ports:
        - containerPort: 4443
        volumeMounts:
        - name: tls
          mountPath: /app/certs
          readOnly: true
      volumes:
      - name: tls
        secret:
          secretName: ttlmac-service-tls
---
kind: Service
apiVersion: v1
metadata:
  name: ttlmac-service
  namespace: drone
spec:
  selector:
    app: ttlmac
  ports:
  - protocol: TCP
    port: 443
    targetPort: 4443
  type: ClusterIP

Kubernetes muss nun noch zur Verwendung des Admission Controllers konfiguriert werden. Dies geschieht durch eine MutatingWebhookConfiguration oder eine ValidatingWebhookConfiguration.
Für die Konfiguration wird jedoch noch das CA-Zertifikat benötigt, mit dem die Vertrauensbeziehung sichergestellt wird. Wird dies ausgelassen, so wird lediglich den System-Zertifikaten vertraut - was aber möglicherweise mehr sind, als erwünscht.
Ist das Zertifikat im Property caBundle spezifiziert, so muss das TLS Zertifikat genau von dieser CA signiert worden sein.

Da das Zertifikat mit der Kubernetes internen CA erstellt wurde, und diese typischerweise nicht in den Systemzertifikaten aufgeführt ist, muss das entsprechende CA-Zertifikat von Kubernetes in jedem Fall spezifiziert werden. Die Ermittlung des CA-Zertifikats kann beispielsweise mit kubectl erfolgen.

Ausgabe des Kubernetes CA Zertifikats
$ kubectl get configmap -n kube-system extension-apiserver-authentication \
  -o=jsonpath='{.data.client-ca-file}' | base64 | tr -d '\n'
LS0tLS1CRUdJTiBDRVJUSU...

Nun kann der Admission Controller konfiguriert werden. Neben dem zu verwendenden Dienst und dem anzufragenden HTTP-Path muss spezifiziert werden, bei welchen Kubernetes-API-Operationen der Admission Controller aufzurufen ist.
Da das Ziel ist, bei Jobs das Attribut ttlSecondsAfterFinished zu setzen, falls dies nicht definiert ist, wird die folgende Regel zum Aufruf verwendet:

  • operations: [ "CREATE" ] - bei neu erzeugten Objekten

  • apiGroups: ["batch"] - bei Objekten der API-Gruppe batch

  • apiVersions: ["*"] - egal, welche API Version

  • resources: ["jobs"] - bei Objekten vom Typ jobs

Damit der Mutating Admission Controller nur in einem spezifischen Namespace angewendet wird, wird dies noch über den namespaceSelector auf den Namespace mit Namen drone eingeschränkt.

Konfiguration des Mutating Admission Controllers in Kubernetes
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
  name: ttl-job-mutator-webhook
  labels:
    app: mutating-admission-webhook
webhooks:
  - name: ttl-job-mac.drone.svc.cluster.local
    clientConfig:
      service:
        name: ttlmac-service
        namespace: drone
        path: "/"
      caBundle: "LS0tLS1CRUdJTiBDRVJUSUZJQ0.....S0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="
    rules:
      - operations: [ "CREATE" ]
        apiGroups: ["batch"]
        apiVersions: ["*"]
        resources: ["jobs"]
    failurePolicy: Fail
    namespaceSelector:
      matchExpressions:
        - {key: name, operator: In, values: [drone]}
      matchLabels:
        #only-auto-ttl: enabled

Nachdem der Webhook konfiguriert wurde, kann er getestet werden. Am einfachsten geschieht dies, indem ein passender Job erzeugt wird. Im erzeugten Objekt sollte dann die durch den Mutating Admission Controller vorgenommene Veränderung sichtbar sein.

Test des Webhook mit einem Job
$ $ kubectl run demo -n drone --image=alpine --restart=OnFailure -it -- \
  /bin/sh -c "sleep 20; echo 'working'; sleep 90;"
If you don't see a command prompt, try pressing enter.
working

Falls es ein Problem mit dem Webhook gibt, so wird Kubernetes die Erzeugung des Job Objekts zurückweisen.
Dazu wurde in der Konfiguration failurePolicy: Fail hinterlegt, so kommt es nicht zu unbemerkten Fehlern.

Das erzeugte Objekt wird mit kubectl inspiziert und sollte dann das ttlSecondsAfterFinished Property aufweisen.

Ausgabe des erzeugten Objekts
$ kubectl -n drone get job demo -o yaml
...
  ttlSecondsAfterFinished: 3600
...

Falls die Ausgabe nicht das Property enthält, der Job jedoch erfolgreich erzeugt wurde, handelt es sich um eine spezielle Situation: Es wurde zwar erfolgreich der Admission Controller aufgerufen, der API Server kennt jedoch das Property ttlSecondsAfterFinished nicht. Grund dafür ist in der Regel, dass das Feature-Gate nicht aktiviert ist.

In dem Folgeartikel Kubernetes Feature Gates wird erklärt, wie das Feature Gate im Kubernetes Cluster aktiviert werden kann, damit das Attribut auch ausgewertet wird.




Zu den Themen Kubernetes, Docker und Cloud Architektur bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:

Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Zur Desktop Version des Artikels