Introduction
Les classes de stockage dans Kubernetes définissent la manière dont les volumes persistants sont provisionnés. Elles contiennent également des paramètres sensibles pour la sécurité : informations d'identification du provisionneur, options du système de fichiers, paramètres de montage et modes d'accès qui déterminent qui peut réclamer un volume. Une classe de stockage mal configurée peut exposer des données, permettre une élévation de privilèges ou bloquer les équipes hors de leur propre stockage. Cet article présente le durcissement de la sécurité des classes de stockage avec des exemples pratiques, des commandes et des étapes de vérification. Il s'adresse aux développeurs, ingénieurs DevOps et équipes techniques de startups qui gèrent des clusters Kubernetes et doivent passer de « nous avons vu un problème » à « nous avons vérifié la correction ».
L'objectif est la sécurité opérationnelle. Nous observons avant de modifier, limitons le rayon d'impact de chaque changement, utilisons des espaces réservés au lieu de secrets réels, vérifions chaque résultat et documentons les chemins de récupération. Chaque section inclut des commandes à exécuter dans un espace de noms de test, la sortie attendue, les signaux d'échec et une décision de récupération.
Inventaire de la version et de l'environnement
Avant de durcir quoi que ce soit, connaissez la version de votre cluster, le provisionneur de stockage et le pilote CSI utilisé. Les différentes versions de Kubernetes et les pilotes CSI prennent en charge des fonctionnalités de sécurité différentes.
Exécutez d'abord cette vérification en lecture seule :
kubectl version --short
kubectl get storageclass
kubectl get csidriver
La sortie attendue de kubectl get storageclass pourrait ressembler à :
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
gp2 (default) kubernetes.io/aws-ebs Delete Immediate true 87d
fast-rbd rbd.csi.ceph.com Delete Immediate true 12d
Si votre provisionneur est un pilote CSI, vérifiez sa version et ses fonctionnalités prises en charge avec kubectl describe csidriver <nom-du-pilote>. Par exemple, kubectl describe csidriver fast-rbd peut afficher attachable: true, podInfoOnMount: true et volumeLifecycleModes: [Persistent]. Ces indicateurs affectent la sécurité de la configuration du pilote.
Le plus petit changement dans cette phase est souvent la documentation de l'environnement. Créez un fichier d'inventaire dans votre référentiel d'exploitation :
# storage-inventory.md
Version Kubernetes : 1.26.3
Provisionneurs : kubernetes.io/aws-ebs, rbd.csi.ceph.com
Pilotes CSI : ebs.csi.aws.com (v1.19.0), rbd.csi.ceph.com (v3.8.1)
Classe de stockage par défaut : gp2
Protégez cet inventaire comme tout autre document interne. Il ne contient aucun secret, mais il indique à un attaquant exactement quels pilotes de stockage cibler.
Chemin de configuration sûr
Le chemin de configuration le plus sûr est : créer une nouvelle classe de stockage plutôt que de modifier une existante, la tester dans un espace de noms dédié, puis migrer lentement les charges de travail. Ne modifiez jamais la classe de stockage par défaut en place sans un plan de restauration.
Exemple : supposons que vous souhaitiez durcir la classe de stockage gp2 pour utiliser le chiffrement par défaut. Au lieu de modifier la classe existante, créez-en une nouvelle appelée gp2-encrypted :
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp2-encrypted
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
encrypted: "true"
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
Enregistrez sous gp2-encrypted.yaml et appliquez :
kubectl apply -f gp2-encrypted.yaml
kubectl get storageclass gp2-encrypted
Sortie attendue :
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
gp2-encrypted kubernetes.io/aws-ebs Delete WaitForFirstConsumer true 5s
Testez-la maintenant dans un espace de noms non productif :
kubectl create namespace storage-test
kubectl apply -n storage-test -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-encrypted-pvc
spec:
storageClassName: gp2-encrypted
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
EOF
Vérifiez le statut du PVC :
kubectl get pvc -n storage-test test-encrypted-pvc
Sortie attendue :
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
test-encrypted-pvc Bound pvc-1a2b3c4d-... 1Gi RWO gp2-encrypted 10s
Si le PVC reste Pending, décrivez-le pour en voir la raison :
kubectl describe pvc -n storage-test test-encrypted-pvc
Les raisons courantes incluent un pilote CSI manquant, des autorisations insuffisantes ou des paramètres invalides. Corrigez la cause spécifique avant de continuer.
Une fois testé, migrez une charge de travail à la fois en modifiant son PVC pour référencer la nouvelle classe de stockage. Ne modifiez pas tous les PVC en masse en une seule fois.
Vérification et diagnostics
Le durcissement ne vaut que si vous pouvez le vérifier. Pour les classes de stockage, la vérification se fait à trois niveaux : la classe de stockage elle-même, la configuration du pilote CSI et le volume résultant.
Niveau classe de stockage
kubectl get storageclass gp2-encrypted -o yaml
Recherchez la section parameters et vérifiez les clés pertinentes pour la sécurité. Extrait attendu :
parameters:
encrypted: "true"
type: gp2
Si encrypted est absent ou défini sur "false", le volume n'est pas chiffré. Recréez la classe de stockage avec le paramètre correct.
Niveau pilote CSI
Certains pilotes CSI exposent des contrôles de sécurité via leur propre configuration, souvent stockée dans une ConfigMap ou un Secret. Pour le pilote CSI AWS EBS, vous pouvez vérifier les journaux du Pod du pilote pour les événements de provisionnement :
kubectl logs -n kube-system deployment/ebs-csi-controller -c ebs-plugin | grep -i "encrypt"
La sortie attendue peut inclure des lignes comme :
msg="Provisioning volume" volume_id="vol-0abcd1234..." encrypted=true
Si le journal affiche encrypted=false, le pilote a ignoré le paramètre. Revérifiez la classe de stockage et la version du pilote.
Niveau volume
Après qu'un PVC est lié, vérifiez les propriétés réelles du volume cloud. Pour AWS, utilisez la CLI AWS :
aws ec2 describe-volumes --volume-ids vol-0abcd1234... --query 'Volumes[0].Encrypted'
Sortie attendue :
true
Si elle renvoie false, le paramètre de la classe de stockage n'a pas pris effet. Vérifiez le nom du provisionneur et si une autre classe de stockage a été utilisée en raison de la sélection par défaut.
Les diagnostics doivent toujours être en lecture seule jusqu'à ce que vous ayez confirmé le mode de défaillance. Ne supprimez jamais un volume pour le « réinitialiser » sans d'abord capturer son état et sa politique de sauvegarde.
Modes de défaillance et récupération
Le durcissement des classes de stockage présente trois modes de défaillance courants, chacun avec un chemin de récupération spécifique.
Défaillance 1 : PVC bloqué en Pending
Cela signifie généralement que la classe de stockage ne peut pas provisionner un volume. Causes possibles : pilote CSI manquant, nom de provisionneur incorrect, RBAC insuffisant pour le provisionneur ou paramètres invalides.
Diagnostic :
kubectl describe pvc -n <namespace> <nom-du-pvc>
Recherchez des événements comme failed to provision volume with StorageClass "gp2-encrypted": rpc error: code = InvalidArgument desc = ....
Récupération :
- Vérifiez que le nom du provisionneur correspond exactement au pilote CSI.
- Vérifiez que les Pods du pilote CSI fonctionnent dans
kube-system. - Passez en revue les paramètres de la classe de stockage pour détecter les fautes de frappe.
- Si le problème vient du RBAC, vérifiez le ServiceAccount du provisionneur et ses ClusterRoleBindings.
Défaillance 2 : Volume créé mais inaccessible au Pod
Symptôme : le Pod ne démarre pas avec FailedMount ou MountVolume.SetUp failed.
Diagnostic :
kubectl describe pod <nom-du-pod>
Recherchez des événements comme MountVolume.SetUp failed for volume "pvc-..." : rpc error: code = Internal desc = ....
Récupération :
- Vérifiez que le volume est dans la même zone de disponibilité que le nœud (pour les fournisseurs cloud).
- Consultez les journaux du plugin du nœud du pilote CSI :
kubectl logs -n kube-system <pod-du-nœud-csi> -c <conteneur-du-pilote>. - Si le volume a été créé avec
ReadWriteOnce, il ne peut être monté que sur un seul nœud à la fois. Assurez-vous qu'aucun autre nœud n'essaie de le monter.
Défaillance 3 : Paramètre de sécurité ignoré silencieusement
C'est la défaillance la plus dangereuse car tout semble sain. Exemple : vous définissez encrypted: "true" mais le volume cloud n'est pas chiffré.
Diagnostic comme indiqué dans la section Vérification : comparez le YAML de la classe de stockage, les journaux du provisionneur et les attributs du volume cloud.
Récupération :
- Si un paramètre est ignoré, consultez la documentation du pilote CSI. Certains paramètres exigent des formats spécifiques ou des versions de pilote.
- Testez avec une classe de stockage minimale contenant uniquement ce paramètre. Si cela échoue toujours, le pilote peut ne pas le prendre en charge.
- Pour les volumes existants, vous ne pouvez pas activer rétroactivement le chiffrement. Vous devez créer un nouveau volume chiffré et migrer les données. Planifiez une fenêtre de maintenance.
Pièges courants et comment les éviter
Le durcissement de la sécurité échoue souvent à cause de ces erreurs récurrentes.
Piège 1 : RBAC trop large pour les classes de stockage
Les développeurs obtiennent souvent des autorisations * sur les classes de stockage, les PVC et les PV. Cela leur permet de créer des classes de stockage arbitraires avec des paramètres dangereux, de supprimer des volumes ou d'accéder aux données d'autres équipes.
Comment éviter : Utilisez le moindre privilège. Définissez des rôles qui n'autorisent que la création de PVC avec des classes de stockage spécifiques.
Exemple de Role pour un espace de noms de développeur :
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: dev
name: storage-user
rules:
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "create", "delete"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list"]
Cela permet aux développeurs d'utiliser des classes de stockage existantes mais pas de les créer ou de les modifier. La création de classes de stockage doit être une capacité réservée aux opérations.
Piège 2 : Coder en dur des secrets dans les paramètres de classe de stockage
Certaines classes de stockage acceptent des informations d'identification dans le champ parameters (par exemple, pour NFS ou le stockage d'objets). Stocker des secrets en clair dans le YAML de la classe de stockage est une fuite de données en puissance.
Comment éviter : Utilisez les Secrets Kubernetes et référencez-les via le pilote CSI. La plupart des pilotes CSI prennent en charge secretRef dans la classe de stockage ou le PVC. Par exemple, le pilote CSI Ceph utilise un secret pour le provisionneur, défini dans un Secret séparé et référencé par la classe de stockage :
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-rbd
provisioner: rbd.csi.ceph.com
parameters:
clusterID: <cluster-id>
pool: <pool-name>
csi.storage.k8s.io/provisioner-secret-name: ceph-secret
csi.storage.k8s.io/provisioner-secret-namespace: kube-system
Les informations d'identification réelles se trouvent dans ceph-secret, pas dans la classe de stockage. Protégez ce Secret avec RBAC et le chiffrement au repos (par exemple, utilisez un fournisseur KMS pour etcd).
Piège 3 : Ignorer les modes de liaison de volume
Le champ volumeBindingMode affecte la sécurité. Avec la liaison Immediate, le volume est provisionné avant qu'un Pod ne l'utilise, potentiellement dans la mauvaise zone de disponibilité ou avec une topologie incorrecte. Avec WaitForFirstConsumer, le volume n'est pas créé tant qu'un Pod n'en a pas réellement besoin, ce qui permet au planificateur de placer le volume correctement. Cependant, WaitForFirstConsumer signifie également que le volume n'est exposé qu'après qu'un Pod s'y est lié, réduisant ainsi la fenêtre d'accès non autorisé.
Vérifiez votre classe de stockage :
kubectl get storageclass gp2-encrypted -o jsonpath='{.volumeBindingMode}'
Sortie attendue :
WaitForFirstConsumer
Si elle affiche Immediate, envisagez de passer à l'autre mode. La modification du mode de liaison sur une classe de stockage existante n'est pas autorisée ; créez une nouvelle classe et migrez.
Piège 4 : Négliger le RBAC et les fichiers de configuration du pilote CSI
Les pilotes CSI s'exécutent sous forme de Pods avec leur propre ServiceAccount et nécessitent souvent de vastes autorisations au niveau du cluster. Les attaquants qui compromettent un pilote CSI peuvent exfiltrer des données. Durcissez les pilotes CSI en :
- Passant en revue leurs autorisations RBAC et en supprimant celles qui ne sont pas documentées comme requises.
- Exécutant les conteneurs du pilote CSI en tant qu'utilisateur non root avec
runAsNonRoot: true. - Restreignant l'accès réseau du pilote aux seuls points de terminaison nécessaires.
- Gardant le pilote à jour ; les correctifs de sécurité sont fréquents.
Vérifiez le contexte de sécurité du Pod du pilote CSI :
kubectl get pod -n kube-system -l app=ebs-csi-controller -o jsonpath='{.items[0].spec.containers[*].name}{"\n"}'
kubectl get pod -n kube-system -l app=ebs-csi-controller -o jsonpath='{.items[0].spec.securityContext}{"\n"}'
Si le contexte de sécurité est vide ou n'inclut pas runAsNonRoot: true, ajoutez-le via un patch ou mettez à jour le Deployment.
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle pour créer une revue de sécurité reproductible pour les classes de stockage. Chaque élément a un propriétaire et une fréquence de revue.
| Élément de la liste de contrôle | Propriétaire (une seule personne responsable) | Fréquence de revue | Commande de vérification ou signal |
|---|---|---|---|
| Inventorier toutes les classes de stockage et provisionneurs | Priya Shah, responsable plateforme | Mensuelle | kubectl get storageclass |
| S'assurer que la classe de stockage par défaut respecte la politique de chiffrement | Alex Chen, ingénieur sécurité | Mensuelle | kubectl get sc <default> -o yaml | grep encrypted |
| Examiner le RBAC des classes de stockage pour le moindre privilège | Marcus Webb, DevOps | Trimestrielle | kubectl auth can-i --list --as=system:serviceaccount:dev:developer |
| Vérifier les versions des pilotes CSI et les avis de sécurité | Priya Shah, responsable plateforme | Hebdomadaire | kubectl get csidriver -o yaml | grep image |
| Tester une nouvelle classe de stockage dans un espace de noms bac à sable avant la production | Alex Chen, ingénieur sécurité | Selon les besoins | kubectl apply -f new-sc.yaml -n sandbox; kubectl get pvc -n sandbox |
| Vérifier les volumes chiffrés au niveau du fournisseur cloud | Marcus Webb, DevOps | Mensuelle | aws ec2 describe-volumes --query 'Volumes[?Encrypted==false]' |
| Rotation des secrets des classes de stockage | Priya Shah, responsable plateforme | Trimestrielle | kubectl get secret -n kube-system | grep csi |
Exécutez d'abord la liste de contrôle en mode d'audit en lecture seule. N'apportez des modifications qu'après que l'audit a confirmé une lacune.
Conclusion
La sécurité des classes de stockage est une pratique continue, pas une configuration ponctuelle. En inventoriant votre environnement, en utilisant des chemins de configuration sûrs, en vérifiant chaque changement et en planifiant les modes de défaillance, vous réduisez le risque d'exposition des données et de mauvaise configuration. Les exemples de cet article vous donnent un point de départ pour durcir vos propres clusters.
Comme prochaine étape, choisissez une vérification à faible risque dans la liste de contrôle. Enregistrez l'état actuel, exécutez la commande documentée, comparez le résultat avec le signal attendu et passez en revue les dépendances telles que les volumes persistants, les demandes de volume persistant et les pilotes CSI. Ensuite, étendez le processus au reste de vos classes de stockage. Un flux de travail fiable rend les défaillances visibles, protège les valeurs sensibles et limite les modifications à la ressource prévue.