• 10 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 19/11/2024

Sécurisez votre déploiement

L’instance de démonstration de Libra est en place depuis quelques semaines, tous les indicateurs semblent au beau fixe et Liam est ravi. Cependant, un soir, Sarah vous envoie un message alarmant :

On a passé un scanner de sécurité sur l’infrastructure et on vient de détecter que l’image du conteneur Libra était truffée de failles… Il faut qu’on règle ça rapidement avant que Liam nous demande de passer l’application en production !

Quelles sont les questions de sécurité à se poser pour la maintenance d’infrastructures conteneurisées ?

La sécurité est un sujet transverse et Docker n’en est pas exempt ! Sécuriser une application ne se limite pas à protéger uniquement les conteneurs ou l'application elle-même. On parle désormais d'une approche plus holistique qui s'inscrit dans les pratiques DevOps et DevSecOps.

En prenant de la hauteur, nous allons voir que la sécurisation d'une application conteneurisée est en réalité une partie d'un ensemble plus vaste, où chaque maillon de la chaîne de production doit être envisagé sous l'angle de la sécurité, nécessitant une approche plus d’anticipation que de réaction.

Intégrez la sécurité dans votre processus d’intégration

La conteneurisation a profondément transformé la conception et la mise en œuvre du cycle de développement logiciel. Cette transformation a touché de nombreux aspects, mais l'effet le plus marquant est sans doute l'intégration de la sécurité dans le processus de production.

Découvrons ensemble la mutation qu’à subit le cycle de vie de la conception et du déploiement d'applications, et pourquoi la sécurité s’est retrouvée naturellement le sujet central de celui-ci.

Cycle de vie classique de déploiement d'application

Dans un modèle traditionnel de déploiement d'applications, les équipes de développement et les équipes opérationnelles (Ops) avaient des responsabilités distinctes. Voici un aperçu des étapes typiques :

1/ Développement :

  • Les développeurs écrivent le code de l'application.

  • Le code est testé dans un environnement de développement et de test.

  • Une fois le code jugé stable, il est préparé pour le déploiement.

 2/ Livraison : 

  • Les équipes de développement remettent le code aux équipes opérationnelles.

  • Les équipes Ops prennent en charge le code et valident la procédure d’installation dans un environnement de test.

3/ Déploiement :

  • Les équipes Ops déploient le code sur des serveurs physiques ou virtuels.

  • Elles configurent les serveurs, installent les dépendances nécessaires et s'assurent que l'application fonctionne correctement.

4/ Maintenance :

  • Les équipes Ops surveillent les serveurs et l'application pour détecter tout problème.

  • Elles gèrent les mises à jour de sécurité des serveurs et des dépendances de l'application.

  • Les correctifs de sécurité et les mises à jour sont appliqués régulièrement.

Cycle classique de déploiement d'application en 4 étapes : développement, livraison, déploiement et maintenance
Cycle classique de déploiement d'application

Dans ce modèle, la sécurité des serveurs et des dépendances système est principalement la responsabilité des équipes Ops. Les développeurs se concentrent principalement sur le code de l'application.

Si l’application Libra était développée en suivant ce modèle, l’environnement d’exécution de celle-ci ne serait abordé qu’en toute fin de chaîne de production ! Pour une application dont la finalité est le transfert de documents confidentiels, cela pourrait paraître particulièrement dangereux de laisser une telle incertitude jusqu’aux portes du déploiement…

Méthodes de travail actuelles avec la conteneurisation

Avec l'émergence des conteneurs et des pratiques DevOps, le cycle de vie de déploiement des applications a changé de manière significative :

1/ Développement :

  • Les développeurs écrivent le code de l'application.

  • Ils définissent également l'environnement d'exécution dans un Dockerfile. Cela inclut les dépendances système, les configurations et les instructions de démarrage.

2/ Conteneurisation :

  • Le code de l'application et son environnement d'exécution sont encapsulés dans une image de conteneur.

  • Les images de conteneur sont stockées dans des registres de conteneurs (comme Docker Hub).

3/ CI/CD (Intégration Continue / Livraison Continue) :

  • Les pipelines CI/CD automatisent le processus de construction, de test et de déploiement des conteneurs.

  • Les tests de sécurité et les analyses de vulnérabilités sont intégrés directement dans le pipeline CI/CD.

  • Les développeurs sont responsables de la création et de la mise à jour des images de conteneur.

4/ Déploiement :

  • Les conteneurs sont déployés sur des plateformes de gestion de conteneurs (comme Docker Swarm ou Kubernetes).

  • Les équipes DevOps, souvent composées de développeurs et d'administrateurs système, collaborent pour gérer le déploiement et la maintenance des conteneurs.

5/ Maintenance :

  • Les équipes DevOps surveillent les conteneurs et les images pour détecter les vulnérabilités et les mises à jour nécessaires.

  • Les développeurs doivent régulièrement mettre à jour les Dockerfiles et reconstruire les images de conteneur pour inclure les correctifs de sécurité.

  • Les outils de surveillance et de gestion des conteneurs permettent une détection proactive des problèmes de sécurité.

Cycle de déploiement avec la conteneurisation en 5 étapes comprenant le développement, la conteneurisation, l'intégration continue / livraison continue, le déploiement et enfin la maintenance
Cycle de déploiement avec la conteneurisation

Décalage de la responsabilité des mises à jour

Avec ces nouvelles méthodes, la responsabilité de la sécurité et des mises à jour des environnements d'exécution se déplace vers les développeurs et les équipes DevOps. Ce décalage peut prendre plusieurs formes :

Entièrement géré par les développeurs :

  • Les développeurs gèrent tout, de la création des Dockerfiles à la mise à jour des dépendances et des images de conteneur.

  • Les équipes Ops peuvent fournir des conseils et des outils, mais les développeurs ont le contrôle total sur l'environnement d'exécution.

Responsabilité partagée entre développeurs et administrateurs :

  • Les développeurs créent les Dockerfiles et gèrent les dépendances de l'application.

  • Les administrateurs système gèrent les infrastructures de déploiement et fournissent des directives de sécurité.

  • Collaboration étroite pour assurer que les meilleures pratiques de sécurité sont suivies.

Responsabilité entre les mains des administrateurs :

  • Les administrateurs système restent responsables de la sécurité et de la maintenance des images de base utilisées par les développeurs.

  • Les développeurs suivent les directives et les images fournies par les administrateurs, en se concentrant sur le code de l'application.

Responsabilités partagées et individuelles entre développeurs et OPS
Responsabilités partagées et individuelles entre développeurs et OPS

Chacune de ces répartitions présentent leurs propres avantages et inconvénients, et il revient à chaque équipe de choisir celle qui lui convient le mieux.

Il est cependant nécessaire de retenir que l’arrivée de la conteneurisation a décalé la gestion des problématiques de sécurité en amont du processus de développement plutôt qu’en aval, tel que c’était (souvent) le cas historiquement.

Protégez-vous des failles “système”

La sécurité est une préoccupation majeure dans le processus de développement et de déploiement des applications modernes. La détection et la mitigation des vulnérabilités systèmes sont cruciales pour maintenir un environnement sécurisé.

Une application comme Libra, dont la sécurité est le principal argument de vente, ne pourrait être considérée comme sûre si une attention particulière n’était pas apportée au système d’exploitation sur laquelle celle-ci s’exécute. Comme le dit l’adage bien connu, “à quoi cela sert-il de fermer ses fenêtres si la porte reste grande ouverte ?”.

Voyons quelques stratégies et outils pour protéger vos conteneurs des failles système.

Définir une stratégie DevOps pour la détection précoce des failles

Une stratégie DevOps efficace pour la sécurité implique l'intégration de pratiques de sécurité tout au long du cycle de développement et de déploiement. Voici quelques éléments clés :

Analyse continue des images de conteneur :

  • Outils de scan : Utilisez des outils comme Trivy ou Clair pour scanner vos images de conteneur régulièrement. Ces outils peuvent identifier les vulnérabilités connues (CVEs) dans vos images.

  • Automatisation : Intégrez ces outils dans votre pipeline CI/CD pour automatiser les scans à chaque nouvelle build. Cela permet de détecter les failles dès que possible.

Pipeline de sécurité intégré :

  • Étapes de sécurité : Ajoutez des étapes spécifiques dans votre pipeline CI/CD pour effectuer des tests de sécurité. Par exemple, une étape pour scanner les images de conteneur après la construction et avant le déploiement.

  • Révision de code : Intégrez des revues de code focalisées sur la sécurité et des audits réguliers de sécurité dans vos processus de développement. Des outils d’analyse statique de code comme SonarQube peuvent également être directement intégrés dans vos pipelines CI/CD afin de détecter une partie des mauvaises pratiques voire des failles avérées dans le code source de l’application. 

Voyons ensemble un exemple d’utilisation d’un outil de scan, Trivy, pour détecter la présence de failles dans une image de conteneur.

Dans cette vidéo, nous avons vu :

  • Comment installer l’outil Trivy sur sa machine

  • Comment utiliser la commandetrivy image <image_name>pour réaliser un scan de sécurité d’une image donnée

  • Comment lire le rapport de scan de Trivy et comment visualiser les failles identifiées.

  • Comment corriger une faille et valider le correctif en exécutant la commande Trivy sur l’image mise à jour.

Fixer les versions des dépendances systèmes

Bien que peut être contre-intuitif, fixer les versions des dépendances systèmes est une pratique essentielle pour maintenir la sécurité de vos conteneurs. Voici pourquoi :

Prévisibilité et stabilité :

  • Contrôle des versions : En fixant les versions, vous vous assurez que vos environnements de développement, de test et de production utilisent les mêmes versions des dépendances, réduisant ainsi les risques de comportements inattendus.

  • Réduction des risques : Les mises à jour inattendues peuvent introduire de nouvelles vulnérabilités ou des incompatibilités. En fixant les versions, vous maîtrisez les changements et pouvez les tester en amont.

Gestion des mises à jour :

  • Planification des mises à jour : Fixer les versions permet de planifier et de tester les mises à jour de manière contrôlée. Vous pouvez évaluer l'impact des nouvelles versions avant de les déployer en production.

  • Documentation et conformité : La fixation des versions permet de mieux documenter les dépendances et de se conformer aux exigences de sécurité et de conformité.

En mettant en place ces bonnes pratiques, vous améliorerez la sécurité de vos déploiements. Mais il existe une autre méthode, particulièrement efficace, pour améliorer la sécurité d’un système: réduire sa surface d’attaque.

Réduisez la surface d’attaque de vos conteneurs

Réduire la surface d'attaque de vos conteneurs est essentiel pour minimiser les risques de sécurité. En adoptant des pratiques spécifiques, vous pouvez limiter les vulnérabilités potentielles et renforcer la sécurité de vos environnements conteneurisés.

Utiliser des images systèmes réduites

Les images systèmes réduites sont conçues pour être aussi minimalistes que possible, ne contenant que les composants essentiels nécessaires à l'exécution de votre application.

Pourquoi privilégier les images réduites ?

L’usage d’une image réduite apporte plusieurs avantages, dont les principaux sont :

  • Moins de vulnérabilités potentielles : Les images réduites contiennent moins de paquets et de bibliothèques, ce qui réduit le nombre de points d'entrée pour les attaques.

  • Performance améliorée : Les images réduites sont souvent plus légères, ce qui peut améliorer les temps de démarrage et la performance globale des conteneurs.

  • Maintenance simplifiée : Moins de composants signifie moins de mises à jour et de correctifs à gérer, simplifiant ainsi la maintenance.

Utiliser un compte utilisateur non privilégié

Exécuter des processus dans vos conteneurs avec des comptes utilisateurs non privilégiés est une bonne pratique pour limiter les risques d'escalade de privilèges en cas de compromission. Voici comment et pourquoi mettre en œuvre cette pratique

Pourquoi utiliser un compte utilisateur non privilégié ?

  • Limiter l'impact d'une compromission : Si un attaquant parvient à accéder à un conteneur, les capacités d'action seront limitées si le conteneur n'exécute pas de processus en tant que root.

  • Meilleures pratiques de sécurité : L'exécution de processus en tant qu'utilisateur non privilégié est une recommandation standard pour les environnements sécurisés. 

Voici un exemple basique d’un Dockerfile créant et utilisant un utilisateur standard :

# On se base sur une image à taille réduite, ici
# Alpine Linux en version 3.20
FROM alpine:3.20
# On installe les dépendances de notre application
RUN apk add python3
# On créait l’utilisateur “myuser” avec des droits standards
RUN adduser -D -s /bin/sh myuser
# On bascule sur l’utilisateur “myuser”
USER myuser
# On copie les sources de notre application dans le répertoire “app”
# du $HOME de notre nouvel utilisateur
COPY . /app /home/myuser/app
# On définit le répertoire /home/myuser/app comme répertoire
# de travail
WORKDIR /home/myuser/app
# On définit la commande exécutée au lancement
# du conteneur, ici une application Python.
CMD ["python3", "app.py"]

Réduire les capacités du compte administrateur

Parfois, des privilèges administratifs sont nécessaires, mais il est possible de réduire les capacités du compte administrateur pour améliorer la sécurité.

Dans un système GNU/Linux, les "capabilities" (ou "capacités" en français) sont des attributs de sécurité qui permettent de restreindre ou de segmenter les privilèges habituellement associés au compte root (super-utilisateur). Au lieu de donner à un processus tous les privilèges root, les capabilities permettent de donner des privilèges spécifiques et limités à un processus, réduisant ainsi la surface d'attaque. Voici quelques exemples de capabilities :

  1. CAP_NET_BIND_SERVICE : Permet de lier des sockets à des numéros de port inférieurs à 1024.

  2. CAP_SYS_ADMIN : Permet diverses opérations administratives, telles que le montage de systèmes de fichiers ou la modification de certains paramètres du noyau.

  3. CAP_DAC_OVERRIDE : Permet de contourner les contrôles d'accès DAC (Discretionary Access Control), autorisant l'accès à des fichiers normalement protégés.

  4. CAP_SYS_TIME : Permet de modifier l'horloge système.

  5. CAP_CHOWN : Permet de changer le propriétaire d'un fichier.

Avec Docker, vous pouvez par exemple définir les capabilities au démarrage du conteneur de la manière suivante :

docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE my-image

Ce qui dans le cas présent revient à supprimer toutes les capabilities associées au compte administrateur puis à lui redonner la capacité à écouter sur un port privilégié (port < 1024).

Armés de ces nouvelles connaissances, vous pouvez désormais améliorer la sécurité de votre infrastructure conteneurisée. Exploitons dès à présent ces compétences !

À vous de jouer

Contexte

Afin de répondre aux craintes exprimées par Sarah et pour montrer qu’il est tout à fait possible de sécuriser l’application Libra avant sa mise en production, vous devez appliquer les bonnes pratiques découvertes dans ce chapitre et les mettre en application sur le Dockerfile de l’application.

Modifiez le Dockerfile rédigé précédemment et améliorez celui-ci en :

  • utilisant une image de base à surface réduite ;

  • fixant les versions des dépendances systèmes

  • créant et utilisant un utilisateur standard pour exécuter l’application Libra.

Consignes

Améliorez le fichier Dockerfile créé lors du P1C5 en :

  • utilisant l’image de basealpine:3.20

  • fixant les versions des différentes dépendances systèmes installées dans l’image du conteneur

  • créant un utilisateur non privilégiélibra et en exécutant l’applicatif Libra avec cet utilisateur. Vous devrez également modifier le port d’écoute de l’applicatif afin d’utiliser un port non privilégié, 8080.

En résumé

  • Le décalage de la responsabilité des mises à jour de sécurité des équipes opérationnelles vers les développeurs et les équipes DevOps permet une gestion plus proactive et intégrée des vulnérabilités.

  • La sécurité doit être intégrée dans le pipeline CI/CD, notamment en utilisant des outils comme Trivy pour analyser et détecter les vulnérabilités.

  • Il est crucial d'utiliser des images systèmes réduites et de fixer les versions des dépendances pour réduire la surface d'attaque et garantir la stabilité.

  • Les processus doivent être exécutés avec des utilisateurs non privilégiés et les capabilities Linux doivent être gérées pour limiter les privilèges et renforcer la sécurité.

Maintenant que nous avons abordé la sécurisation de votre application conteneurisée, il est enfin temps de parler d’un autre point important, si ce n’est crucial: l’optimisation de l’usage des ressources.

Exemple de certificat de réussite
Exemple de certificat de réussite