Integrarea Kubernetes cu OpenID Connect prin fluxul Resource Owner

  • 5 minute de citit
  • 12 exemple de cod
  • 8 imagini

Disponibil și în English

Pe această pagină

Salut 😄,

În acest articol, voi demonstra cum să configurezi Kubernetes (minikube) ca să folosească OpenID Connect ca strategie de autentificare.

Vom acoperi fluxul Resource Owner Password. Alege tu fluxul de autentificare potrivit nevoilor aplicației tale.

Pentru a alege fluxul, consultă această diagramă:

Ține cont că fluxul Client Credentials nu este suportat de Kubernetes. Conform documentației oficiale:

„Pentru a identifica utilizatorul, autentificatorul folosește id_token-ul (nu access_token-ul) din răspunsul de token OAuth2 ca bearer token. “

Deoarece fluxul Client Credentials returnează doar un access_token, nu vom putea folosi acest flux cu Kubernetes.

Configurarea clientului OAuth

Ca să experimentăm cu aceste concepte, avem nevoie de o aplicație OAuth. Prefer să folosesc Auth0, ca toată lumea să poată urmări gratuit.

  1. Creează o aplicație de tip Regular Web Application

2. Deschide aplicația nou creată, mergi la Settings, derulează în jos și apasă pe advanced settings. Pe tab-ul Grant Types, apasă pe elementul Password și apoi pe Save.

3. Mergi la Authentication -> Database și creează o conexiune nouă de tip database. Am numit conexiunea Username-Password-Authentication.

4. Mergi la Settings -> Tenant și, în secțiunea API Authorization Settings, setează numele conexiunii Default Directory la Username-Password-Authentication

5. Mergi la User Management -> Users și creează un utilizator nou. L-am numit [email protected] și i-am dat următoarea parolă: Pa27rgN9KneN

Apoi, apasă pe utilizator și setează adresa lui de email ca verificată.

Configurarea Minikube

Putem rula Kubernetes local pe calculatoarele noastre folosind Minikube.

Dacă este prima dată când te joci cu Minikube, urmează instrucțiunile de instalare de la: și pornește clusterul cu minikube start.

După ce clusterul a pornit, oprește-l cu minikube stop. Acest pas este necesar, altfel vei întâmpina erori la următoarea comandă.

Acum, să-l pornim cu următoarea comandă:

 minikube start --extra-config=apiserver.authorization-mode=RBAC --extra-config=apiserver.oidc-issuer-url="https://xxx.auth0.com/" --extra-config=apiserver.oidc-client-id=1RaJmjhjaapNLGXQjcYViQ15ZYzZoZdL --extra-config=apiserver.oidc-username-claim=email

--extra-config=apiserver.oidc-issuer-url= trebuie să fie egal cu domeniul tău Auth0; trebuie să înceapă cu https:// și să se termine cu /.

--extra-config=apiserver.oidc-client-id= va conține Client ID-ul clientului OAuth.

--extra-config=apiserver.oidc-username-claim=email va fi setat la email, deoarece vrem să mapăm adresa de email a utilizatorului Auth0 [email protected] la un utilizator pe care îl vom crea în Kubernetes.

Auth0 va returna un id_token de forma Header.Payload.Signature. În cazul nostru, va avea un payload cu câmpul email egal cu adresa de email a utilizatorului.

Următorul token nu include câmpul email, dar include câmpul name; în acest caz, putem folosi name ca apiserver.oidc-username-claim.

Pentru a te juca cu JWT-uri, vizitează:

Dacă clusterul tău Minikube a pornit, ar trebui să vezi următorul output:

😄 minikube v1.20.0 on Microsoft Windows 10 Home 10.0.19042 Build 19042✨ Using the docker driver based on existing profile👍 Starting control plane node minikube in cluster minikube🚜 Pulling base image …🔄 Restarting existing docker container for "minikube" …🐳 Preparing Kubernetes v1.20.2 on Docker 20.10.6 …▪ apiserver.authorization-mode=RBAC▪ apiserver.oidc-issuer-url=https://xxx.auth0.com/▪ apiserver.oidc-client-id=1RaJmjhjaapNLGXQjcYViQ15ZYzZoZdL▪ apiserver.oidc-username-claim=email🔎 Verifying Kubernetes components…▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5🌟 Enabled addons: storage-provisioner, default-storageclass🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

Felicitări că ai ajuns până aici! 🎉✨

Următorul pas este să mapăm utilizatorul [email protected] la un rol de admin în interiorul clusterului Kubernetes.

Deschide editorul tău de cod preferat, creează clusterrole.yaml și lipește următorul conținut:

kind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1metadata:  name: admin-rolerules:- apiGroups: ["*"]  resources: ["*"]  verbs: ["*"]

Apoi, creează clusterrolebinding.yaml și lipește:

kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata:  name: admin-bindingsubjects:- kind: User  name: [email protected]roleRef:  apiGroup: rbac.authorization.k8s.io  kind: ClusterRole  name: admin-role

Aplică rolul și legarea rolului în minikube:

kubectl apply -f clusterrole.yamlkubectl apply -f clusterrolebinding.yaml

Super, acum am interacționat cu clusterul minikube folosind un certificat client, o cheie și utilizatorul minikube.

PS C:\Users\denis\Downloads> cat C:\Users\denis\.kube\configapiVersion: v1clusters:...users:- name: minikube  user:    client-certificate: C:\Users\denis\.minikube\profiles\minikube\client.crt    client-key: C:\Users\denis\.minikube\profiles\minikube\client.key

Apoi, vom actualiza kubeconfig ca să trecem la utilizatorul [email protected].

Actualizarea kubeconfig

Fișierul kubeconfig se află de obicei în directorul ~/.kube/config și conține informații despre clustere și utilizatori.

Ca să trimitem cereri către minikube cu utilizatorul [email protected], mai întâi trebuie să luăm id_token-ul:

curl --request POST \  --url https://xxx.auth0.com/oauth/token \  --header 'Content-Type: application/x-www-form-urlencoded' \  --data grant_type=password \  --data client_id=1RaJmjhjaapNLGXQjcYViQ15ZYzZoZdL \  --data client_secret=ZzxjjnOfxIj-__pUu7HCRNC9qknS5jYxOsTxd5Koz1uH7AmeXxtRClwlEW6WJLav \  --data [email protected] \  --data 'password=Pa27rgN9KneN'{  "access_token": "yepJxATWNN2hCZUmP4H4BzBaLycsUsJw",  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1FSkVNRVE1TWtFMlFrUTRNVEZFTWpZNE5qSkRORFkxTURBMk16bENORVJET1VNelJUSTBPUSJ9.eyJuaWNrbmFtZSI6Im51Y3VsYWJzLWt1YmUiLCJuYW1lIjoibnVjdWxhYnMta3ViZUBudWN1bGFicy5kZXYiLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzQ1NjMxNzQxNjU0ODViYWE1NDBjOTBkODc2MzAyN2I_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZudS5wbmciLCJ1cGRhdGVkX2F0IjoiMjAyMS0wNS0xM1QxOTo1Nzo0Mi42ODZaIiwiZW1haWwiOiJudWN1bGFicy1rdWJlQG51Y3VsYWJzLmRldiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9kbnV0aXUtZGV2LmV1LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHw2MDlkNzlkMjk2OWU3MjAwNjhhNmFhNjciLCJhdWQiOiIxUmFKbWpoamFhcE5MR1hRamNZVmlRMTVaWXpab1pkTCIsImlhdCI6MTYyMDkzNTg2MiwiZXhwIjoxNjIwOTcxODYyfQ.avnUJv2aM0vpTkcoGiO54N4y765BxpXHV2alYAVXCpFeaNI2ISW-lW0sFUFrU3w35oM4p2Xh2tlMmVoQplSvJL0mJ9qZObMDvdqijFGdUbYN3XnD2F0kI5CCwshGhP59cbS_gdXIwwz3SOwDKKjGvacvEPcOofmhcBxNVW16qP7GS2JaAnrbGygCpj6AOyRcCkAL-jz0rxQCPrwZ5i1E4ofsbH0H8cVprYYazBpNRKPWcadcMqvGAXrtkrkSzHvrmTbQNaXi2rUiDzepDeJvciYJsNefLGmW2iStpB4KuN9M0wpXdV2PAd6lMYAd3sYSn_4NYLQKEbtmL5Sp1lkxow",  "scope": "openid profile email address phone",  "expires_in": 86400,  "token_type": "Bearer"}

Ține cont că token-ul expiră în 86400 de secunde, adică 24 de ore, deci îl putem folosi doar în acest interval. Simte-te liber să decodezi token-ul pe jwt.io ca să-i inspectezi conținutul. 😀

Ca să-i spunem kubectl să folosească id_token-ul pe care tocmai l-am obținut, trebuie să-l actualizăm după cum urmează:

apiVersion: v1 clusters:

  • cluster: certificate-authority: C:\Users\denis.minikube\ca.crt server: https://127.0.0.1:56840 name: minikube contexts:
  • context: cluster: minikube namespace: default user: [email protected] name: minikube current-context: minikube kind: Config preferences: {} users:
  • name: minikube2 user: client-certificate: C:\Users\denis.minikube\profiles\minikube\client.crt client-key: C:\Users\denis.minikube\profiles\minikube\client.key
  • name: [email protected] user: auth-provider: name: oidc config: client-id: 1RaJmjhjaapNLGXQjcYViQ15ZYzZoZdL client-secret: ZzxjjnOfxIj-__pUu7HCRNC9qknS5jYxOsTxd5Koz1uH7AmeXxtRClwlEW6WJLav idp-issuer-url: https://xxx.auth0.com/ id-token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1FSkVNRVE1TWtFMlFrUTRNVEZFTWpZNE5qSkRORFkxTURBMk16bENORVJET1VNelJUSTBPUSJ9.eyJuaWNrbmFtZSI6Im51Y3VsYWJzLWt1YmUiLCJuYW1lIjoibnVjdWxhYnMta3ViZUBudWN1bGFicy5kZXYiLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzQ1NjMxNzQxNjU0ODViYWE1NDBjOTBkODc2MzAyN2I_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZudS5wbmciLCJ1cGRhdGVkX2F0IjoiMjAyMS0wNS0xM1QxOTo1Nzo0Mi42ODZaIiwiZW1haWwiOiJudWN1bGFicy1rdWJlQG51Y3VsYWJzLmRldiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9kbnV0aXUtZGV2LmV1LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHw2MDlkNzlkMjk2OWU3MjAwNjhhNmFhNjciLCJhdWQiOiIxUmFKbWpoamFhcE5MR1hRamNZVmlRMTVaWXpab1pkTCIsImlhdCI6MTYyMDkzNTg2MiwiZXhwIjoxNjIwOTcxODYyfQ.avnUJv2aM0vpTkcoGiO54N4y765BxpXHV2alYAVXCpFeaNI2ISW-lW0sFUFrU3w35oM4p2Xh2tlMmVoQplSvJL0mJ9qZObMDvdqijFGdUbYN3XnD2F0kI5CCwshGhP59cbS_gdXIwwz3SOwDKKjGvacvEPcOofmhcBxNVW16qP7GS2JaAnrbGygCpj6AOyRcCkAL-jz0rxQCPrwZ5i1E4ofsbH0H8cVprYYazBpNRKPWcadcMqvGAXrtkrkSzHvrmTbQNaXi2rUiDzepDeJvciYJsNefLGmW2iStpB4KuN9M0wpXdV2PAd6lMYAd3sYSn_4NYLQKEbtmL5Sp1lkxow

    Notă: Autentificarea nu va funcționa dacă utilizatorul nu are adresa de email verificată: E0513 20:03:32.089659 1 authentication.go:53] Unable to authenticate the request due to an error: [invalid bearer token, oidc: email not verified] trebuie să creezi alt utilizator sau să marchezi adresa de email a utilizatorului ca verificată din interfața Auth0.

Ca să verifici că autentificarea funcționează, salvează fișierul kubeconfig și rulează:

PS C:\Users\denis\Downloads> kubectl get servicesNAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGEkubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   33m

Dacă modifici un singur byte din id-token, autentificarea nu va mai funcționa.

✨ Am interacționat cu succes cu Minikube folosind utilizatorul nostru Auth0. ✨

Reîmprospătarea token-ului

Deoarece fluxul Resource Owner nu returnează un refresh_token, plugin-ul oidc de autorizare pentru kubectl nu va putea reîmprospăta token-ul, deci este nevoie de o reîmprospătare manuală.

Documentația Kubernetes oferă o soluție pentru asta: ExecCredentials. Poți folosi un plugin go existent sau poți scrie tu un program care se execută și afișează pe stdout un obiect ExecCredential, care arată așa:

{  "apiVersion": "client.authentication.k8s.io/v1beta1",  "kind": "ExecCredential",  "status": {    "token": "my-bearer-token"  }}

Dacă este furnizat un expirationTimestamp împreună cu token, atunci kubectl va păstra token-ul în cache până expiră; altfel, dacă expirationTimestamp lipsește, kubectl va folosi token-ul până când serverul răspunde cu 401.

Un fișier kubectl făcut pentru un scenariu ExecCredentials ar arăta așa:

<pre class="wp-block-code">```users:- name: [email protected]  user:    exec:      apiVersion: "client.authentication.k8s.io/v1beta1"      command: "my-program-that-prints-an-credential"      args:      - "print-token"

```

Mulțumesc pentru lectură 📚!

Referințe:

Fotografia de copertă a articolului de 贝莉儿 DANIST.