TP16 : HTTPS avec cert-manager et IAM avec Keycloak (OIDC)

Deux briques transverses de toute plateforme : le chiffrement des échanges (HTTPS, certificats renouvelés automatiquement par cert-manager) et la gestion des identités et des accès (IAM) avec Keycloak, fournisseur d’identité open source qui implémente OAuth 2.0 et OpenID Connect. Sans nom de domaine public, Let’s Encrypt est impossible : vous créez votre propre autorité de certification de lab, comme le font les entreprises pour leurs services internes.


Fichiers de départ : manifests.zip

1. Objectifs pédagogiques

À l’issue du TP, vous serez capable de :

  • expliquer la chaîne de confiance TLS (CA, certificat, clé privée, SAN) ;
  • installer cert-manager, créer une CA de lab et des ClusterIssuers, obtenir des certificats automatiquement pour un Ingress ;
  • distinguer authentification et autorisation, OAuth 2.0 et OpenID Connect ;
  • configurer un realm, un client OIDC, des rôles et des utilisateurs dans Keycloak ;
  • protéger une application sans modifier son code avec oauth2-proxy, et filtrer l’accès par rôle ;
  • lire le contenu d’un jeton JWT (claims iss, aud, exp, realm_access.roles).

Les images de cette page sont des schémas explicatifs, pas des captures d’écran.

2. Environnement du TP

  • Le cluster K3s du TP14 (≥ 2 Go de RAM libres pour Keycloak), kubectl et Helm (TP15).
  • Dans /etc/hosts de la VM de lab : <IP-du-nœud> whoami.lab.local keycloak.lab.local.
  • Déployer l’application : kubectl apply -f manifests/00-app.yaml.
Terme Signification
CA Autorité de certification : signe les certificats ; on lui fait confiance, donc à ce qu’elle signe.
SAN Subject Alternative Name : noms DNS pour lesquels un certificat est valide.
Issuer Ressource cert-manager qui sait signer des certificats (autosigné, CA, ACME…).
OAuth 2.0 Protocole de délégation d’autorisation (jeton d’accès).
OpenID Connect Couche d’authentification au-dessus d’OAuth 2.0 (jeton d’identité id_token).
Realm Espace isolé de Keycloak : utilisateurs, rôles, clients.
Client Application déclarée dans le realm, qui délègue l’authentification à Keycloak.

3. Partie 1 : cert-manager et CA de lab

cert-manager : CA de lab, ClusterIssuer, Certificate, Secret TLS, Traefik cert-manager : CA de lab, ClusterIssuer, Certificate, Secret TLS, Traefik

  • Installer cert-manager avec ses CRD :

    helm repo add jetstack https://charts.jetstack.io && helm repo update
    helm install cert-manager jetstack/cert-manager -n cert-manager --create-namespace --set crds.enabled=true
    kubectl -n cert-manager get pods
  • Compléter manifests/10-issuers.yaml (émetteur ca qui utilise le Secret lab-ca), puis l’appliquer :

    kubectl apply -f manifests/10-issuers.yaml
    kubectl get clusterissuers               # READY True
    kubectl -n cert-manager get certificate lab-ca
  • Exporter le certificat public de la CA et lui faire confiance sur la VM de lab :

    kubectl -n cert-manager get secret lab-ca -o jsonpath='{.data.ca\.crt}' | base64 -d > lab-ca.crt
    openssl x509 -in lab-ca.crt -noout -subject -dates
    sudo cp lab-ca.crt /usr/local/share/ca-certificates/lab-ca.crt && sudo update-ca-certificates

    On ne diffuse jamais la clé privée de la CA (tls.key) : quiconque la possède peut émettre des certificats valides pour n’importe quel nom.

4. Partie 2 : Ingress HTTPS

  • Compléter manifests/20-ingress-tls.yaml : bloc tls et entrée websecure uniquement. Appliquer, puis observer le travail de cert-manager :

    kubectl apply -f manifests/20-ingress-tls.yaml
    kubectl -n tp16 get certificate,certificaterequest
    kubectl -n tp16 describe certificate whoami-tls
    curl -v https://whoami.lab.local 2>&1 | grep -E 'subject|issuer|SSL certificate verify'
  • Vérifier le SAN et la durée de validité (openssl s_client -connect whoami.lab.local:443 -servername whoami.lab.local </dev/null | openssl x509 -noout -text | grep -A1 'Subject Alternative').

  • Supprimer le Secret whoami-tls : que fait cert-manager ? Quel champ du Certificate règle le renouvellement anticipé (renewBefore) ?

5. Partie 3 : Déployer Keycloak

  • Créer le mot de passe administrateur, appliquer la résolution DNS interne puis Keycloak :

    kubectl create namespace keycloak
    kubectl -n keycloak create secret generic keycloak-admin --from-literal=password="$(openssl rand -base64 18)"
    kubectl apply -f manifests/05-coredns-custom.yaml   # adapter l'IP, puis redémarrer coredns
    kubectl apply -f manifests/30-keycloak.yaml
    kubectl -n keycloak rollout status deploy/keycloak
  • Se connecter à https://keycloak.lab.local (utilisateur admin, mot de passe lu dans le Secret).

6. Partie 4 : Realm, client, rôles et utilisateurs

Flux OIDC : oauth2-proxy redirige vers Keycloak, échange le code contre des jetons, vérifie le rôle Flux OIDC : oauth2-proxy redirige vers Keycloak, échange le code contre des jetons, vérifie le rôle

  • Créer le realm lab.

  • Créer les rôles de realm user, developer, admin.

  • Créer trois utilisateurs (alice → admin, bob → developer, carol → user) avec un mot de passe non temporaire.

  • Créer le client whoami :

    • Client authentication : On (client confidentiel), flux Standard flow uniquement ;
    • Valid redirect URIs : https://whoami.lab.local/oauth2/callback ;
    • onglet Client scopes → whoami-dedicated : ajouter un mapper Audience qui inclut whoami dans le claim aud ;
    • copier le Client secret (onglet Credentials).
  • Obtenir un jeton pour bob et le décoder (le flux password est utilisé ici uniquement pour l’observer ; activez temporairement Direct access grants, puis désactivez-le) :

    TOKEN=$(curl -s https://keycloak.lab.local/realms/lab/protocol/openid-connect/token \
      -d grant_type=password -d client_id=whoami -d client_secret=<secret> \
      -d username=bob -d password=<mdp> | jq -r .access_token)
    echo "$TOKEN" | cut -d. -f2 | tr '_-' '/+' | base64 -d 2>/dev/null | jq '{iss, aud, exp, preferred_username, realm_access}'
  • Consulter la découverte OIDC : curl -s https://keycloak.lab.local/realms/lab/.well-known/openid-configuration | jq .

7. Partie 5 : Protéger l’application avec oauth2-proxy

  • Créer le Secret d’oauth2-proxy et une copie du certificat public de la CA dans le namespace tp16 :

    kubectl -n tp16 create secret generic oauth2-proxy \
      --from-literal=client-secret=<secret-du-client> \
      --from-literal=cookie-secret="$(openssl rand -base64 32 | tr -- '+/' '-_' | head -c 32)"
    kubectl -n tp16 create secret generic lab-ca-public --from-file=ca.crt=lab-ca.crt
  • Compléter manifests/40-oauth2-proxy.yaml (--allowed-role=developer, --allowed-role=admin), l’appliquer, puis faire pointer l’Ingress whoami vers le Service oauth2-proxy.

  • Tester dans un navigateur (après avoir importé lab-ca.crt dans son magasin de certificats) ou avec un tunnel SSH :

    • alice et bob accèdent à whoami : repérer les en-têtes X-Auth-Request-User et X-Auth-Request-Email ;
    • carol (rôle user) reçoit un 403.
  • Se déconnecter (/oauth2/sign_out) et expliquer pourquoi la session Keycloak peut survivre (SSO).

Défi

  1. Écrire un middleware Express qui valide lui-même le jeton JWT (signature via le JWKS de Keycloak, iss, aud, exp) et protège POST /api/admin pour le seul rôle admin.
  2. Remplacer la CA de lab par un émetteur ACME pointant vers un serveur de test local (step-ca ou Pebble) pour observer le protocole ACME utilisé par Let’s Encrypt.

8. Livrables

Captures à déposer dans captures/ :

Fichier Contenu attendu
clusterissuers.png kubectl get clusterissuers et le Certificate de la CA
https-curl.png curl -v https://whoami.lab.local : certificat vérifié, émis par la CA de lab
keycloak-realm.png Realm lab avec ses rôles et ses utilisateurs
jwt-decoded.png Jeton décodé : iss, aud, exp, rôles
oauth2-allowed.png Accès autorisé (bob) avec les en-têtes X-Auth-Request-*
oauth2-403.png Accès refusé (carol)

Ainsi que les manifests complétés. Jamais la clé privée de la CA, le secret du client ou le mot de passe admin.