
Une application compromise ne devrait pas pouvoir lire tous les Secrets ni joindre toutes les bases du cluster. Voici un exercice de durcissement pour un namespace applicatif, inspiré des contrôles officiels Kubernetes, à répéter d'abord en préproduction.
1. Établir les droits et les flux réels
Prévoyez kubectl, une identité autorisée à auditer RBAC et à impersonner les ServiceAccounts, un CNI appliquant les NetworkPolicies et les manifests de référence conservés dans Git. Les commandes utilisent le namespace existant shop, les pods app=web sur TCP 8080 et une base app=db sur TCP 5432. Adaptez ces noms et ports avant exécution.
kubectl config current-contextkubectl versionkubectl -n shop get pods,sa,networkpolicykubectl -n shop get rolebindings -o yamlkubectl get clusterrolebindings -o yamlSA=system:serviceaccount:shop:webkubectl auth can-i --list -n shop --as="$SA"kubectl auth can-i list secrets -n shop --as="$SA"kubectl auth can-i create pods -n shop --as="$SA"Répétez pour chaque compte attaché à un workload. Inspectez les Roles et ClusterRoles référencés : jokers, cluster-admin, modification de bindings, impersonation et création de pods méritent une justification. Une réponse « no » sur Secrets ne suffit pas : créer un pod peut permettre de monter un Secret du namespace. Les droits hérités des groupes exigent aussi un contrôle ; l'impersonation du seul nom ne reproduit pas nécessairement tous les groupes d'une authentification réelle.
Dessinez les flux attendus : ingress-controller → web → db, puis DNS, supervision et dépendances externes. Mesurez latence et erreurs avant modification. Sauvegardez les labels du namespace et les politiques dans un dossier protégé, sans exporter les valeurs des Secrets.
2. Appliquer quatre barrières, séparément
Identité. Donnez un ServiceAccount distinct à chaque application. Dans le PodSpec du Deployment, utilisez :
serviceAccountName: webautomountServiceAccountToken: falseSi l'application utilise réellement l'API, conservez un jeton projeté de courte durée et attribuez uniquement les ressources et verbes nécessaires via un Role et un RoleBinding locaux. Retirez le binding excessif après validation du remplacement ; ajouter un Role restreint ne supprime aucun droit existant.
Pods. Commencez par avertir et auditer :
P=pod-security.kubernetes.iokubectl label ns shop "$P/warn=restricted" "$P/audit=restricted" --overwriteÉpinglez aussi les labels warn-version, audit-version et enforce-version sur la version mineure validée de votre cluster. Corrigez les workloads ; voici un fragment à intégrer, pas un Deployment complet :
spec: template: spec: securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault containers: - name: web securityContext: privileged: false allowPrivilegeEscalation: false capabilities: drop: ["ALL"]Utilisez une image compatible avec un UID non-root. Testez le redéploiement, puis activez enforce=restricted. L'admission ne transforme pas les pods existants : leur remplacement révèle les incompatibilités.
Réseau. Enregistrez cet exemple dans web-egress.yaml. Il sélectionne uniquement web et autorise sa base dans le même namespace et le DNS CoreDNS standard :
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: web-egress namespace: shopspec: podSelector: matchLabels: app: web policyTypes: [Egress] egress: - to: - podSelector: matchLabels: app: db ports: - protocol: TCP port: 5432 - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system podSelector: matchLabels: k8s-app: kube-dns ports: - protocol: UDP port: 53 - protocol: TCP port: 53Vérifiez les labels DNS ; NodeLocal DNS peut nécessiter une autre règle. Ajoutez les dépendances externes mesurées, puis :
kubectl apply --dry-run=server -f web-egress.yamlkubectl apply -f web-egress.yamlCette application coupe les autres sorties de web sauf autorisation par une autre politique : les règles sont additives. Ajoutez ensuite des politiques Ingress pour web depuis l'ingress-controller et pour db depuis web. Validez chaque flux avant une politique de refus par défaut couvrant tout le namespace.
Secrets. Montez seulement le Secret requis dans le conteneur concerné. Évitez list/watch globaux, les valeurs dans Git et les journaux applicatifs. Vérifiez avec l'administrateur le chiffrement au repos d'etcd et des sauvegardes ; base64 ne chiffre rien.
3. Prouver l'isolation et préparer le repli
Depuis des pods de test autorisés, vérifiez DNS et connexion web→db ; depuis un pod sans label autorisé, vérifiez le refus vers db. Une connexion déjà établie n'est pas un test fiable : ouvrez de nouvelles connexions. Confirmez aussi qu'un pod privilégié est refusé par admission en préproduction.
Surveillez erreurs DNS, timeouts, 403 de l'API, refus d'admission, pods non prêts et métriques du CNI. Une politique acceptée par l'API ne prouve pas son application. Les coûts réseau dépendent du CNI ; comparez CPU et latence à votre référence.
En incident, revenez au manifest précédent dans Git, puis réappliquez-le. Si cette politique est entièrement nouvelle, le repli ciblé est :
kubectl -n shop delete networkpolicy web-egressCela peut rouvrir les sorties : limitez la fenêtre et conservez les autres protections. Restaurez les anciens labels Pod Security si un rollback applicatif l'exige, puis corrigez avant réactivation. Ne rétablissez pas cluster-admin par commodité. En production, séparez les namespaces système, conservez les journaux d'audit hors cluster et faites valider les exceptions avec une échéance.
Sources : Kubernetes, contrôles de sécurité, Kubernetes, NetworkPolicies.