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
- Créer un dossier de travail et initialiser un projet Node.js :
mkdir secure-ci-cd && cd secure-ci-cd
npm init -y
- 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
- Créer un dossier
.github/workflowset un fichierci-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
- Ajouter un secret dans GitHub Secrets (
SECRET_KEY). - Modifier
ci-cd.ymlpour utiliser ce secret :
- name: Utiliser une variable secrète
run: echo "Clé secrète utilisée"
env:
SECRET_KEY: ${{ secrets.SECRET_KEY }}
- Vérifier que le pipeline s’exécute correctement après un commit.
Test de mémorisation/compréhension
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
- Créer un dossier de travail et initialiser un projet Node.js :
mkdir secure-pipeline && cd secure-pipeline
npm init -y
- Installer Express.js et Jest pour les tests :
npm install express
npm install --save-dev jest supertest
- Créer un fichier
server.jscontenant 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;
- Ajouter un fichier
index.jspour démarrer le serveur :
const app = require("./server");
app.listen(3000, () => {
console.log("Serveur en écoute sur le port 3000");
});
- Créer un dossier
testset ajouter un fichierserver.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 !");
});
- Modifier
package.jsonpour ajouter un script de test :
"scripts": {
"test": "jest"
}
Cette étape prépare une application Node.js avec une API simple et un test automatisé.
npm init -yinitialise un projet Node.js.npm install expressinstalle Express.js pour gérer les requêtes HTTP.npm install --save-dev jest supertestinstalle Jest et Supertest pour les tests unitaires.server.jsdéfinit une API qui retourne un message.index.jsdémarre le serveur.server.test.jscontient un test qui vérifie la réponse de l'API.- Le script
"test": "jest"permet d’exécuter les tests avecnpm test.
Étape 2 : Ajouter un pipeline GitHub Actions
- Créer un dossier
.github/workflowset un fichierci-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
- 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
- Vérifier que le pipeline s’exécute dans l’onglet Actions du dépôt GitHub.
Cette étape met en place un pipeline CI/CD avec GitHub Actions.
.github/workflows/ci-cd.ymldéfinit un pipeline qui s’exécute à chaque push ou pull request surmain.actions/checkout@v3récupère le code source.actions/setup-node@v3installe Node.js.npm installinstalle les dépendances.npm testexécute les tests unitaires.- La commande
git push -u origin mainenvoie le code sur GitHub pour déclencher le pipeline.
Étape 3 : Sécuriser le pipeline avec des secrets
- Ajouter un fichier
.envcontenant une variable secrète :
SECRET_MESSAGE=Message ultra confidentiel
- Modifier
server.jspour 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;
- Installer
dotenvpour gérer les variables d’environnement :
npm install dotenv
-
Ajouter un secret dans GitHub Secrets sous le nom
SECRET_MESSAGE. -
Modifier
ci-cd.ymlpour passer ce secret au pipeline :
- name: Utiliser une variable secrète
run: echo "SECRET_MESSAGE=${{ secrets.SECRET_MESSAGE }}" > .env
- Vérifier que l’application utilise bien la variable secrète.
Cette étape protège les informations sensibles en utilisant des variables d’environnement.
.envstocke une variable secrète localement.dotenvcharge les variables d’environnement dansserver.js.GitHub Secretspermet de stockerSECRET_MESSAGEde manière sécurisée.echo "SECRET_MESSAGE=${{ secrets.SECRET_MESSAGE }}" > .envinjecte la variable dans le pipeline.
Étape 4 : Analyser les vulnérabilités du projet
- 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
-
Exécuter le pipeline et vérifier s’il y a des vulnérabilités.
-
Si des vulnérabilités sont détectées, essayer de les corriger :
npm audit fix
Cette étape permet de détecter et corriger les failles de sécurité dans les dépendances.
npm audit --audit-level=highscanne les dépendances et affiche les vulnérabilités critiques.npm audit fixtente de corriger automatiquement les failles détectées.
Étape 5 : Restreindre l’accès au pipeline
- Modifier
ci-cd.ymlpour exécuter le pipeline uniquement si les commits sont signés :
permissions:
contents: read
id-token: write
-
Activer Branch Protection Rules sur GitHub pour exiger des revues et des commits signés.
-
Vérifier que seuls les commits signés déclenchent le pipeline.
Cette étape renforce la sécurité du pipeline en limitant son exécution aux commits signés.
permissions: contents: read, id-token: writelimite 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.