Sécurité de Kubernetes dans le cloud Azure – partie 4 – Sécuriser les images
Ici je vais traiter d’un sujet qui doit concerner l’ensemble des acteurs de la chaine impliqués dans une application exécutée dans Kubernetes : développeurs, SRE, DevOps, IT, DevSecOps, RSSI…
En effet, l’image est LE package qui va être instancié sur le cluster Kubernetes sur le moteur de conteneur (Moby, ContainerD…). Si cette image est compromise ou contient des vulnérabilités alors c’est tout le cluster et sa sécurité qui sera impactée !


Ne pas oublier que le partage du kernel fait la force des conteneurs mais c’est aussi potentiellement une faiblesse en termes de sécurité. Le kernel étant partagé, chaque vulnérabilité de celui-ci est démultipliée par le nombre de conteneurs. Si un processus arrive à “sortir” du conteneur et obtient des privilèges équivalents sur la machines Host alors on parle de Container Breakout.
Il va donc falloir préparer des images de conteneurs de confiance avec un contenu connu, à jour et analysé. Eviter au maximum l’utilisation d’images externes non maitrisées ou non analysées par votre organisation et donc réduire les risques.
Il faut donc traiter le problème en amont et en aval.
En amont : connaitre la provenance des images en partant de registries de confiance et d’images officielles directement fournies par un éditeur/communauté et ayant déjà subit des validations en termes de sécurité. Ces images officielles (par exemple dans le Docker Hub) sont une bonne base pour construire vos images en y ajoutant vos applications et les dépendances associées.

En poussant le modèle à l’extrême, il est également possible de partir sur des images de base allégées (ex: alpine slim) en terme de shell et outils voire très allégées de typedistroless (pas de package manager, pas de shell, pas d’outils) comme celles proposées par Google. On est clairement ici dans une optique de réduction de la surface d’exposition et dans l’optimisation de l’empreinte en termes de ressource.
En aval ; instancier dans le cluster Kubernetes principalement (uniquement?) des images que vous aurez construites, analysées, signées et stockées dans votre propre registry. Il va bien entendu prévoir la gestion du cycle de vie de ces images (mise à jour des images dans la registry, déploiement de ces images dans le cluster, nettoyage des anciennes images dans la registry…).
Maintenant, regardons un peu plus dans le détails les différentes opérations :
Les développeurs et leur code applicatif
Ici il faut appliquer simplement les bonnes pratiques et des outils d’analyse statique du code pour détecter et corriger tout problème de sécurité lors de la phase de build.

L’utilisation de traitements automatisés dans des pipelines (ex: Azure Pipelines ou GitHub Actions) doit aider à la systématisation de ce type de tests de sécurité. Le résultat de cette intégration continue étant généralement des binaires qui sont ensuites inclus dans la création de l’image de conteneur (il est également possible de tout faire avec du multistage builds), le fameux docker build.
Quelques exemples d’outils d’analyse de sécurité du code :
- L’extension Microsoft Security Code Analysis permet d’analyser la sécurité d’un code dans des pipelines Azure DevOps. Cela inclus : une analyse anti malware, BinSkim, Credential Scanner (recherche de mots de passe, clés, secrets dans le code), Roslyn Analyzer…
- Code Scanning a GitHub Repository using GitHub Advanced Security within an Azure Pipeline
- Sonar Cloud
- WhiteSource Bolt: Avec GitHub : https://github.com/marketplace/whitesource-bolt, avec Azure DevOps : https://marketplace.visualstudio.com/items?itemName=whitesource.ws-bolt
- Synopsis Detect (anciennement Blackduck) : Avec GitHub Actions: https://synopsys.atlassian.net/wiki/spaces/PARTNERS/pages/151093290/Synopsys+Detect+GitHub+Action, avec dans Azure Pipelines : https://synopsys.atlassian.net/wiki/spaces/INTDOCS/pages/622618/Synopsys+Detect+for+Azure+DevOps
La phase de build de l’image de conteneur
Cette phase est celle où le docker build est réalisé à partir du Dockerfile. Ici encore il est nécessaire d’analyser les différents couches qui vont composer cette image.
Ce travail d’analyse devrait commencer à la source (c’est à dire sur le poste du développeur) avec l’option scan de la CLI Docker : https://brianchristner.io/how-to-use-docker-scan
Si l’opération de build de l’image est faite dans un pipeline, alors intégrer à celui ci des tâches d’analyse de sécurité spécifiques aux images avec des outils tels que :
- Clair : https://github.com/quay/clair
- Qualys : https://www.qualys.com/apps/container-security
- Aqua : https://www.aquasec.com/products/container-security/
- Twistlock (désormais dans l’offre Prisma Cloud de Palo Alto): https://www.paloaltonetworks.com/prisma/cloud/container-security
- Anchore Engine : https://github.com/anchore/anchore-engine
- Dagda : https://github.com/eliasgranderubio/dagda
Point intéressant : il n’est pas nécessaire de passer forcément par une solution de build locale ou un pipeline (Azure Pipeline, Github Actions..) pour construire son image de conteneur, il est aussi possible la fonctionnalité de ACR tasks intégrée à Azure Container Registry. Ce processus automatisé d’Azure Container Registry peut être déclenché en mode intégration continu par un commit dans un dépôt de code source, la mise à jour d’une image servant de base ou de manière planifiée.

Si les opérations d’analyses de sécurité sont passées avec succès dans la phase de build de l’image dans le pipeline, alors il est possible de pousser l’image générée dans une registry comme Azure Container Registry.
La phase de stockage et d’usage des images dans une registry
Azure Container Registry peut elle même intégrer des mécanismes d’analyse de sécurité tierces parties comme Aqua, Twislock ou encore mieux utiliser Azure Security Defender for Containers Registry (qui est une intégration des technologies de Qualys).

Ainsi dès qu’une image est poussée, elle est imédiatement scannée pour vérifier la présence de code malveillant ou de vulnérabilités (par utilisation de composants obsolètes). De même, les images importées depuis une autre registry (ex: Docker Hub) ou utilisées dans les 30 derniers jours sont systématiquement scannées.

Le résultat est immédiatement disponible dans Azure Security Center.

Sécurisation de la registry dans Azure
La registry qui va stocker les images des applications d’une organisation est le plus une registry privée qu’il faut configurer au mieux afin d’en assurer la confidentialité, l’intégrité et la disponibilité.
Cette registry peut être instanciée sous la forme d’une machine virtuelle, d’un conteneur ou d’un service managé comme Azure Container Registry. C’est ce dernier choix qui est celui le plus classiquement fait sur la plateforme Azure.
L’accès à l’Azure Container Registry peut se faire via l’usage d’un login/mot de passe mais c’est pas l’option recommandée (et ne doit être utilisé qu’en dernier recours si votre solution de pipeline ne sait pas se connecter à Azure). Il est préférable de s’appuyer sur Azure Active Directory (utilisateur, group, Service Principal ou managed identities) et le RBAC d’Azure pour controller finement les accès et usages de l’ACR.
Il existe par défaut dans Azure, un ensemble de roles prêts à l’emploi pour faire de gestion d’identités et des accès sur Azure Container Registry.

Si vous faites le build localement (ou dans un pipeline) avec la CLI docker, il est possible de se connecter à l’Azure Container Registry en utilisant un Service Principal de la manière suivante:

Niveau confidentialité, les données d’une ACR sont chiffrées au repos (at rest) par une clé gérée par Microsoft. Il est cependant possible de gérer soit même sa clé qui sera stockée dans Azure Key Vault

Niveau réseau, par défaut une Azure Container Registry dispose d’un endpoint public accessible via un FQDN du type nomderegistry.azurecr.io. Ce endpoint est ouvert sur Internet et il est recommandé (comme pour l’API Server d’AKS) de limiter son exposition à des adresses IP spécifiques.

Il est aussi possible d’utiliser un endpoint privé et de limiter les accès à l’ACR à des sous-réseaux Azure spécifiques. Cette option forcément plus sécurisée implique cependant certaines limitations (comme l’impossibilité d’utiliser des agents Microsoft dans les pipelines Azure DevOps ou encore l’impossibilité d’utiliser certains services d’analyse de sécurité)