← Übersicht  ·  Skripte & Dateien  ·  cloud-host · OpenShift · Fundament

Project, oc und ServiceAccount in OpenShift

Die Zeichnungen dazu stehen hier bei den jeweiligen Schritten. Hier spielst du die drei Fundament-Bausteine von Hand durch: ein oc new-project live verfolgt, wie viel mehr dabei entsteht als ein blosser Namespace – dann die API selbst erkunden, ohne Doku – und zuletzt eine eigene ServiceAccount anlegen, binden und ihr Token prüfen. EX280-relevant.

Stand: 4. September 2026 OKD 4.x / OCP 4.x ns library

Voraussetzung: ein Cluster mit oc login. Diese Anleitung legt selbst ein frisches Project an, du brauchst also nichts vorzubereiten.

1 Project vs Namespace

Ein Namespace ist in reinem Kubernetes nur eine Hülle. Ein Project ist dieselbe Hülle plus Selbstbedienung.

Ein Namespace (Kubernetes) ist nur ein Namensraum mit RBAC- und Quota-Grenze. Ein Project ist derselbe Namespace – nur angelegt über einen Weg, der gleich Rollen, ServiceAccounts und SCC-Annotationen mitbringt (eine Annotation ist eine an ein Objekt geheftete Notiz, die Werkzeuge lesen).

Namespace – plain Kubernetes · Namespace für Objektnamen · Grenze für RBAC · Grenze für ResourceQuota · ein ServiceAccount „default“ · sonst nichts kubectl create namespace demo-fundament nur die Hülle – du bist darin nicht automatisch berechtigt oc get project und oc get namespace zeigen fast dasselbe – Project ist ein Namespace mit Annotationen. oc new-project demo-fundament Schritt 1 – der Weg, den ein Nicht-Admin gehen darf ProjectRequest das einzige Objekt, das du selbst anlegen darfst project-request-Template liegt in openshift-config, du kannst es ändern Template legt an: Namespace + RoleBinding „admin“ für dich Schritt 2 – Cluster-Controller ergänzen (nicht das Template) · ServiceAccounts default, builder, deployer + RoleBindings (system:image-builder, system:deployer) · der SCC-Allocator schreibt aufs Namespace: openshift.io/sa.scc.uid-range · .mcs · .supplemental-groups → dein Pod bekommt daraus eine zufällige hohe UID (z. B. 1000680000), keinen festen $HOME · Zugriff auf die SCC restricted-v2 (über die Gruppe system:authenticated, clusterweit) · Annotationen openshift.io/description, display-name, requester Das alles ist der Unterschied zwischen kubectl create namespace und oc new-project. NICHT automatisch da – muss ins Template oder pro Projekt · LimitRange → ohne sie starten Pods ohne CPU-/RAM-Requests · ResourceQuota → ohne sie hat das Projekt keinen Deckel · NetworkPolicy → ohne sie erreicht jeder Pod jeden (Diagramm F2) Für alle Projekte: ins project-request-Template. Projektübergreifend: ClusterResourceQuota per Label-Selektor. Befehle oc new-project demo-fundament – Projekt anlegen, du wirst admin darin oc project demo-fundament – aktuellen Namespace wechseln (schreibt in die kubeconfig) oc projects – zeigt nur, was du sehen darfst · oc get namespace (als Admin) zeigt alle oc delete project demo-fundament – Namespace geht auf Terminating, Finalizer räumen alles darin ab oc adm new-project – als Admin, umgeht Template und Selbstbedienung Im Gespräch oft gefragt · Ein Project ist keine staerkere Isolation als ein Namespace – nur Annotationen plus die automatische admin-RoleBinding. · Netz-Trennung zwischen Projekten kommt separat über NetworkPolicy – ein frisches Project ist nach außen offen. · Terminating hängt ewig = ein Finalizer klemmt. Nachsehen: oc get ns demo-fundament -o yaml, Feld spec.finalizers. · Die Rolle self-provisioner (an system:authenticated:oauth) erlaubt jedem angemeldeten Nutzer oc new-project – Admins entfernen das Binding oft. · oc get project vs oc get projects.project.openshift.io: dasselbe. Das kurze oc project ohne Argument zeigt den aktuellen.
Ein Project ist ein Namespace mit Startpaket. oc new-project schickt einen ProjectRequest durch ein änderbares Template (Namespace + deine admin-Rolle), danach hängen Controller die drei ServiceAccounts, die SCC-Annotationen und den restricted-v2-Zugriff an. LimitRange, ResourceQuota und NetworkPolicy sind nicht dabei – die gehören ins Template, wenn jedes Projekt sie haben soll.
  • ein Namespace in Kubernetes ist nur ein Namensraum mit RBAC- und Quota-Grenze plus ein default-ServiceAccount, sonst nichts
  • kubectl create namespace demo-fundament legt nur die Hülle an, du bist darin nicht automatisch berechtigt
  • oc new-project library erzeugt zuerst einen ProjectRequest, das einzige Objekt, das ein Nicht-Admin anlegen darf
  • daraufhin legt das project-request-Template den Namespace plus eine admin-RoleBinding für dich an (das Template liegt in openshift-config und ist editierbar)
  • danach ergänzen Cluster-Controller (nicht das Template) die ServiceAccounts default, builder, deployer samt ihren RoleBindings system:image-builder und system:deployer (A6)
  • Annotation: ein an ein Objekt geheftetes Schlüssel-Wert-Paar, das Werkzeuge und Controller lesen. Anders als ein label ist es nicht zum Filtern da, sondern zum Mitgeben von Information (darf lang sein, darf JSON enthalten). Merksatz: Labels sagen „wie man es findet“, Annotationen sind „Notizen dran, meist von Maschinen“
  • der SCC-Allocator schreibt drei Annotationen auf den Namespace (openshift.io/sa.scc.uid-range, .mcs, .supplemental-groups) – dein Pod bekommt dadurch eine zufällige hohe UID (etwa 1000680000) und kein festes $HOME (A7)
  • der Namespace erhält Zugriff auf die SCC restricted-v2, über die clusterweite Gruppe system:authenticated
  • weitere Annotationen: openshift.io/description, display-name und requester
  • nicht automatisch da: LimitRange (sonst Pods ohne Requests), ResourceQuota (sonst kein Deckel), NetworkPolicy (sonst erreicht jeder Pod jeden, F2)
  • wer das für alle Projekte will, trägt es ins project-request-Template ein. Über mehrere Projekte hinweg geht eine ClusterResourceQuota per Label-Selektor
  • Befehle: oc new-project library (du wirst admin darauf), oc project library (Namespace wechseln, schreibt die kubeconfig), oc projects, oc delete project library (geht in Terminating), oc adm new-project (als Admin, umgeht Template und Selbstbedienung)
  • ein Project ist keine stärkere Isolation als ein Namespace, nur ein paar Annotationen plus die automatische admin-RoleBinding
  • Netz-Trennung zwischen Projekten kommt separat über NetworkPolicy (F2) – ein frisches Project ist nach außen offen
  • bleibt der Namespace ewig in Terminating, klemmt ein Finalizer (nachsehen mit oc get ns library -o yaml im Feld spec.finalizers)
  • die Rolle self-provisioner (an system:authenticated:oauth) erlaubt jedem angemeldeten Nutzer oc new-project – Admins entfernen dieses Binding oft
  • im Projekt: oc new-project library ist der erste Schritt des Bibliothek-Deploys – danach hängt Jenkins die pipeline-SA und ihre Bindings (A5) dran und Helm installiert die Bitnami-Infra
Befehllegt andu bist danach
kubectl create namespace demo-fundamentnur den Namespacenicht automatisch berechtigt
oc new-project libraryNamespace + admin-RoleBinding + Annotationenadmin auf dem eigenen Project

oc new-project erzeugt zuerst einen ProjectRequest – das einzige Objekt, das ein Nicht-Admin anlegen darf. Das project-request-Template (liegt in openshift-config, editierbar) legt daraufhin den Namespace plus die admin-RoleBinding für dich an. Ein Project ist keine stärkere Isolation als ein Namespace – nur ein paar Annotationen plus die automatische Binding. Echte Netz-Trennung kommt separat über NetworkPolicy (F2).

2 oc new-project live verfolgen

Ein frisches Project, und alles ansehen, was dabei automatisch entsteht.

Laptop · Bash
oc new-project demo-fundament
oc whoami
oc get rolebinding -n demo-fundament
# -> admin-Binding auf dich, plus system:image-builder/system:deployer fuer builder/deployer

oc get sa -n demo-fundament
# -> default, builder, deployer - schon da, ohne dass du etwas getan hast

oc get ns demo-fundament -o yaml | grep -A5 annotations

Die ServiceAccounts kommen nicht aus dem Template, sondern von Cluster-Controllern nach der Namespace-Erstellung (A6). Wichtige Annotationen am Namespace: openshift.io/sa.scc.uid-range, .mcs, .supplemental-groups (setzt die zufällige UID für Pods, A7), dazu openshift.io/description, display-name und requester. Der Namespace bekommt automatisch Zugriff auf die SCC restricted-v2 – über die clusterweite Gruppe system:authenticated, nicht extra pro Project.

3 Was bewusst nicht entsteht

FehltFolge ohne es
LimitRangePods ohne Requests/Limits laufen unbegrenzt
ResourceQuotakein Deckel – ein Project kann den ganzen Node füllen
NetworkPolicyjeder Pod erreicht jeden – ein frisches Project ist nach außen offen (F2)

Wer das für alle Projects will, trägt es ins project-request-Template ein. Über mehrere Projects hinweg geht eine ClusterResourceQuota per Label-Selektor.

Selbstbedienung abschaltenDie Rolle self-provisioner (an system:authenticated:oauth) erlaubt jedem angemeldeten Nutzer oc new-project – Admins entfernen dieses Binding oft, damit Projects nur kontrolliert entstehen.

Bleibt ein gelöschter Namespace ewig in Terminating, klemmt meist ein Finalizer – nachsehen mit oc get ns demo-fundament -o yaml im Feld spec.finalizers.

4 oc, kubectl und die API-Gruppen

oc ist kubectl plus die OpenShift-eigenen Verben – dieselbe Binary redet auch mit reinem Kubernetes.

Dieselbe Binary, derselbe Port 6443 – nur redet oc zusätzlich mit den OpenShift-eigenen API-Gruppen und bringt Bequem-Verben mit. Die kubeconfig sagt, mit welchem Cluster und als wer.

dein Laptop oc / kubectl dieselbe REST-Schnittstelle, HTTPS die kubeconfig beantwortet drei Fragen 1 · Wohin? – cluster server: https://api.sno.<ip>.nip.io:6443 certificate-authority: das CA-Zert des Clusters damit oc weiß: das ist wirklich dieser Cluster 2 · Als wer? – user token: sha256~aBcD… – läuft ab (~24 h), dann neu oc login oder client-certificate – der system:admin-Weg geht bei jeder Anfrage als Bearer-Header mit 3 · Was gilt gerade? – context buendelt cluster + user + namespace: library current-context: zeigt auf einen davon oc project demo-fundament ändert nur den Namespace hier eine Datei hält viele Cluster · oc login legt je Cluster einen Context an getrennte Dateien: $KUBECONFIG / --kubeconfig wählt (SNO vs k3s) dieselbe Binary redet auch mit reinem k3s (biblio.yaml) – dort fehlen nur die *.openshift.io-Objekte HTTPS 6443 ein API-Server, ein Port 6443 – viele API-Gruppen core/v1 – Kubernetes-Kern Pod, Service, ConfigMap, Secret, Namespace, ServiceAccount · Pfad /api/v1 benannte Gruppen – auch Standard-Kubernetes apps/v1 Deployment · batch/v1 Job/CronJob · rbac.authorization.k8s.io Role · Pfad /apis/<gruppe>/<version> die OpenShift-Gruppen – über den aggregierten API-Server, selber Port route.openshift.io → Route · image.openshift.io → ImageStream · build.openshift.io → BuildConfig apps.openshift.io → DeploymentConfig · project.openshift.io → Project · user.openshift.io → User, Group security.openshift.io → SecurityContextConstraints · oauth.openshift.io → OAuthAccessToken diese Gruppen laufen über einen Extra-Prozess – für oc ist es eine Ressource wie jede andere CRD-gestützte Gruppen – was Operatoren mitbringen (Block D) eine CRD registriert einen Typ, dann ist kind: PostgresCluster gleichwertig zu Pod – selbe oc get-Bedienung oc api-resources listet alle Typen · oc explain route.spec.tls holt das Schema aus diesem Cluster Web-Konsole – eine eigene Route console-openshift-console.apps.<cluster> Administrator-Perspektive: Cluster, Operatoren, Nodes, Projekte, Monitoring Developer-Perspektive: ein Project, Topology-Ansicht, Add-Menue, Logs oc whoami --show-console gibt die URL · Login über denselben OAuth-Server wie oc login (A4) alles, was die Konsole tut, geht auch per oc – sie schreibt nur dieselben Objekte im Bibliothek-Alltag am häufigsten oc get pods -n library – Spalte READY lesen, nicht STATUS oc get route -n library → library / keycloak / library-frontend oc logs --previous · oc get endpoints · oc rsh · oc debug node/<n> oc get <typ> -o yaml | -o wide | -o jsonpath='{…}' oc cp: unter Git Bash :// statt :/ (Pfad-Umschreibung), unter PowerShell nicht oc ist ein Superset von kubectl: alles von kubectl, dazu oc new-app, expose, rollout, rsh, debug, adm, login, project, status. Der API-Server prüft, nimmt an, benachrichtigt – er ist der einzige Punkt, an dem Rechte (A5), SCC (A7) und Protokollierung greifen. OCP-Unterschied: keiner – oc, die API-Gruppen und die Konsole sind bei OKD und OCP identisch. Die kubeconfig-Datei – wo sie liegt und wie oc sie findet Wo? Standard: ~/.kube/config (Windows: C:\Users\<du>\.kube\config) – eine Datei, kein .yaml-Ende Projekt: ~/.kube/okd-sno-hetzner.yaml (SNO) · ~/.kube/biblio.yaml (k3s) – benannt, um mehrere auseinanderzuhalten oc login <url> -u kubeadmin -p … schreibt die Datei (clusters / users / contexts + current-context) – von Hand editierst du sie nie Wie oc sie findet (Reihenfolge): 1. oc --kubeconfig <pfad> (Flag gewinnt) → 2. $KUBECONFIG (Umgebungsvariable) → 3. sonst ~/.kube/config · jeder oc-Befehl liest sie automatisch – du „rufst sie nicht auf“
Ein Endpunkt, viele Sprachen. oc und kubectl sprechen dieselbe REST-API auf 6443; die kubeconfig liefert Adresse und Identität. OpenShift hängt seine eigenen Objekttypen (Route, ImageStream, Project, SCC …) über einen aggregierten API-Server an denselben Port, und Operatoren stecken per CRD weitere dazu. oc api-resources und oc explain zeigen, was dieser Cluster kann.
  • oc ist kubectl plus die OpenShift-Verben (new-app, expose, rollout, rsh, debug, adm) und login / project
  • dieselbe Binary redet auch mit reinem Kubernetes (k3s, biblio.yaml), dort fehlen nur die *.openshift.io-Objekte
  • ein Port 6443, viele API-Gruppen: core/v1, die benannten (apps, batch, rbac und weitere), *.openshift.io und die CRD-gestützten
  • die OpenShift-Gruppen laufen über den aggregierten API-Server, einen Extra-Prozess hinter denselben Pfaden
  • eine CRD registriert einen neuen Typ – danach ist kind: PostgresCluster gleichwertig zu Pod und wird genauso mit oc get bedient (D2)
  • oc api-resources listet alle Typen (Kurzname, apiVersion, NAMESPACED, KIND), --namespaced=false filtert auf Cluster-Objekte
  • oc explain route.spec.tls gibt die Schema-Referenz direkt aus diesem Cluster
  • eine kubeconfig besteht aus drei Teilen: Cluster (Adresse und CA), User (Token oder x509-Zertifikat) und Context (welcher Cluster, welcher Namespace)
  • im Projekt: ~/.kube/okd-sno-hetzner.yaml (SNO), ~/.kube/biblio.yaml (k3s), die Variable $KUBECONFIG wählt – oc login schreibt die Datei, oc project setzt den Namespace
  • die SNO-API (api.sno.<ip>.nip.io:6443) ist nur übers Tailnet offen (Ports 22/6443 in der Firewall zu) – ein hosts-Eintrag zeigt den Namen auf die Tailnet-IP, sonst passt der Zert-SAN nicht (Diagramm 03)
  • die Web-Konsole ist eine Route (console-openshift-console.apps.<cluster>) mit zwei Perspektiven, Administrator und Developer – Adresse mit oc whoami --show-console
  • alles, was die Konsole tut, geht auch per oc – sie schreibt nur dieselben Objekte (die Klick-Wege stehen in okd-konfiguration-nachschauen.html)
  • Bibliothek-Alltag: oc get pods -n library (Spalte READY), oc get route -n library, oc logs --previous, oc get endpoints, oc rsh <pod>, oc debug node/<name>
  • oc cp unter Git Bash braucht :// statt :/ (die Umgebung schreibt Pfade um), unter PowerShell nicht
  • OCP-Unterschied: keineroc, die API-Gruppen und die Konsole sind bei OKD und OCP identisch
oc extranew-app, expose, rollout, rsh, debug, adm, login, project – auf reinem Kubernetes (k3s) fehlen nur die *.openshift.io-Objekte, der Rest funktioniert gleich
API-Gruppenein Port 6443, viele Gruppen: core/v1, benannte (apps, batch, rbac...), *.openshift.io und CRD-gestützte
Aggregationdie OpenShift-Gruppen laufen über den aggregierten API-Server, einen Extra-Prozess hinter denselben Pfaden – für dich unsichtbar, ein oc get route sieht aus wie jeder andere Aufruf

Eine CRD registriert einen neuen Typ – danach ist kind: PostgresCluster gleichwertig zu Pod und wird genauso mit oc get bedient (D2). Genau so kommen Operatoren zu neuen Ressourcentypen.

5 Die API selbst erkunden

Ohne Doku herausfinden, was ein Cluster kann – drei Befehle.

Laptop · Bash
# alle Typen: Kurzname, apiVersion, NAMESPACED, KIND
oc api-resources
oc api-resources --namespaced=false   # nur Cluster-Objekte

# Schema-Referenz direkt aus DIESEM Cluster, nicht aus der Doku:
oc explain route.spec.tls
oc explain route.spec.tls.termination

# eine kubeconfig hat drei Teile: Cluster, User, Context
oc config view --minify
echo $KUBECONFIG

Eine kubeconfig besteht aus Cluster (Adresse und CA), User (Token oder x509-Zertifikat) und Context (welcher Cluster, welcher Namespace). oc login schreibt die Datei, oc project <name> setzt darin den Namespace.

Web-Konsole = dieselben ObjekteDie Route console-openshift-console.apps.<cluster> mit den Perspektiven Administrator und Developer tut nichts, was oc nicht auch könnte – sie schreibt nur dieselben Objekte. Adresse mit oc whoami --show-console. Die Klick-Wege stehen in okd-konfiguration-nachschauen.html.

6 Das ServiceAccount-Modell

Drei Identitätsarten, alle drei RBAC-Subjekte (A5): User (aus dem IdP, A4), Group, und ServiceAccount.

Ein Mensch meldet sich per oc login an (A4). Ein Pod kann das nicht – er braucht trotzdem eine Identität, sobald er mit dem API-Server redet. Diese Identität ist eine ServiceAccount: ein „Benutzer für Maschinen“, an den man genauso Rollen (A5) und SCCs (A7) bindet, nur ohne IdP und ohne Passwort.

Ein Pod kann sich nicht anmelden – er ist eine ServiceAccount und trägt deren Token bei jedem API-Aufruf. UserMenschen, aus dem IdP (A4) ↗ GroupSammlung von Usern ServiceAccount für Prozesse / Pods · system:serviceaccount:<ns>:<name> alle drei können Subjekteiner RoleBinding sein(A5) ↗ Namespace library default/builder/deployer: wird generiert · pipeline: du legst an (oc create sa) default builder deployer pipeline Pod catalog-7f9c… spec.serviceAccountName: default fehlt das Feld → default. Jede App-Pod hat immer eine SA. automountServiceAccountToken: false → kein Token gemountet Deploy-Pods läuft der Cluster als builder / deployer eine SA ist namespaced – sie existiert nur hier, im RBAC steht immer der volle Name system:serviceaccount:library:pipeline anlegen: oc create sa pipeline -n library projiziertes Volume im Pod /var/run/secrets/kubernetes.io/serviceaccount/ token kurzlebiges JWT (Bound Token) ca.crt traut dem API-Server-Zertifikat namespace der eigene Namespace als Text Label: wird generiert vom kubelet – nicht anfassen Client-Libs (client-go) lesen den Pfad von allein token ist ein JWT – sub = die volle SA-Kennung API-Server · 6443 Pod sendet Authorization: Bearer <token> prüft das SA-Token selbst (signiert vom Cluster) dann greift RBAC (A5) ↗ eine frische SA darf nichts – wie ein neuer User Token weg / abgelaufen → 401, wie ein falsches oc create token pipeline holt eins on demand die App ruft die API mit dem Token 1 · RBAC-Rechte (A5) per Default: keine oc adm policy add-role-to-user edit -z pipeline -n library -z = SA in diesem Namespace → RoleBinding mit der SA als Subjekt Bibliothek: pipeline bekommt admin + system:image-builder (aus A5) 2 · SCC (A7) ↗ oc adm policy add-scc-to-user anyuid -z pipeline bestimmt den erlaubten securityContext des Pods nicht die SCC des Users zählt, sondern die der SA, unter der der Pod läuft darum ist die SA der Ort für Root-Ausnahmen 3 · imagePullSecret (B10) ↗ oc secrets link pipeline <secret> --for=pull womit der Pod Images zieht die default-SA hat schon eins für die interne Registry --for=mount macht ein Secret automatisch mountbar 4 · builder / deployer builder: push in die interne Registry (system:image-builder) deployer: steuert DeploymentConfig-Rollouts (B2) beide vom Cluster je Namespace angelegt – OpenShift-eigen reines Deployment (B1) nutzt sie nicht Das Token: früher vs heute früher (bis OCP 4.15) je SA ein Dauer-Secret kubernetes.io/service- account-token in etcd, lief nie ab heute (Bound Token) kubelet holt es über die TokenRequest-API, projiziert es · ~1 h · audience-gebunden · rotiert selbst statischer Token nur für externe Nutzer (CI): oc create token pipeline --duration=8760h extern: Jenkins authentifiziert sich als system:serviceaccount:library:pipeline mit dessen Token (A5 · B11 · E1) ↗ prüfen: oc auth can-i --as=system:serviceaccount:library:pipeline list pods -n library · oc describe sa pipeline SA als OAuth-Client (selten): Annotation serviceaccounts.openshift.io/oauth-redirecturi.<name> – für Apps, die Nutzer-Login weiterreichen OCP-Unterschied: OpenShift ergänzt builder + deployer je Namespace und die OAuth-Client-Annotation – der Rest ist Kubernetes.
Ein Pod ist immer „jemand“. Diese Identität ist eine ServiceAccount. Der Pod bekommt ihr Token als projiziertes Volume (token, ca.crt, namespace), schickt es als Bearer-Header an den API-Server, und ab da entscheidet RBAC (A5). Eine frische SA darf nichts – man bindet ihr Rollen mit -z, eine SCC mit add-scc-to-user, ein Pull-Secret mit oc secrets link. Das Token ist heute kurzlebig und rotiert von selbst, der alte Dauer-Token in etcd ist Geschichte.
  • es gibt drei Identitätsarten: User (aus dem IdP, A4), Group und ServiceAccount (system:serviceaccount:<ns>:<name>). Alle drei sind RBAC-Subjekte (A5)
  • jeder Namespace bekommt automatisch default, builder, deployerbuilder und deployer sind OpenShift-eigen
  • ein Pod ohne serviceAccountName läuft als defaultjeder App-Pod hat immer eine SA
  • der Pod bekommt ein projiziertes Volume unter /var/run/secrets/kubernetes.io/serviceaccount/ mit den Dateien token, ca.crt und namespace
  • das Bound Token ist kurzlebig (etwa eine Stunde) und an eine audience gebunden – der kubelet holt es über die TokenRequest-API und rotiert es. Es wird generiert, nicht anfassen
  • früher (bis OCP 4.15) hatte jede SA ein nie ablaufendes Token-Secret (kubernetes.io/service-account-token) in etcd
  • einen statischen Token braucht man nur für externe Nutzer: oc create token <sa> --duration=<dauer> oder ein Secret von Hand (das du anlegst)
  • im Projekt: genau so ein Secret ist pipeline-token in library (Annotation kubernetes.io/service-account.name: pipeline) – Jenkins nutzt es als podman login-Passwort und in KUBECONFIG_PROD. Es verschwindet nach jedem sno.ps1 up, der Post-up-Hook legt es neu an
  • automountServiceAccountToken: false an SA oder Pod, wenn der Pod die API gar nicht ruft (die meisten App-Pods)
  • eine SA startet mit keinen Rechten – Rollen bindet man mit oc adm policy add-role-to-user edit -z <sa> -n <ns> (-z steht für ServiceAccount)
  • die SCC kommt von der SA, nicht vom Useroc adm policy add-scc-to-user anyuid -z <sa> steuert den erlaubten securityContext (A7)
  • Pull-Secret anhängen mit oc secrets link <sa> <secret> --for=pull – die default-SA hat schon eins für die interne Registry (B10)
  • builder darf in die interne Registry pushen (Rolle system:image-builder), deployer steuert DeploymentConfig-Rollouts
  • von außen meldet sich Jenkins als system:serviceaccount:library:pipeline mit dessen Token an (A5, B12, E1)
  • prüfen mit oc auth can-i --as=system:serviceaccount:<ns>:<sa>, oc describe sa <sa> oder oc create token <sa>
SAist
defaultjeder Pod ohne serviceAccountName läuft hiermit – jeder App-Pod hat immer eine SA
builderOpenShift-eigen, darf in die interne Registry pushen (system:image-builder)
deployerOpenShift-eigen, steuert DeploymentConfig-Rollouts (system:deployer)

Der Pod bekommt ein projiziertes Volume unter /var/run/secrets/kubernetes.io/serviceaccount/ mit den Dateien token, ca.crt und namespace. Das Bound Token ist kurzlebig (etwa eine Stunde), an eine audience gebunden, wird über die TokenRequest-API geholt und automatisch rotiert – nicht anfassen. Früher (bis OCP 4.15) hatte jede SA ein nie ablaufendes Token-Secret in etcd.

7 Eine ServiceAccount live anlegen

Anlegen, binden, Token prüfen – der Weg, den auch die Bibliothek-Pipeline geht.

Laptop · Bash
oc create sa demo-sa -n demo-fundament
oc describe sa demo-sa -n demo-fundament

# startet ohne jedes Recht:
oc auth can-i get pods --as=system:serviceaccount:demo-fundament:demo-sa -n demo-fundament   # no

# Rolle binden (-z = ServiceAccount im aktuellen Namespace):
oc adm policy add-role-to-user edit -z demo-sa -n demo-fundament
oc auth can-i get pods --as=system:serviceaccount:demo-fundament:demo-sa -n demo-fundament   # yes

# ein statisches Token nur fuer externe Nutzer (z.B. eine CI ausserhalb):
oc create token demo-sa -n demo-fundament --duration=1h

# Pull-Secret anhaengen:
oc secrets link demo-sa <secret> --for=pull -n demo-fundament

automountServiceAccountToken: false an SA oder Pod setzen, wenn der Pod die API gar nicht ruft – die meisten App-Pods brauchen kein Token. Die SCC kommt von der SA, nicht vom Useroc adm policy add-scc-to-user anyuid -z demo-sa steuert den erlaubten securityContext (A7).

8 Typische Fallen

Symptommeist
frisches Project ist von außen erreichbarerwartet – keine NetworkPolicy ist Vorgabe, nicht Bug (F2)
Pods laufen ohne Limits den Node vollkeine LimitRange/ResourceQuota im Project
Namespace hängt in Terminatingein Finalizer wartet – spec.finalizers prüfen
oc new-project schlägt fehl (Forbidden)die self-provisioner-Binding wurde entfernt – ein Admin muss anlegen (oc adm new-project)
ein Pod kann die API nicht erreichenfehlende RBAC-Binding auf seiner SA, nicht auf einem User – oc auth can-i --as=system:serviceaccount:... prüft es direkt

9 Wie die Bibliothek es macht

Bausteinreal
Projectoc new-project library ist der erste Schritt des Deploys – danach hängt Jenkins die pipeline-SA und ihre Bindings dran (A5), Helm installiert die Bitnami-Infra
ServiceAccountJenkins meldet sich extern als system:serviceaccount:library:pipeline an – genau so ein Secret wie pipeline-token in library ist der statische Token-Weg (Annotation kubernetes.io/service-account.name: pipeline)
Bekannte Störungdas pipeline-token-Secret verschwindet nach jedem sno.ps1 up (Snapshot-Restore) – der Post-up-Hook (sno-wsl.sh remediate) legt es automatisch neu an
kubeconfigzwei getrennte Dateien – ~/.kube/okd-sno-hetzner.yaml (SNO) und ~/.kube/biblio.yaml (k3s), $KUBECONFIG wählt

OCP-Unterschied bei allen dreien: keiner – Project-Selbstbedienung, oc/API-Gruppen/Konsole und das ServiceAccount-Modell sind Kern-OpenShift bzw. Kern-Kubernetes, identisch auf OKD und OCP.

+ Die Kurzfassung

ProjectNamespace + admin-Binding + Annotationen – keine stärkere Isolation, nur Selbstbedienung
oc= kubectl + OpenShift-Verben, redet auch mit reinem Kubernetes – api-resources/explain erkunden das Cluster-Schema live
ServiceAccountjeder Pod hat immer eine – Rechte und SCC kommen von der SA, nicht vom User