Accès au cluster et identités des applications
Pourquoi
Un cluster Kapsule ajoute une couche de droits à celles que le cours Identité et accès a posées. D'un côté, l'IAM de Scaleway décide qui peut créer, modifier, supprimer le cluster et ses pools, et qui peut télécharger de quoi s'y connecter. De l'autre, le RBAC de Kubernetes (Role-Based Access Control, contrôle d'accès par rôles) décide ce que chaque identité peut faire à l'intérieur : lire les pods de l'espace de noms signalements, supprimer un déploiement, lire un Secret. Deux systèmes, deux vocabulaires, et un raccord entre les deux que l'on configure rarement avec attention.
Pour la migration de Signalements vers Kapsule, quatre situations se présentent dans les premières semaines.
- Les développeuses et développeurs veulent voir les pods et les journaux de production, et agir librement sur un espace de préproduction, sans disposer des droits d'administration du cluster.
- Argo CD, qui déploie l'application (GitOps avec Argo CD), agit déjà dans le cluster avec ses propres droits : il n'a pas besoin d'une identité humaine.
- L'API de Signalements lit son mot de passe de base et les clés de son bucket dans Secret Manager, via External Secrets, et les nœuds tirent les images depuis
rg.fr-par.scw.cloud. Ces deux accès demandent une identité Scaleway, pas une identité Kubernetes. - Quelqu'un doit pouvoir joindre l'API du cluster depuis un poste de travail, mais pas depuis n'importe quelle adresse d'Internet.
Sans méthode, la réponse habituelle tient en un fichier : un kubeconfig d'administrateur circule entre collègues, une clé d'API personnelle est copiée dans un Secret « pour que ça marche », et la même clé sert à tout. Quand une personne quitte l'équipe, on ne sait plus quoi révoquer. Cette leçon décrit le chemin inverse : une identité par personne et par usage, des droits limités, et des clés que l'on sait faire tourner.
Les concepts
Deux couches, un point de contact
flowchart LR
P["Personne ou application"] -->|"clé d'API Scaleway"| I["IAM Scaleway<br/>permissions par projet"]
P -->|"jeton porteur"| K["API Kubernetes"]
I -->|"jeux de permissions Kubernetes*<br/>deviennent des groupes"| K
K --> R["RBAC<br/>Role, RoleBinding"]
R --> O["Ressources du cluster"]
Quand vous appelez l'API de Kubernetes avec kubectl, la requête porte un jeton porteur (bearer token) qui est la clé secrète d'une clé d'API Scaleway. Le serveur d'API de Kapsule la fait valider par l'IAM, qui répond avec l'identité (personne ou application) et ses jeux de permissions. Ceux dont le nom commence par Kubernetes sont traduits en groupes Kubernetes, que le RBAC peut ensuite référencer. Le RBAC ne connaît donc ni projets ni politiques IAM : il ne voit que des utilisateurs et des groupes.
Les trois formes d'un kubeconfig
Le fichier kubeconfig (voir le glossaire) dit à kubectl où est l'API, quelle autorité de certification lui faire confiance, et comment s'authentifier. La commande scw k8s kubeconfig get (et install, qui fusionne le résultat dans votre ~/.kube/config) propose trois méthodes, par l'argument auth-method.
| Méthode | Ce que le fichier contient | À retenir |
|---|---|---|
cli (défaut) | Aucun secret : une commande à exécuter, scw, qui fournit un jeton à chaque appel | Le fichier peut être copié sans danger, mais il exige scw et un profil configuré sur le poste |
copy-cli-token | La clé secrète du profil scw courant, en clair | Utile pour des outils sans scw (CI, conteneur) ; le fichier est alors un secret |
legacy | Le jeton d'administrateur historique du cluster | Marqué obsolète dans le code de la CLI ; hors IAM, donc invisible dans les politiques et les audits |
La méthode cli s'appuie sur le mécanisme d'exec credential de Kubernetes : kubectl lance la commande indiquée dans le fichier et lit le jeton qu'elle affiche. Le code de la CLI ajoute une précaution : si la variable d'environnement SCW_SECRET_KEY est définie au moment de la génération, il retombe sur la méthode du jeton copié et avertit que le jeton sera codé en dur dans le fichier. C'est une surprise fréquente en CI.
La méthode legacy mérite une explication, parce que la commande scw k8s cluster reset-admin-token existe encore : à l'origine, un cluster Kapsule avait un jeton d'administrateur, et le kubeconfig téléchargé le contenait. Quiconque avait ce fichier était administrateur, sans trace de qui il était. L'authentification par IAM a remplacé ce modèle, et c'est elle qu'il faut utiliser.
Les jeux de permissions Kubernetes et leurs groupes
La documentation de Scaleway donne la correspondance entre les jeux de permissions IAM et les groupes Kubernetes vus par le RBAC.
| Jeu de permissions IAM | Groupes Kubernetes obtenus | Effet |
|---|---|---|
KubernetesReadOnly | scaleway:cluster-read | Lecture de la plupart des objets du cluster |
KubernetesFullAccess | scaleway:cluster-write et scaleway:cluster-read | Écriture sur le cluster, et gestion du cluster côté API Scaleway |
KubernetesSystemMastersGroupAccess | system:masters | Super-utilisateur : le RBAC est ignoré |
Les deux premiers groupes sont liés à des ClusterRole et ClusterRoleBinding de même nom, créés par Scaleway, que vous pouvez modifier : Kapsule ne les réconcilie pas ensuite. Le troisième est la clé de secours : si une erreur de configuration vous enferme dehors, une personne ou une application qui reçoit KubernetesSystemMastersGroupAccess repasse par-dessus le RBAC. À distribuer avec une extrême parcimonie, et à retirer après usage.
Deux autres sources de groupes s'ajoutent. Chaque groupe IAM dont l'identité est membre devient un groupe Kubernetes scaleway:group:<ID du groupe> (l'identifiant, pas le nom). Et le nom d'utilisateur Kubernetes est de la forme scaleway:bearer:<ID de l'utilisateur ou de l'application>. Ces deux préfixes sont ceux que vous écrirez dans vos RoleBinding.
Comptes de service : l'identité des pods
Un pod qui doit parler à l'API Kubernetes (un opérateur, Argo CD, un contrôleur d'ingress) le fait sous un compte de service Kubernetes (ServiceAccount, voir le glossaire), un objet d'un espace de noms. Depuis Kubernetes 1.24, aucun jeton durable n'est plus créé automatiquement dans un Secret : le pod reçoit un jeton projeté, obtenu par l'API TokenRequest, lié au pod, à durée limitée et renouvelé par le kubelet. On peut en demander un pour un usage ponctuel avec kubectl create token.
Deux conséquences pratiques. D'abord, un pod qui n'appelle jamais l'API Kubernetes n'a pas à recevoir de jeton : automountServiceAccountToken: false, sur le compte de service ou sur le pod, le retire, et la documentation de Kubernetes précise que le réglage du pod l'emporte. C'est le cas de l'API de Signalements. Ensuite, ce jeton est une identité Kubernetes, valable devant l'API Kubernetes et nulle part ailleurs.
L'accès aux API Scaleway : une clé par application
Voici la limite qu'il faut regarder en face. Chez les fournisseurs qui permettent la fédération d'identité (voir le glossaire), le jeton du compte de service Kubernetes est échangé contre des identifiants cloud temporaires, et aucune clé n'est stockée. Scaleway ne propose pas ce mécanisme à la date de rédaction : les applications IAM s'authentifient par clé d'API. Un pod qui doit lire Secret Manager ou le registre doit donc détenir une clé secrète, dans un Secret Kubernetes (ou dans un fichier monté par un autre mécanisme).
On ne supprime pas ce risque, on le borne :
- une application IAM par usage, jamais la clé d'un humain, jamais une clé « tout faire » ;
- une politique limitée au projet
signalements-prod, au jeu de permissions minimal, et pour Secret Manager à un dossier de secrets (condition vue dans Secret Manager) ; - une clé à expiration, remplacée par une rotation planifiée ;
- la clé placée dans l'espace de noms de son seul consommateur, avec un RBAC qui interdit la lecture des
Secretaux autres.
Le registre privé
Les nœuds tirent les images de rg.fr-par.scw.cloud/... avec des identifiants que Kubernetes ne connaît pas. Le mécanisme standard est le secret de tirage d'image (image pull secret) : un Secret de type kubernetes.io/dockerconfigjson, référencé par imagePullSecrets dans le pod ou, mieux, dans le compte de service que le pod utilise. La documentation de Kapsule le présente ainsi et n'expose pas d'autre mécanisme ; l'identifiant est celui d'une application IAM disposant de ContainerRegistryReadOnly, et le mot de passe sa clé secrète. Le secret vit dans l'espace de noms du pod : il faut en créer un par espace.
La liste blanche de l'API
L'API d'un cluster Kapsule a une adresse publique. La liste de contrôle d'accès (ACL) du plan de contrôle limite les adresses sources autorisées. Par défaut, elle contient 0.0.0.0/0 (tout le monde). Selon la documentation, tous les clusters créés après le 8 mars 2025 disposent de la fonction ; sur un cluster plus ancien, il faut demander son activation au support. Le piège est symétrique : trop ouverte, elle ne protège rien ; trop fermée, elle coupe vos propres nœuds, qui joignent aussi l'API par leurs adresses (nous y reviendrons).
En pratique
1. Installer un kubeconfig et lire qui l'on est
Avec un profil scw configuré (clé d'API personnelle, projet signalements-prod, région fr-par) :
$ scw k8s cluster list name=lyneko-apps
$ scw k8s kubeconfig install <id-du-cluster>
$ kubectl auth whoami
install fusionne le contexte dans ~/.kube/config en méthode cli (le défaut). kubectl auth whoami affiche le nom d'utilisateur, son identifiant et ses groupes, tels que l'API les voit : c'est la première vérification à faire, parce qu'elle montre l'identité réelle, celle qui compte pour le RBAC. La documentation de Scaleway en donne un exemple : un nom scaleway:bearer:<id> et des groupes parmi lesquels scaleway:cluster-read, scaleway:group:<id> et system:authenticated.
2. Un groupe IAM pour l'équipe Signalements
On crée le groupe, on lui donne la lecture du cluster, puis on affine par espace de noms dans Kubernetes.
$ GRP=$(scw iam group create name=signalements-dev \
description="Équipe Signalements, accès Kapsule" -o json | jq -r .id)
$ scw iam policy create name=signalements-dev-kapsule \
group-id="$GRP" \
rules.0.project-ids.0="$PROJET" \
rules.0.permission-set-names.0=KubernetesReadOnly
La commande scw iam group add-member ajoute ensuite chaque personne (consultez son aide pour les arguments exacts). Chaque membre obtient scaleway:cluster-read : la lecture du cluster, selon la définition par défaut du groupe. Cette définition, citée dans la documentation, n'inclut pas les Secret dans ses ressources lisibles : un développeur ne les lira donc pas par ce chemin. C'est une propriété à garder si vous éditez le ClusterRole.
Warning
Donner KubernetesFullAccess à un groupe d'équipe le fait entrer dans scaleway:cluster-write, c'est-à-dire plus qu'il n'en faut dans la quasi-totalité des cas, et lui ouvre aussi la gestion du cluster par l'API Scaleway (suppression comprise). Réservez-le à l'équipe plateforme.
3. Accorder des droits par espace de noms
La documentation de Scaleway illustre le cas avec un Role aux jokers (apiGroups: ["*"], resources: ["*"], verbs: ["*"]). Il fonctionne, mais accorde plus qu'il n'en faut, y compris la lecture des Secret de l'espace. Kubernetes fournit des ClusterRole d'usage courant, view, edit et admin, que l'on attache à un espace précis par un RoleBinding (un RoleBinding peut référencer un ClusterRole : les droits sont alors limités à l'espace du RoleBinding). edit permet de modifier la plupart des objets d'un espace, de lire les Secret et d'utiliser les comptes de service ; il ne permet pas de modifier les rôles ni les droits.
Pour Signalements, on veut : écrire librement en préproduction, regarder sans toucher en production.
# rbac-signalements.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: equipe-edit
namespace: signalements-preprod
subjects:
- kind: Group
name: scaleway:group:<ID-DU-GROUPE-signalements-dev>
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: edit
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: equipe-view
namespace: signalements
subjects:
- kind: Group
name: scaleway:group:<ID-DU-GROUPE-signalements-dev>
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: view
apiGroup: rbac.authorization.k8s.ioLe ClusterRole view ne donne pas non plus accès aux Secret. On l'applique avec une identité administratrice, puis on vérifie ce que le groupe peut réellement faire, en usurpant (impersonation) l'identité, ce que seul un administrateur peut faire :
$ kubectl apply -f rbac-signalements.yaml
$ kubectl auth can-i delete deployments -n signalements-preprod \
--as=scaleway:bearer:test --as-group=scaleway:group:<ID>
$ kubectl auth can-i delete deployments -n signalements \
--as=scaleway:bearer:test --as-group=scaleway:group:<ID>
$ kubectl auth can-i get secrets -n signalements \
--as=scaleway:bearer:test --as-group=scaleway:group:<ID>
Les réponses attendues sont yes, no et no. Une réponse inattendue se corrige dans le RoleBinding, pas en ajoutant un autre rôle par-dessus : le RBAC additionne.
4. Des personnes par OIDC, ou par groupe IAM ?
Les groupes IAM sont la voie la plus simple, mais ils supposent que les personnes aient un compte Scaleway avec une clé d'API. Si votre organisation dispose déjà d'un fournisseur d'identité (cas de Lyneko, qui utilise Google pour l'authentification unique d'Argo CD), le cluster peut aussi faire confiance à un fournisseur OpenID Connect (voir le glossaire). Les options sont celles de la commande de mise à jour du cluster :
$ scw k8s cluster update <id-du-cluster> \
open-id-connect-config.issuer-url=https://accounts.example.com \
open-id-connect-config.client-id=<identifiant-client> \
open-id-connect-config.username-claim=email \
open-id-connect-config.groups-claim.0=groups \
open-id-connect-config.groups-prefix=oidc: \
open-id-connect-config.required-claim.0=hd=lyneko.example
D'après l'aide de la CLI, issuer-url (en https://) permet à l'API de récupérer les clés publiques de signature ; client-id est l'identifiant pour lequel tous les jetons doivent être émis ; username-claim choisit le nom d'utilisateur (hors email, il est préfixé par l'adresse du fournisseur) ; groups-claim et groups-prefix fabriquent les groupes ; required-claim impose des paires clé-valeur dans le jeton. Sur le poste, un plugin OIDC pour kubectl (le plus répandu est kubelogin, projet de la communauté) obtient le jeton par le navigateur.
Note
Le choix d'un fournisseur doit être fait avec ses limites en tête. À notre connaissance, les jetons d'identité de Google ne portent pas de revendication de groupes : on y lie donc des utilisateurs par leur adresse électronique, ce qui ne passe pas à l'échelle. Un fournisseur qui expose les groupes (Keycloak, Entra ID, Okta) se prête mieux au RBAC par groupes. Lisez la documentation de votre fournisseur avant de déclarer l'OIDC sur le cluster.
OIDC et IAM cohabitent : un administrateur peut garder son accès par IAM comme secours, et les développeurs passer par l'OIDC. Dans les deux cas, ce sont les RoleBinding qui décident.
5. Le compte de service de l'application
L'API de Signalements n'appelle pas l'API Kubernetes. Son compte de service ne doit donc rien recevoir :
apiVersion: v1
kind: ServiceAccount
metadata:
name: signalements-api
namespace: signalements
automountServiceAccountToken: false
imagePullSecrets:
- name: registre-lectureLe même objet porte la référence au secret de tirage d'image (étape 7) : tous les pods qui utilisent ce compte de service en héritent, sans que chaque manifeste ait à le répéter. Le Deployment y fait référence par serviceAccountName: signalements-api.
6. Une identité Scaleway pour External Secrets
L'application IAM, sa politique et sa clé, à créer par un administrateur, une fois :
$ APP_ESO=$(scw iam application create name=kapsule-external-secrets \
description="Lecture des secrets de signalements-prod" -o json | jq -r .id)
$ scw iam policy create name=kapsule-external-secrets \
application-id="$APP_ESO" \
rules.0.project-ids.0="$PROJET" \
rules.0.permission-set-names.0=SecretManagerReadOnly \
rules.0.permission-set-names.1=SecretManagerSecretAccess
$ scw iam api-key create application-id="$APP_ESO" \
description="External Secrets, rotation trimestrielle" expires-at=+90d -o json \
> .tmp/cle-eso.json
Ces deux jeux de permissions sont le minimum que la documentation de Scaleway indique pour External Secrets. Le fichier contient la clé : on la place immédiatement dans le cluster, puis on la détruit.
$ kubectl create namespace external-secrets
$ kubectl -n external-secrets create secret generic scw-secret-manager \
--from-literal=access-key="$(jq -r .access_key .tmp/cle-eso.json)" \
--from-literal=secret-key="$(jq -r .secret_key .tmp/cle-eso.json)"
$ shred -u .tmp/cle-eso.json
Un ClusterSecretStore référence ensuite ce secret (la documentation de Scaleway montre un SecretStore d'un seul espace de noms ; le principe est le même, voir GitOps avec Argo CD, leçon 8) :
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: scaleway
spec:
provider:
scaleway:
region: fr-par
projectId: <ID-DU-PROJET>
accessKey:
secretRef:
name: scw-secret-manager
namespace: external-secrets
key: access-key
secretKey:
secretRef:
name: scw-secret-manager
namespace: external-secrets
key: secret-keyCette clé est le secret racine du cluster : de là, tous les autres s'obtiennent. Elle n'existe nulle part dans Git (c'est le seul secret créé à la main), et c'est pour cela qu'elle est à expiration courte et qu'on prépare sa rotation dès le premier jour.
7. Le registre : un secret généré, pas écrit
Le secret de tirage pourrait être créé à la main avec kubectl create secret docker-registry, comme dans la documentation de Scaleway. Il vaut mieux le fabriquer à partir de Secret Manager, pour qu'il suive les rotations. Une application IAM kapsule-registre (ContainerRegistryReadOnly, limitée au projet) a sa clé rangée dans un secret registre/lecture ; External Secrets construit le Secret de type dockerconfigjson :
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: registre-lecture
namespace: signalements
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: scaleway
target:
name: registre-lecture
template:
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: |
{"auths":{"rg.fr-par.scw.cloud":{"username":"nologin","password":"{{ .cle }}"}}}
data:
- secretKey: cle
remoteRef:
key: name:registre/lecture
version: latest_enabledrefreshInterval fait relire le secret toutes les heures : après une rotation, le nouveau mot de passe arrive seul. Le champ key accepte plusieurs formes de référence (par identifiant ou par nom) ; vérifiez celle qu'accepte votre version d'External Secrets dans sa documentation du fournisseur Scaleway avant de la figer. Un ExternalSecret s'écrit par espace de noms : pour dix espaces, dix objets ; on les produit par un chart ou un ApplicationSet.
8. Limiter les adresses qui joignent l'API
$ scw k8s acl list cluster-id=<id-du-cluster>
$ scw k8s acl set cluster-id=<id-du-cluster> \
acls.0.ip=198.51.100.0/24 acls.0.description="Réseau de Lyneko" \
acls.1.scaleway-ranges=true acls.1.description="Nœuds et services Scaleway"
set remplace la liste ; add ajoute des règles à celle qui existe ; delete en retire une. La seconde règle autorise toutes les plages d'adresses de Scaleway (scaleway-ranges) : d'après la documentation de Scaleway sur l'isolation des clusters, les nœuds en isolation contrôlée joignent le plan de contrôle par leurs adresses publiques, et cette règle les laisse passer ; il vaut mieux commencer ainsi, puis resserrer après avoir vérifié sur un cluster d'essai ce dont vos composants ont besoin. En Terraform, la ressource scaleway_k8s_acl écrit la même chose ; la documentation du fournisseur précise qu'une ACL définie en Terraform remplace la règle 0.0.0.0/0 créée avec le cluster, et que celle-ci est recréée si on supprime la ressource.
Vérifiez en trois temps : depuis une adresse autorisée, kubectl get nodes répond ; depuis une autre (un partage de connexion mobile suffit), il échoue par un délai d'attente ; et les nœuds restent Ready pendant tout le changement.
Sous le capot
L'authentification est déléguée. Le serveur d'API de Kapsule est configuré avec un authentificateur de jetons qui interroge l'IAM de Scaleway : il reçoit la clé secrète en jeton porteur, obtient l'identité et ses jeux de permissions, et fabrique l'utilisateur Kubernetes (scaleway:bearer:...) et ses groupes. Rien n'est stocké dans le cluster : ce qui explique que le retrait d'une clé d'API ou d'une politique IAM coupe l'accès immédiatement sans toucher au cluster, et que kubectl auth whoami change dès que l'on modifie les groupes d'une identité. Ce comportement se déduit de la documentation (la suppression de la clé « invalide immédiatement » le kubeconfig) et de la forme des identités, pas d'un code que nous aurions lu côté Scaleway.
La méthode cli ne stocke aucun jeton. kubectl exécute scw, qui lit le profil, et affiche un objet ExecCredential (API client.authentication.k8s.io/v1) contenant le jeton. Le mode interactif est désactivé (Never) : la commande ne doit jamais poser de question. Un kubeconfig copié sur une machine sans scw échouera donc, c'est son message d'aide qui l'explique.
Pièges courants
Unauthorized après un changement de poste. Le contexte en méthode cli appelle scw ; sans binaire ou sans profil, kubectl affiche une erreur d'authentification du plugin d'exec, avec un message qui demande d'installer la CLI. Installez et configurez scw, ou générez un fichier copy-cli-token si le poste ne peut pas l'avoir.
Forbidden : User "scaleway:bearer:..." cannot list resource "pods". L'authentification a réussi, le RBAC refuse. Lisez le nom exact et les groupes (kubectl auth whoami), puis cherchez la liaison manquante. Un RoleBinding écrit avec le nom du groupe au lieu de son identifiant, ou sans le préfixe scaleway:group:, est la cause la plus courante.
La liste blanche trop étroite. Une ACL qui ne contient que l'adresse du bureau coupe la CI, les nœuds ou Argo CD s'il est hébergé hors du cluster. Symptôme : kubectl fonctionne depuis le bureau, mais des nœuds passent NotReady ou des pipelines échouent avec un délai d'attente. Revenez à la règle scaleway-ranges ou ajoutez les adresses manquantes.
ErrImagePull ou ImagePullBackOff avec 401 Unauthorized. Le secret de tirage est absent de l'espace de noms du pod, mal formé (serveur rg.fr-par.scw.cloud écrit avec un chemin ou un schéma), ou la clé a expiré. kubectl describe pod donne la cause exacte, dans les événements.
La rotation oubliée. Une clé expire un jour, à une heure qui n'est pas celle d'un incident choisi : External Secrets cesse de lire, les nouveaux secrets ne se créent plus, et l'on découvre le problème au premier déploiement suivant. La rotation se planifie avant l'expiration.
Sécurité
- Pas de
legacy. Unkubeconfigen méthodelegacydonne l'accès administrateur sans IAM, sans identité nominative, sans possibilité de le révoquer autrement que parreset-admin-token. Si un tel fichier existe (cluster ancien), réinitialisez le jeton et migrez les usages vers l'IAM. - Un fichier
copy-cli-tokenest un secret. Il ne se commit pas, ne se colle pas dans une conversation, ne s'embarque pas dans une image. Mettezkubeconfig*dans.gitignore. - Moindre privilège à deux niveaux. Au niveau IAM : permission minimale, projet précis. Au niveau RBAC :
vieweteditplutôt quecluster-write, par espace de noms plutôt que global. Voir moindre privilège. - Les
SecretKubernetes sont lisibles par quiconque aget secretsdans l'espace.editle permet. Dans l'espace où vit la clé racine d'External Secrets, personne d'autre que l'opérateur ne doit l'avoir. - Le journal d'audit de l'API n'est fourni, d'après la documentation de Scaleway, qu'avec un plan de contrôle dédié, dans Cockpit (leçon 7). Sans lui, la question « qui a supprimé ce déploiement ? » se résout par le journal de l'IAM et l'historique Git d'Argo CD, moins précis.
En production
Qui administre. Trois rôles suffisent la plupart du temps : plateforme (KubernetesFullAccess, très peu de personnes), équipes applicatives (KubernetesReadOnly + RoleBinding edit en préproduction et view en production), et machines (une application IAM par automatisme). Le déploiement en production passe par Argo CD, pas par kubectl : la plupart des gens n'écrivent donc jamais en production, ce qui est le but.
Rotation de la clé racine. La procédure se répète tous les trimestres : créer une seconde clé pour la même application, mettre à jour le Secret, vérifier qu'External Secrets relit (kubectl get externalsecret -A, colonne READY), puis supprimer l'ancienne. Une clé n'a jamais deux jours de vie commune de plus que nécessaire. À terme, un outil de rotation plutôt qu'un calendrier : la leçon sur les secrets du cours Gestion des secrets y revient.
Exercices
1. Lire une identité (niveau 200). kubectl auth whoami affiche Username scaleway:bearer:1234..., Groups [scaleway:group:aaaa scaleway:cluster-read system:authenticated]. Quel jeu de permissions IAM la personne a-t-elle sur le projet ? Peut-elle supprimer un déploiement dans l'espace signalements, sachant qu'un seul RoleBinding désigne scaleway:group:aaaa avec le ClusterRole view ?
Solution
Le groupe scaleway:cluster-read correspond à KubernetesReadOnly (et KubernetesFullAccess donnerait en plus scaleway:cluster-write). Il ne peut pas supprimer un déploiement : scaleway:cluster-read ne donne que get, list, watch, et view est un rôle de lecture. Seule une liaison vers un rôle qui contient delete sur deployments le permettrait. kubectl auth can-i delete deployments -n signalements le confirme.
2. Un accès pour un prestataire (niveau 300). Une prestataire doit déboguer signalements-preprod pendant deux semaines, sans voir la production ni les Secret. Concevez l'accès : IAM, RBAC, durée. Quelle erreur le modèle edit lui ferait-il commettre ?
Solution
Un groupe IAM prestataires-sig avec KubernetesReadOnly sur le projet, et une règle IAM assortie d'une condition de date (deux semaines) ; une clé d'API d'application ou de personne à expiration à la même date ; un RoleBinding dans signalements-preprod vers un rôle sur mesure : get, list, watch sur pods, pods/log, deployments, events, plus create sur pods/exec si le débogage l'exige. edit donne la lecture des Secret de l'espace, ce qu'on a exclu ; c'est l'erreur : confondre « écrire » et « déboguer ». Penser aussi à supprimer le groupe à l'échéance. Si la lecture du cluster global (cluster-read) est jugée trop large, il faut vérifier ce qu'elle expose (les noms d'espaces de noms, les nœuds) avant d'accorder.
3. La clé qui expire dimanche (niveau 300). L'application IAM kapsule-external-secrets voit sa clé expirer dimanche. Écrivez la suite de commandes de rotation, dans l'ordre, et dites ce qui se passe si l'on supprime l'ancienne clé avant de mettre à jour le Secret.
Solution
scw iam api-key create application-id=... expires-at=+90d(nouvelle clé, la précédente reste valable). 2. Mettre à jour leSecretscw-secret-manageravec la nouvelle paire (kubectl create secret ... --dry-run=client -o yaml | kubectl apply -f -). 3. Vérifier :kubectl get externalsecret -A(colonneREADYàTrue) et la date de dernière synchronisation. 4.scw iam api-key delete <ancienne>. Si l'ancienne clé est supprimée avant l'étape 2, External Secrets échoue à chaque actualisation : leSecretKubernetes déjà créé reste en place (donc l'application tourne), mais plus aucune modification de Secret Manager n'est répercutée, et tout nouveauExternalSecretreste en erreur jusqu'à la mise à jour.
Récapitulatif
- Deux couches : l'IAM de Scaleway (qui gère le cluster, qui s'y authentifie) et le RBAC de Kubernetes (ce que chacun y fait). Le raccord, ce sont les groupes que l'IAM fait apparaître :
scaleway:cluster-read,scaleway:cluster-write,system:masters, etscaleway:group:<ID>pour chaque groupe IAM. - Un kubeconfig s'authentifie par
cli(aucun secret dans le fichier,scwrequis),copy-cli-token(la clé en clair) oulegacy(jeton d'administrateur historique, à abandonner). - Les droits fins se donnent par
RoleBindingpar espace de noms, avec lesClusterRolevieweteditplutôt que des jokers ; le RBAC additionne, on vérifie aveckubectl auth can-iet--as-group. - Les personnes peuvent passer par l'OIDC du cluster (émetteur, identifiant client, revendications d'utilisateur et de groupes), si le fournisseur expose des groupes.
- Un compte de service Kubernetes est l'identité d'un pod devant l'API Kubernetes seulement ; sans besoin,
automountServiceAccountToken: false. - Pas de fédération d'identité chez Scaleway : une application IAM par usage, une politique minimale, une clé à expiration, un
Secretdans le seul espace consommateur, une rotation planifiée. - Le secret de tirage d'image (
dockerconfigjson) se fabrique depuis Secret Manager et se rattache au compte de service ; la liste d'adresses autorisées de l'API ne doit pas exclure les nœuds.
Pour aller plus loin
- La page de Scaleway sur les permissions IAM et le RBAC, avec ses exemples de groupes et la définition complète de
scaleway:cluster-read. - La documentation de Kubernetes sur le RBAC, en particulier ses bonnes pratiques et la section des rôles agrégés (
view,edit,admin). - Le cours GitOps avec Argo CD, pour les droits d'Argo CD lui-même et son authentification unique.
- La leçon 6, pour les mises à jour qui changent l'API du cluster, et la leçon 8, pour écrire en Terraform la liste d'adresses autorisées et les applications IAM.
- Les cours Sécurité de Kubernetes et Gestion des secrets, pour la politique des admissions, les modèles de menace et la rotation outillée.
Sources
- Scaleway, Setting IAM permissions and implementing RBAC on a cluster
- Scaleway, How to connect to a Kubernetes Kapsule cluster with kubectl (kubeconfig, révocation)
- Scaleway, Managing allowed IP addresses for Kubernetes products
- Scaleway, Deploying External Secrets on Kapsule
- Scaleway, Deploying an image from Container Registry (image pull secret)
- scaleway/scaleway-cli, méthodes d'authentification du kubeconfig (custom_kubeconfig.go)
- Fournisseur Terraform de Scaleway, ressource scaleway_k8s_acl
- Kubernetes, Configure Service Accounts for Pods