Zum Inhalt

Access Control

Ziel

In diesem Projekt geht es um Zugriffskontrolle und wer welche Aktionen an einem Cluster durchführen darf. Sie werden:

  • Namespaces verwenden
  • RBAC verwenden

Hilfsmittel

  • Versuchen Sie, die unten stehenden Aufgaben mit Hilfe der Folien und der Cheatsheets eigenständig zu lösen.
  • Sollten Sie dabei Probleme haben, finden Sie bei jeder Aufgabe einen ausklappbaren Block, in dem der Lösungsweg beschrieben wird.

Aufgabe 1: Namespaces

Aufgabe 1.1: Namespaces im Cluster untersuchen

  • Verwenden Sie kubectl get namespace, um einen Überblick über die Namespaces im Cluster zu erhalten

Aufgabe 1.2: Namespaces erstellen

  • Erstellen Sie die Datei namespace.yaml mit einem Namespace test
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: Namespace
metadata:
  name: test
  • Deployen Sie den Namespace
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f namespace.yaml
  • Erstellen Sie ein Deployment in dem Namespace test mit dem Namen nginx und dem Image nginx
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl create deployment nginx --image=nginx --namespace=test
  • Verwenden Sie kubectl get pods um die Pods im Namespace test zu erhalten
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl get pods --namespace=test

Aufgabe 1.3: Namespaces verstehen

Namespaces bieten Separierung und keine Isolation. Das werden wir in dieser Aufgabe anschauen.

  • Erstellen Sie einen Namespace dev mit einer YAML-Datei dev.yaml
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: Namespace
metadata:
  name: dev
  • Deployen Sie das Namespace-Manifest.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f dev.yaml
  • Erstellen Sie ein nginx Deployment im dev Namespace sowie einen Service mit einer YAML-Datei nginx-dev.yaml und folgendem Inhalt:
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: dev
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      volumes:
      - name: data
        emptyDir: {}
      initContainers:
      - name: init
        image: nginx
        command:
        - bash
        - -c
        args:
        - |
          echo "Overriding index.html file..." && echo "Hello from DEV-namespace" | tee /usr/share/nginx/html/index.html
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: data
      containers:
      - name: nginx
        image: nginx
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: data
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: dev
spec:
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  • Deployen Sie das Deployment und Service Manifest
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f nginx-dev.yaml
  • Erstellen Sie einen Namespace prod mit einer YAML-Datei prod.yaml
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: Namespace
metadata:
  name: prod
  • Deployen Sie das Namespace Manifest
  • Erstellen Sie ein nginx Deployment im prod Namespace sowie einen Service mit einer YAML-Datei nginx-prod.yaml und folgendem Inhalt:
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: prod
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      volumes:
      - name: data
        emptyDir: {}
      initContainers:
      - name: init
        image: nginx
        command:
        - bash
        - -c
        args:
        - |
          echo "Overriding index.html file..." && echo "Hello from PROD-namespace" | tee /usr/share/nginx/html/index.html
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: data
      containers:
      - name: nginx
        image: nginx
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: data
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: prod
spec:
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  • Deployen Sie das Deployment und Service Manifest
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f nginx-prod.yaml
  • Führen Sie ein exec in das dev Deployment aus und versuchen Sie auf das prod Deployment zuzugreifen
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl --namespace dev exec services/nginx -- curl http://nginx
kubectl --namespace dev exec services/nginx -- curl http://nginx.dev
kubectl --namespace dev exec services/nginx -- curl http://nginx.prod

Wie Sie sehen können, kann das dev Deployment auf das prod Deployment zugreifen. Das liegt daran, dass Namespaces eine Möglichkeit bieten, Ressourcen in einem Kubernetes-Cluster zu organisieren. Sie sind für den Einsatz in Umgebungen mit vielen Benutzern in mehreren Teams oder Projekten gedacht, bieten aber keine Netzwerkisolation. Wenn Sie die dev und prod Deployments isolieren möchten, müssen Sie Netzwerkrichtlinien verwenden.

Aufgabe 2: Kubeconfig

Aufgabe 2.1: Aktuelle Config untersuchen

  • Verwenden Sie kubectl config view um die aktuelle Kubeconfig anzuzeigen

Die Kubeconfig enthält Informationen über Cluster, Kontexte und Benutzer. Ein cluster enthält einen Namen, ein Zertifikat und die Adresse des API-Endpunkts. Ein user enthält den Benutzernamen und das Token zur Authentifizierung. Ein context ist eine benannte Referenz, der Zugriffsparameter wie Cluster, Benutzer und Namespace unter einem Namen gruppiert. Der Context bietet die Möglichkeit, um schnell und einfach zwischen Clustern, Namespaces und Benutzern durch current-context zu wechseln.

Aufgabe 2.2: Speicherort der Kubeconfig untersuchen

  • Schauen Sie sich den Wert der Umgebungsvariable $KUBECONFIG an. Diese Variable enthält den Pfad, an dem die Kubeconfig gespeichert ist.
  • Verwenden Sie kubectl --kubeconfig=/home/coder/.kube/kubeconfig config view um den Kubeconfig-Pfad explizit zu setzen.
  • Verwenden Sie kubectl --kubeconfig=/home/coder/.kube/nonexisting_file config view um zu sehen, wie eine leere Config aussehen würde.

Aufgabe 2.3: Übersicht der Config Subcommands

  • Verwenden Sie kubectl config um eine Übersicht der verfügbaren Subcommands für die Nutzung von Kubeconfig zu erhalten

Aufgabe 2.4: Context lesen

  • Verwenden Sie kubectl config get-contexts um alle verfügbaren Contexts anzuzeigen
  • Verwenden Sie kubectl config current-context um den aktuell verwendeten Context anzuzeigen

Aufgabe 2.5: Kubeconfig sichern

  • Kopieren Sie die Kubeconfig-Datei

    cp ~/.kube/kubeconfig ~/.kube/kubeconfig.bak
    

Wir werden in den folgenden Aufgaben Änderungen an der Kubeconfig-Datei vornehmen. Sollten Sie Fehler machen, können Sie die Sicherungskopie wiederherstellen.

Aufgabe 2.6: Context hinzugefügt und löschen

  • Verwenden Sie kubectl config set-context um den Namespace des aktuellen Contexts auf kube-system zu setzen
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl config set-context --current --namespace=kube-system --user=code-x-cluster-admin
  • Verwenden Sie kubectl config get-contexts um den aktuellen Context zu überprüfen
  • Führen Sie kubectl get pods aus, um zu sehen, dass der Namespace auf kube-system gesetzt wurde

  • Setzen Sie für einen Context den Namespace auf default, nutzen Sie dabei den Context-Namen

Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl config set contexts.code-x-admin.namespace default
  • Überprüfen Sie den Namespace des Contexts
  • Löschen Sie den Namespace aus einem Context mit kubectl config unset
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl config unset contexts.code-x-admin.namespace

Aufgabe 2.7: Neuen Context erstellen

  • Erstellen Sie einen neuen Context mit dem Namen my-system-context, der folgende Eigenschaften hat:
  • Cluster: code-N (Ersetzen Sie code-N durch den Namen Ihres Clusters)
  • Namespace: kube-system
  • User: code-x-cluster-admin (Ersetzen Sie code-N durch den Namen Ihres Clusters)

Hint

  • Die Workshop-Umgebung bietet nur einen einzigen Cluster und Benutzer
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl config set-context my-system-context --cluster=code-1 --namespace=kube-system --user=code-x-cluster-admin
  • Überprüfen Sie den neuen Context my-system-context
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl config get-contexts my-system-context

Aufgabe 2.8: Context Eigenschaften auslesen

  • Verwenden Sie kubectl config view um die gesamte Kubeconfig anzuzeigen
  • Lassen Sie sich die die Config als JSON anzeigen und filtern Sie nach der Server Adresse (.cluster.server)
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl config view -o json | jq -r '.clusters[] | select(.name | startswith("code-")) | .cluster.server'

Aufgabe 2.9: Kubeconfig Backup wiederherstellen

  • Stellen Sie die Sicherungskopie der Kubeconfig-Datei wieder her:
cp ~/.kube/kubeconfig.bak ~/.kube/kubeconfig

Aufgabe 3: Role Based Access Control (RBAC) (Optional)

Aufgabe 3.1: Namespace erstellen

  • Erstellen Sie einen Namespace app mit einer YAML-Datei app-namespace.yaml
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: Namespace
metadata:
  name: app
  • Deployen Sie das Namespace Manifest
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f app-namespace.yaml

Aufgabe 3.2: ServiceAccount erstellen

  • Erstellen Sie einen ServiceAccount mit dem Namen bob im Namespace app in der Datei bob.yaml
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: bob
  namespace: app
  • Deployen Sie den ServiceAccount
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f bob.yaml
  • Schauen Sie sich den ServiceAccount mit kubectl get serviceaccounts im Namespace app an
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl --namespace app get serviceaccounts

Aufgabe 3.3: Rolle erstellen

  • Erstellen Sie eine Rolle mit dem Namen pod-admin im Namespace app mit der folgenden Regel:
apiGroups: [""] # "" indicates the core API group which covers Pod resources
resources: ["pods"]
verbs:
- "create"
- "get"
- "list"
- "watch"
- "delete"
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-admin
  namespace: app
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs:
  - "create"
  - "get"
  - "list"
  - "watch"
  - "delete"
  • Deployen Sie die Rolle

Die Rolle pod-admin erlaubt es, Pods zu erstellen, anzuzeigen und zu löschen, jedoch nur im Namespace app, in dem die Rolle erstellt wurde.

  • Schauen Sie sich die Rollen im Namespace app an
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl --namespace app get roles

Aufgabe 3.4: RoleBinding erstellen

  • Erstellen Sie ein RoleBinding mit dem Namen pod-admins im Namespace app. Das RoleBinding sollte die Berechtigungen der Rolle pod-admin dem ServiceAccount bob gewähren.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-admins
  namespace: app
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: pod-admin
subjects:
- kind: ServiceAccount
  name: bob
  namespace: app
  • Deployen Sie das RoleBinding
  • Schauen Sie sich die RoleBindings im Namespace app an
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl --namespace app get rolebindings

Aufgabe 3.5: Impersonation

  • Erstellen Sie einen Pod als bob mit dem Namen nginx im Namespace app mit dem Image nginx. Um sich als ServiceAccount bob zu authentifizieren, verwenden Sie die Option --as system:serviceaccount:app:bob mit kubectl Sie können folgendes Manifest verwenden:
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: app
spec:
  containers:
  - name: nginx
    image: nginx
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f pod.yaml --as system:serviceaccount:app:bob
  • Schauen Sie sich den Pod im Namespace app als bob an
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl get pods --namespace app --as system:serviceaccount:app:bob

Aufgabe 3.6: Impersonation mit unzureichenden Berechtigungen

  • Versuchen Sie, die Ressourcen aus dem default Namespace mit dem ServiceAccount bob aufzulisten
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl get pods --namespace default --as system:serviceaccount:app:bob

Aufgabe 4: Cluster Authentifizierung (Optional)

  • Setzen Sie den Namespace in der Kubeconfig auf default
kubectl config set-context --current --namespace=default

Aufgabe 4.1: Pod erstellen

  • Erstellen Sie einen Pod mit dem folgen Manifest:
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  • Deployen Sie den Pod

Aufgabe 4.2: ServiceAccount erstellen

  • Erstellen Sie einen ServiceAccount mit dem Namen alice
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: alice
  • Deployen Sie den ServiceAccount
  • Lassen Sie sich die ServiceAccounts mit kubectl get serviceaccounts anzeigen

Aufgabe 4.3: Secret für ServiceAccount erstellen

  • Erstellen Sie ein Secret vom Typ kubernetes.io/service-account-token mit dem Namen alice
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: Secret
metadata:
  name: alice-credentials
  annotations:
    kubernetes.io/service-account.name: alice
type: kubernetes.io/service-account-token
  • Lassen Sie sich das secret mit kubectl get secret <secret> anzeigen
  • Lassen Sie sich die Details des Secrets als YAML anzeigen
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl get secret alice-credentials -o yaml

Aufgabe 4.4: JWT aus dem Secret lesen

  • Lesen Sie den data.token Abschnitt aus dem Secret und nutzen Sie base64 -d um ihn zu dekodieren
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl get secret alice-credentials -o json | jq -r '.data.token' | base64 -d

JWT Struktur

JWT-Tokens bestehen aus drei Teilen: Header, Payload und Signatur Jeder Bestandteil ist separat base64 kodiert. Die Bestandteile werden durch Punkten . verbunden. Die Signatur eines JWT wird verwendet, um zu überprüfen, dass der Inhalt des JWT gültig und vertrauenswürdig ist. Manipulation des Payloads oder Headers würde die Signatur brechen.

  • Beachten Sie die Punkte . als Trennzeichen der JWT Teile

Aufgabe 4.5: JWT dekodieren

  • Nutzen Sie das jwt Kommandozeilenwerkzeug, um den JWT aus Aufgabe 3 zu dekodieren.
kubectl get secret alice-credentials -o json | jq -r '.data.token' | base64 -d | jwt decode -

Sie sehen den Header und die Payload (Claims) des JWT, der Signaturteil überprüft aber nicht angezeigt. Wir können ablesen, dass das Token den ServiceAccount alice mit dem Secret alice-credentials im Namespace default authentifiziert oder kurz: system:serviceaccount:default:alice.

Aufgabe 4.6: Neuen Kubeconfig-Context erstellen

  • Nutzen Sie kubectl config set-credentials um einen User Abschnitt zur Kubeconfig Datei mit dem Namen alice hinzuzufügen. Nutzen Sie das JWT als Wert für das User Token.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
JWT=$(kubectl get secret alice-credentials -o json | jq -r '.data.token' | base64 -d)
kubectl config set-credentials alice --token=${JWT}
  • Nutzen Sie kubectl config set-context um einen Context Abschnitt mit dem Namen alice zur Kubeconfig hinzuzufügen:
CURRENT_CONTEXT=$(kubectl config current-context)
CURRENT_CLUSTER=$(kubectl config view | yq ".contexts[] | select(.name==\"$CURRENT_CONTEXT\") | .context.cluster")
kubectl config set-context alice --cluster=${CURRENT_CLUSTER} --user=alice --namespace=default
  • Nutzen Sie kubectl config get-contexts um zu überprüfen, ob User und Context hinzugefügt wurden

Aufgabe 4.7: Neuen Kubeconfig-Context testen

  • Wechseln Sie den Kubeconfig-Context zu alice und testen Sie den neuen Context. Nutzen Sie die Option --context=alice mit dem kubectl get pods Befehl.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl --context=alice get pods

Default permissions

Der ServiceAccount "alice" hat noch keine Rollen oder RoleBindings. Standardmäßig hat ein ServiceAccount keine Berechtigungen. Daher wird kubectl API-Anfragen ablehnen.

Aufgabe 4.8: Rolle und RoleBinding erstellen

  • Erstellen Sie eine Rolle mit dem Namen pod-admin im Namespace default mit der folgenden Regel:
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-admin
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs:
  - "create"
  - "get"
  - "list"
  - "watch"
  - "delete"
  • Deployen Sie die Rolle
  • Erstellen Sie ein RoleBinding mit dem Namen pod-admins im Namespace default. Das RoleBinding sollte die Berechtigungen der Rolle pod-admin dem ServiceAccount alice gewähren.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-admins
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: pod-admin
subjects:
- kind: ServiceAccount
  name: alice
  namespace: default
  • Deployen Sie das RoleBinding

Aufgabe 4.9: Testen der Berechtigungen

  • Testen Sie die Berechtigungen, indem Sie die Pods mit dem neuen Context auflisten
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl --context=alice get pods

Sie sollten nun die Pods im Namespace default sehen können.

Cleanup

Löschen Sie alle Objekte die Sie in diesem Hands-On erstellt haben.