Sécurité
Audience: Opérateur de plateforme
Modèle de menace
Les menaces à considérer se répartissent en trois catégories : un pod
compromis, un nœud compromis, et un injector compromis. Un pod compromis
peut lire ses propres variables d'environnement et /proc/<pid>/environ. En
mode legacy, cela expose les identifiants DB en clair ; en mode NRI, cela
expose des placeholders opaques avant substitution et les mêmes identifiants
en clair après substitution. Un nœud compromis avec accès root contrôle
déjà tous les conteneurs dessus — root peut lire chaque /proc/*/environ et
chaque Secret monté. Un injector compromis est celui dont le comportement
change selon le mode.
En mode legacy, l'injector détient une politique Vault étendue :
database/creds/* et auth/token/create-orphan. Un attaquant qui prend le
contrôle du pod injector peut émettre des identifiants pour n'importe quel
rôle DB configuré sous database/. Le rayon d'impact est l'union de tous les
moteurs DB que l'injector peut atteindre.
En mode projected-SA, l'injector ne détient pas de politique d'émission DB.
Le pod-token porte la contrainte de rôle de manière cryptographique : un JWT
émis par l'injector pour le pod A ne peut pas passer bound_service_account_names
sur le rôle Vault du pod B. Un injector compromis peut toujours émettre des
TokenRequests pour le ServiceAccount de n'importe quel pod — mais le token
Vault résultant est limité au rôle du pod, et le journal d'audit Vault attribue
l'émission au pod réel.
Risques résiduels : le DaemonSet du plugin NRI s'exécute en root pour lire
le socket containerd. Une évasion de conteneur depuis le pod du plugin donne un
accès root complet au nœud et (via la permission serviceaccounts/token à
l'échelle du cluster) un accès Vault effectivement total. Le mode NRI ne
mitige pas la compromission au niveau du nœud — il mitige la fuite des
identifiants depuis etcd, GitOps, et les journaux d'audit.
Durcissement du mode NRI
- PodSecurityAdmission
restrictedsur chaque namespace utilisateur.restrictedetbaselineinterdisent tous deux les volumes hostPath, seul moyen pour un pod non-injector de s'enregistrer comme plugin NRI ou de lire le fichier de cache. Le namespace propre du plugin doit être labélisépod-security.kubernetes.io/enforce=privileged. - Kyverno ClusterPolicy bloquant les montages hostPath
/var/run/nri,/opt/nri, et/run/<release-fullname>en dehors du namespace de confiance. Politique de référence ci-dessous. - SELinux/AppArmor en mode enforcing sur RHEL/CoreOS. N'exécutez aucun pod
avec
seLinuxOptions.type: spc_t. Le label de socketcontainer_runtime_tpar défaut empêche les pods utilisateurs de se connecter même s'ils contournent la vérification hostPath. - Restrictions hostPath au niveau de l'admission : le seul consommateur
légitime de
/var/run/nri/nri.sockest le DaemonSet du plugin lui-même.
Politique Kyverno
Le dépôt fournit une Kyverno ClusterPolicy de référence qui bloque les montages hostPath NRI en dehors du namespace de l'injector. C'est la couche de défense en profondeur la moins coûteuse au-dessus de PSA, et la base recommandée :
helm/policies/kyverno-restrict-nri-socket.yaml
La politique refuse tout pod qui monte /var/run/nri, /opt/nri, ou
/run/<release-fullname> sauf s'il se trouve dans le namespace de l'injector.
Cela ferme le vecteur "pod utilisateur qui s'enregistre comme plugin NRI",
déjà bloqué par PSA au niveau restricted — utile quand vous ne pouvez pas
imposer restricted à l'échelle du cluster.
Gains de sécurité du mode projected-SA
- Attestation native par Vault : le journal d'audit indique quel ServiceAccount de pod a acquis quels identifiants. En mode legacy, chaque émission est attribuée au ServiceAccount de l'injector.
- Un injector compromis ne peut pas émettre d'identifiants DB arbitraires : l'injector n'a pas de politique d'émission DB en mode projected, et le pod-token porte la contrainte de rôle de manière cryptographique.
- Rayon d'impact réduit : la seule capacité Kubernetes dont l'injector a
encore besoin est
serviceaccounts/token, limitée par audience. UntokenRequestAudiencesvide est refusé au démarrage depuis la v3.0 car une audience vide produit un JWT réutilisable par n'importe quel service.
Posture du fichier de cache
Le cache du plugin NRI à /run/<release-fullname>/nri/cache.json contient
des identifiants déchiffrés en clair, avec les permissions 0600 root:root,
sur tmpfs. La même posture s'applique aux tokens ServiceAccount projected de
kubelet à /var/lib/kubelet/pods/<UID>/volumes/kubernetes.io~projected/...
et à tout Secret monté comme volume.
Un attaquant root sur le nœud peut déjà lire /proc/<pid>/environ de chaque
conteneur, donc le cache n'ajoute aucune nouvelle surface d'attaque au-delà de
ce que root possède déjà. Le cache n'est jamais sur disque persistant
(tmpfs) et jamais dans les sauvegardes (/run est exclu de tous les
outils de sauvegarde de nœud).
Le seul vecteur permettant à un pod utilisateur non-root de lire le cache est
de monter hostPath /run et de s'exécuter en UID 0. PSA restricted et
baseline interdisent les montages hostPath. La politique Kyverno bloque
/run/<release-fullname> pour les pods utilisateurs comme second niveau de
protection.
Piste d'audit
En mode projected, le journal d'audit Vault affiche le ServiceAccount du
pod comme identité ayant émis chaque appel database/creds/<role>. Corrélation
par nom de ServiceAccount et annotation db-creds-injector.numberly.io/uuid
inscrite sur le pod. En mode legacy, chaque émission est attribuée au
ServiceAccount propre de l'injector — le journal d'audit indique que l'injector
a agi pour le compte de quelqu'un, mais ne peut pas prouver qui sans corréler
avec le mont de bookkeeping KV.
Le DaemonSet NRI s'exécute en root
Le mode NRI requiert que le DaemonSet du plugin monte
/var/run/nri/nri.sock — le même socket que containerd utilise pour
l'enregistrement des plugins. Tout pod qui monte ce hostPath peut
s'enregistrer comme plugin NRI et muter tous les conteneurs créés sur
le nœud (variables d'environnement, montages, capabilities, arguments).
Ceci est inhérent à NRI, pas spécifique à ce projet. L'administrateur
du cluster doit restreindre qui peut monter ces chemins via PSA
restricted/baseline sur les namespaces utilisateurs, la politique
Kyverno ci-dessus, et SELinux/AppArmor en mode enforcing. Une évasion de
conteneur depuis le pod du plugin NRI donne un accès root complet au nœud
et un accès Vault effectivement total via la permission
serviceaccounts/token à l'échelle du cluster.
Déployez le mode NRI uniquement sur des nœuds dédiés ou durcis. Vérifiez régulièrement les images de nœud pour détecter toute compromission.