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/workflows
et 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.yml
pour 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.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;
- 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");
});
- Créer un dossier
tests
et 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.json
pour 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 -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 avecnpm test
.
Étape 2 : Ajouter un pipeline GitHub Actions
- Créer un dossier
.github/workflows
et 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.yml
définit un pipeline qui s’exécute à chaque push ou pull request surmain
.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
- Ajouter un fichier
.env
contenant une variable secrète :
SECRET_MESSAGE=Message ultra confidentiel
- 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;
- Installer
dotenv
pour gérer les variables d’environnement :
npm install dotenv
-
Ajouter un secret dans GitHub Secrets sous le nom
SECRET_MESSAGE
. -
Modifier
ci-cd.yml
pour 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.
.env
stocke une variable secrète localement.dotenv
charge les variables d’environnement dansserver.js
.GitHub Secrets
permet de stockerSECRET_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
- 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=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
- Modifier
ci-cd.yml
pour 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: 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.