Aller au contenu principal

Pipeline CI/CD sécurisé

Automatiser le déploiement des applications tout en garantissant leur sécurité.

Notions théoriques

L’intégration et le déploiement continus (CI/CD) permettent d’automatiser les tests et la mise en production des applications.

Un pipeline CI/CD bien conçu réduit les erreurs humaines et accélère le cycle de développement.

Cependant, sans mesures de sécurité appropriées, il peut devenir une cible pour les attaquants.

Principaux concepts du CI/CD

  • Intégration continue (CI) : Automatisation des tests et de la validation du code à chaque modification.
  • Déploiement continu (CD) : Automatisation du déploiement en production après validation.
  • Pipeline : Ensemble d’étapes automatisées permettant d’assurer la qualité et la sécurité du code avant son déploiement.

Risques de sécurité dans un pipeline CI/CD

  • Fuites de secrets : Stocker des clés API ou des mots de passe en clair dans le code peut exposer des informations sensibles.
  • Dépendances vulnérables : L’utilisation de bibliothèques non sécurisées peut introduire des failles.
  • Accès non restreint : Un pipeline mal configuré peut permettre des modifications non autorisées du code source.
  • Exécution de code malveillant : Un attaquant peut injecter du code malveillant dans le pipeline pour compromettre l’application.

Bonnes pratiques pour sécuriser un pipeline CI/CD

Protéger les secrets

  • Utiliser des gestionnaires de secrets comme GitHub Secrets, GitLab CI/CD Variables ou Vault.
  • Ne jamais stocker de secrets en dur dans le code ou les fichiers de configuration.

Sécuriser les dépendances

  • Scanner les dépendances avec des outils comme Dependabot, Trivy ou Snyk.
  • Mettre à jour régulièrement les bibliothèques et vérifier les alertes de sécurité.

Restreindre les accès

  • Appliquer le principe du moindre privilège en limitant les permissions des comptes CI/CD.
  • Vérifier que seuls les utilisateurs et services autorisés peuvent modifier le pipeline.

Vérifier l’intégrité du code

  • Mettre en place une analyse statique du code avec SonarQube ou CodeQL.
  • Utiliser la signature de commits pour garantir l’authenticité du code soumis.

Surveiller et auditer le pipeline

  • Activer les journaux d’exécution pour détecter toute activité suspecte.
  • Configurer des alertes en cas de comportement anormal dans le pipeline.

Exemple pratique

Il est possible, par exemple, de mettre en place un pipeline CI/CD sécurisé avec GitHub Actions pour une application Node.js.

Étape 1 : Préparer l’application

  1. Créer un dossier de travail et initialiser un projet Node.js :
mkdir secure-ci-cd && cd secure-ci-cd
npm init -y
  1. Installer Express.js et créer un fichier server.js :
npm install express
const express = require("express");
const app = express();

app.get("/", (req, res) => {
res.send("Pipeline CI/CD sécurisé !");
});

app.listen(3000, () => {
console.log("Serveur en écoute sur le port 3000");
});

Étape 2 : Ajouter un pipeline GitHub Actions

  1. Créer un dossier .github/workflows et un fichier ci-cd.yml :
name: CI/CD sécurisé

on:
push:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Récupérer le code
uses: actions/checkout@v3

- name: Installer Node.js
uses: actions/setup-node@v3
with:
node-version: 18

- name: Installer les dépendances
run: npm install

- name: Vérifier les vulnérabilités
run: npm audit --audit-level=high

- name: Lancer les tests
run: npm test

Étape 3 : Sécuriser le pipeline

  1. Ajouter un secret dans GitHub Secrets (SECRET_KEY).
  2. Modifier ci-cd.yml pour utiliser ce secret :
      - name: Utiliser une variable secrète
run: echo "Clé secrète utilisée"
env:
SECRET_KEY: ${{ secrets.SECRET_KEY }}
  1. Vérifier que le pipeline s’exécute correctement après un commit.

Test de mémorisation/compréhension


Quel est le rôle principal d'un pipeline CI/CD ?


Pourquoi est-il risqué de stocker des secrets en clair dans un pipeline CI/CD ?


Quel outil permet de scanner les dépendances pour détecter des vulnérabilités ?


Quelle est une bonne pratique pour sécuriser un pipeline CI/CD ?


Quel fichier contient la configuration d'un pipeline GitHub Actions ?


Quel risque est associé à l'utilisation de bibliothèques non mises à jour ?


Quel outil permet d'analyser le code source pour détecter des failles ?


Pourquoi est-il important de surveiller l'exécution du pipeline CI/CD ?


Quelle commande permet d'installer les dépendances d'un projet Node.js ?


Pourquoi est-il conseillé de signer ses commits ?


TP pour réfléchir et résoudre des problèmes

L'objectif de ce TP est de mettre en place un pipeline CI/CD sécurisé pour un projet Node.js, en intégrant des tests automatisés et des bonnes pratiques de sécurité.

Contrairement à l'exemple pratique, ce TP inclut la gestion des tests unitaires et l'utilisation avancée des secrets pour éviter l'exposition d'informations sensibles.

Étape 1 : Préparer l’application et les tests

  1. Créer un dossier de travail et initialiser un projet Node.js :
mkdir secure-pipeline && cd secure-pipeline
npm init -y
  1. Installer Express.js et Jest pour les tests :
npm install express
npm install --save-dev jest supertest
  1. Créer un fichier server.js contenant une API simple :
const express = require("express");
const app = express();

app.get("/", (req, res) => {
res.send("Application sécurisée avec CI/CD !");
});

module.exports = app;
  1. Ajouter un fichier index.js pour démarrer le serveur :
const app = require("./server");

app.listen(3000, () => {
console.log("Serveur en écoute sur le port 3000");
});
  1. Créer un dossier tests et ajouter un fichier server.test.js :
const request = require("supertest");
const app = require("../server");

test("L'API doit retourner un message", async () => {
const response = await request(app).get("/");
expect(response.text).toBe("Application sécurisée avec CI/CD !");
});
  1. Modifier package.json pour ajouter un script de test :
"scripts": {
"test": "jest"
}
info

Cette étape prépare une application Node.js avec une API simple et un test automatisé.

  • npm init -y initialise un projet Node.js.
  • npm install express installe Express.js pour gérer les requêtes HTTP.
  • npm install --save-dev jest supertest installe Jest et Supertest pour les tests unitaires.
  • server.js définit une API qui retourne un message.
  • index.js démarre le serveur.
  • server.test.js contient un test qui vérifie la réponse de l'API.
  • Le script "test": "jest" permet d’exécuter les tests avec npm test.

Étape 2 : Ajouter un pipeline GitHub Actions

  1. Créer un dossier .github/workflows et un fichier ci-cd.yml :
name: CI/CD sécurisé

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Récupérer le code
uses: actions/checkout@v3

- name: Installer Node.js
uses: actions/setup-node@v3
with:
node-version: 18

- name: Installer les dépendances
run: npm install

- name: Lancer les tests
run: npm test
  1. Ajouter, valider et pousser le code vers GitHub :
git init
git add .
git commit -m "Premier commit"
git branch -M main
git remote add origin <URL_DU_REPO>
git push -u origin main
  1. Vérifier que le pipeline s’exécute dans l’onglet Actions du dépôt GitHub.
info

Cette étape met en place un pipeline CI/CD avec GitHub Actions.

  • .github/workflows/ci-cd.yml définit un pipeline qui s’exécute à chaque push ou pull request sur main.
  • actions/checkout@v3 récupère le code source.
  • actions/setup-node@v3 installe Node.js.
  • npm install installe les dépendances.
  • npm test exécute les tests unitaires.
  • La commande git push -u origin main envoie le code sur GitHub pour déclencher le pipeline.

Étape 3 : Sécuriser le pipeline avec des secrets

  1. Ajouter un fichier .env contenant une variable secrète :
SECRET_MESSAGE=Message ultra confidentiel
  1. Modifier server.js pour utiliser cette variable :
require("dotenv").config();
const express = require("express");
const app = express();

app.get("/", (req, res) => {
res.send(`Message : ${process.env.SECRET_MESSAGE}`);
});

module.exports = app;
  1. Installer dotenv pour gérer les variables d’environnement :
npm install dotenv
  1. Ajouter un secret dans GitHub Secrets sous le nom SECRET_MESSAGE.

  2. Modifier ci-cd.yml pour passer ce secret au pipeline :

      - name: Utiliser une variable secrète
run: echo "SECRET_MESSAGE=${{ secrets.SECRET_MESSAGE }}" > .env
  1. Vérifier que l’application utilise bien la variable secrète.
info

Cette étape protège les informations sensibles en utilisant des variables d’environnement.

  • .env stocke une variable secrète localement.
  • dotenv charge les variables d’environnement dans server.js.
  • GitHub Secrets permet de stocker SECRET_MESSAGE de manière sécurisée.
  • echo "SECRET_MESSAGE=${{ secrets.SECRET_MESSAGE }}" > .env injecte la variable dans le pipeline.

Étape 4 : Analyser les vulnérabilités du projet

  1. Ajouter une étape de scan de sécurité dans ci-cd.yml :
      - name: Scanner les dépendances avec npm audit
run: npm audit --audit-level=high
  1. Exécuter le pipeline et vérifier s’il y a des vulnérabilités.

  2. Si des vulnérabilités sont détectées, essayer de les corriger :

npm audit fix
info

Cette étape permet de détecter et corriger les failles de sécurité dans les dépendances.

  • npm audit --audit-level=high scanne les dépendances et affiche les vulnérabilités critiques.
  • npm audit fix tente de corriger automatiquement les failles détectées.

Étape 5 : Restreindre l’accès au pipeline

  1. Modifier ci-cd.yml pour exécuter le pipeline uniquement si les commits sont signés :
  permissions:
contents: read
id-token: write
  1. Activer Branch Protection Rules sur GitHub pour exiger des revues et des commits signés.

  2. Vérifier que seuls les commits signés déclenchent le pipeline.

info

Cette étape renforce la sécurité du pipeline en limitant son exécution aux commits signés.

  • permissions: contents: read, id-token: write limite les permissions du pipeline.
  • Branch Protection Rules empêche les modifications non autorisées sur main.

Conclusion

Ce TP a permis de mettre en place un pipeline CI/CD sécurisé en intégrant :

  • Des tests unitaires pour garantir la fiabilité du code
  • L’utilisation de secrets pour protéger les informations sensibles
  • Un scan de sécurité des dépendances
  • Une restriction des accès pour éviter les modifications non autorisées

Ces pratiques permettent de renforcer la sécurité et la fiabilité des déploiements automatisés.