Sécurité, conformité, coûts : kit de démarrage open source pour CI/CD

Temps de lecture : 11 minutes

Le DevOps et l’approche CI/CD ça vous parle ? Comment se lancer ? Quels outils intégrer ? Comment rester simple ? Si ces questions vous empêchent de dormir la nuit, cet article peut vous intéresser.

Vous trouverez énormément d’articles sur l’importance du DevOps et de la CI/CD dans vos projets de développement. L’objectif de cet article n’est pas de vanter les mérites de l’approche CI/CD mais plutôt de mettre le pied à l’étrier et tirer profits des outils DevOps open source disponibles avec un minimum de développement.

Aujourd’hui je vais donc vous présenter un ensemble d’outils DevOps que j’ai eu l’occasion de mettre en place et d’utiliser durant ces deux dernières années au poste de DevOps & Cloud Engineer chez Devoteam Revolve.

J’ai choisi ces outils car ils répondent à des problématiques communes à tous les projets Cloud et sont très simples à mettre en place. 

Cet article est pour vous si vous cherchez des solutions pour :

  • Authentifier votre CI/CD auprès de votre provider cloud
  • Éviter la fuite de vos secrets et remédier rapidement en cas d’accident
  • Rester à jour sur vos dépendances
  • Sécuriser votre infrastructure cloud
  • Surveiller vos coûts

Commençons tout de suite avec un peu de contexte.

Contexte

Ces deux dernières années j’ai eu la chance d’intervenir en tant que consultant DevOps & Cloud au sein d’un grand compte du CAC40 qui, depuis 2020, a pour ambition de devenir un éditeur de logiciels au service de ses activités principales. Pour cela, ils développent des solutions digitales qui améliorent leur impact environnemental.

C’est dans ce contexte que notre équipe a été créée afin d’améliorer la qualité de ces “Solutions Digitales” et d’en faire de vrais produits commercialisables.

Comment ? En mettant en place une plateforme DevOps et sa chaine CI/CD centralisée disponible pour tous les projets qui s’assurera du niveau des projets sur les aspects suivants :

  • La qualité du code
  • La maturité du projet au niveau devops
  • La sécurité

Après plusieurs années de production et de développement permanent, voici quelques chiffres :

  • Plus de 500 repository utilisateurs de la chaîne CI/CD
  • 10 millions de ligne de code scannées
  • Plus de 3000 projets audités chaque nuit

Dans cet article je n’entrerai pas dans les détails d’implémentation spécifiques à l’environnement de mon client, mais l’idée ici est plutôt de présenter quelques outils gratuits, open source et simples à mettre en place que nous utilisions dans notre CI/CD.

Chacun de ces outils a ses détails techniques et sa complexité et chacun pourrait faire l’objet d’une étude plus détaillée. Dans le cadre de cet article nous allons voir comment les intégrer de manière simple avec une configuration basique/par défaut.

Voici une overview du pipeline que nous allons implémenter aujourd’hui.

Mettre en place une fédération d’identité

Avant de parler d’outils nous allons commencer par une étape primordiale au lancement d’un projet dans le Cloud, il s’agit de la connexion entre votre CI/CD et votre Cloud Provider.

Plus précisément l’authentification des jobs de votre CI/CD sur un compte AWS par exemple et l’autorisation à effectuer des actions sur les ressources.

La méthode la plus connue serait de créer un service account, de lui générer une clef et de stocker ces informations dans les variables de votre CI/CD. 

Cette méthode fonctionne mais vous oblige à gérer un parc de clés pour lequel il faut faire des rotations régulières. Aujourd’hui il est possible de se passer de clés de service account qui perdurent dans le temps et d’utiliser des credentials temporaires en passant par le fédération d’identité.

La fédération d’identité est un système de “trust” entre deux parties, un Identity Provider (IdP) et un Service Provider (SP). Dans notre cas, l’IdP sera notre système de contrôle de version (Gitlab) et notre SP sera le Cloud Provider (AWS).

Pour les détails techniques du fonctionnement de la fédération d’identité et pour son implémentation je vous recommande de vous référer à cet article : Towards a CI/CD world without credentials.

Une fois en place, cela vous permettra d’utiliser votre CI_JOB_JWT_V2 pour obtenir des credentials AWS temporaires et effectuer des actions sur le compte.

Gitleaks

Toujours dans le thème des secrets, nous allons maintenant voir un premier outil open source à intégrer : Gitleaks.

Gitleaks est un outil de static application security testing (SAST). Il permet de détecter des secrets stockés en dur dans le code comme des mots de passe, des tokens, des secret_keys etc.

Setup

Nous allons voir deux intégrations possibles pour sécuriser votre repo. Ces intégrations sont complémentaires.

Pour les deux solutions il est possible d’utiliser l’image Docker fournie par Gitleaks, ce qui simplifie encore son intégration.

Hook pre-commit

Les hooks pre-commit utilisent les Git Hooks pour exécuter des scripts customs au lancement d’une commande “git commit”. La commande sera exécutée automatiquement avant le commit du code. 

C’est parfait pour ce cas d’usage car nous avons besoin de scanner le code avant qu’il soit commité et donc avant qu’il soit dans l’historique git.

Installer pre-commit

Configurer le fichier .pre-commit-config.yaml

repos:
 - repo: https://github.com/gitleaks/gitleaks
   rev: v8.18.0  # Specify the desired version of Gitleaks
   hooks:
     - id: gitleaks-docker

Le problème est que cette méthode ne garantit pas que les développeurs exécutent Gitleaks.

Si vous voulez renforcer la sécurité il faudra implémenter également la deuxième solution.

Job de CI

Cette solution permet de détecter un secret qui a déjà leak (car il est désormais présent dans l’historique git) et de prendre en charge la remédiation. La première chose à faire lorsque l’on détecte un secret à ce niveau du workflow c’est de le révoquer car même un squash de commit et un force push ne vous permettent pas de supprimer le secret des références stockées par votre hébergeur de code.

gitleaks:
 stage: leaks
 image:
   name: zricethezav/gitleaks:latest
   entrypoint: [""]
 script:
   - gitleaks detect --verbose --redact --source="${CI_PROJECT_DIR}"

Le flag –redact ici est très important car il permet de ne pas afficher en clair dans les logs du job le contenu des leaks trouvés dans le repo et ainsi limiter leur diffusion.

Une fois revoqué vous pouvez ajouter le fingerprint de votre secret dans le fichier .gitleaksignore pour que Gitleaks ne le détecte plus.

Renovate

Renovate est lui aussi un outil gratuit et open source. Il permet d’automatiser la mise à jour des dépendances d’un projet. Il va pour cela chercher dans votre repo des fichiers type de déclarations de dépendances (requirements.txt, pyproject.toml, package.json). Dans ces fichiers il va alors récupérer la liste des dépendances et leurs versions associées, vérifier si des mises à jour sont disponibles et vous proposer une Merge Request avec les changements.

Setup

Renovate fournit également une image Docker. La création du job est donc très simple. Il suffit de spécifier l’image Renovate et de définir les quelques variables d’environnement nécessaires au run de Renovate.

Je définis ici le strict minimum pour que le job s’exécute.

J’ai également ajouté une règle sur l’exécution du job Renovate. Celui-ci n’a pas besoin de tourner à chaque commit mais plutôt de manière régulière et schedulée. C’est l’objet du bloc “rules” ci-dessous. Pour qu’il s’exécute il suffit ensuite de créer “pipeline schedule” dans Gitlab, lui définir un cron et le tour est joué.

renovate:
 image: renovate/renovate:slim
 stage: test
 variables:
   RENOVATE_ENDPOINT: $CI_API_V4_URL
   RENOVATE_PLATFORM: gitlab
   RENOVATE_TOKEN: $GITLAB_PROJECT_TOKEN
 script: renovate --autodiscover-filter="$CI_PROJECT_PATH" --autodiscover=true
 rules:
   - if: $CI_PIPELINE_SOURCE == "schedule"
     when: always
   - when: never

Au premier run Renovate va créer une MR d’onboarding qui ajoute un fichier de config dans le repo. Dans les commentaires de la MR Renovate vous indiquera aussi les fichiers de dépendances trouvés ainsi que les dépendances à mettre à jour.

En voici un aperçu :

Une fois cette MR mergée Renovate va maintenant utiliser la configuration présente dans le fichier renovate.json et proposer des MR lorsque qu’un dépendance n’est pas à jour. Voici un exemple :

Avec son changement

Pour aller plus loin

Il est possible de bien plus paramétrer son job. Voici quelques exemples de ce que vous pouvez faire :

  • Changer le nom des branches créées par Renovate (pour respecter des branch name rules)
  • Changer la config de onboarding
  • Regrouper certaines upgrade de dépendances en une seule MR
  • Auto-merge les upgrade de patch

Tous ces paramètres peuvent être passés en variables d’environnement ou en flag de la commande comme nous l’avons vu aujourd’hui mais aussi en fichier de config renovate.json 

Checkov

Checkov est un outil qui permet de scanner la configuration de votre infrastructure cloud pour détecter des anomalies, vous faire des recommandations de bonnes pratiques etc.

Comment ? Checkov va analyser votre IaC. Cela permet de régler d’éventuelles mauvaises configurations avant même qu’elles soient déployées. Dans notre cas d’usage du jour nous allons analyser du Terraform, mais Checkov fonctionne aussi avec CloudFormation, Kubernetes, Helm, ARM Templates et Serverless framework.

Setup

Voici l’implémentation du job dans la CI :

checkov-terraform:
 stage: test
 image:
   name: bridgecrew/checkov:latest
   entrypoint: [""]
 script:
   - checkov --framework terraform -b "${CI_COMMIT_BRANCH}" --directory "${CI_PROJECT_DIR}" --output cli

J’utilise encore une fois l’image docker fournie  par l’outil. Dans les flags de la commande checkov, je spécifie la branche à analyser (celle du commit), le directory (ici je scanne l’entièreté du repo mais j’aurais pu me limiter au répertoire iac)

Voici un exemple de recommandation que nous fait checkov :

On peut voir dans les logs l’ID de la règle et sa description, le bloc de code concerné ainsi qu’un lien d’exemple de remédiation. Dans ce cas précis nous n’avons pas spécifié de VPC pour notre Lambda function.

Pour aller plus loin

Il peut arriver que le contexte d’un projet ne nous permette pas de respecter les règles de checkov à la lettre. Dans ce cas, nous avons la possibilité d’ignorer certaines règles.

Prenons un autre exemple :

Ici Checkov nous conseille de mettre en place du “code-signing” pour s’assurer que le code exécuté par la lambda provient d’une source approuvée. Le problème est qu’ici le code de notre lambda est packagé dans une image docker et que le “code-signing” n’est pas possible dans ce cas de figure.

Nous allons donc ajouter un commentaire dans la ressource pour dire à Checkov de “skip” cette règle :

Ce qui nous donne dans les logs du scan :

Infracost

Le dernier outil que nous verrons dans cet article est Infracost.

Infracost permet d’avoir une estimation des coûts mensuels d’une infrasctructure cloud avant même de la déployer.

Comment ça fonctionne ? C’est très simple, Infracost va venir dans votre repo analyser votre IaC. Plus de 1000 ressources AWS, Azure et GCP sont déjà supportés. Le seul outil d’IaC supporté pour le moment est Terraform.

Setup

Nous allons voir deux implémentations possibles d’Infracost. La première avec une installation locale pour une utilisation en CLI, et la seconde intégrée dans une chaîne de CI/CD Gitlab CI.

Pour commencer vous devez suivre les instructions de mise en place décrites ici en fonction de votre système d’exploitation. Ceci se résume en deux étapes:

  • Installer la CLI infracost localement
  • Récupérer une API KEY

Cette dernière permet à Infracost de contacter son API pour récupérer les tarifs des différentes ressources.

Passons maintenant à son utilisation.

CLI

Une fois installé, vous pouvez lancer la commande suivante en spécifiant le path vers votre répertoire d’IaC. Infracost vous donnera une estimation du coût de l’infrastructure gérée par ce code.

infracost breakdown --path iac

Voici un exemple de résultat de cette commande :

On peut y voir la liste des ressources coûteuses qui seront ou sont déployées par ce code, leur coût ainsi que le total. On voit également qu’il y a des ressources gratuites. Il est possible de les lister en ajoutant le flag “–show-skipped” à la commande. Dans ce cas précis nous avons par exemple un “aws_iam_role”.

CI/CD

Voyons maintenant comment intégrer cet outil dans votre chaîne de CI/CD. Il est possible de l’utiliser comme en CLI pour estimer les coûts globaux d’une infra au niveau d’une branche de feature ou de la branche principale par exemple, mais c’est sur les Merge Requests (MR) qu’infracost devient vraiment intéressant en CI. 

Le job Infracost sur MR permet d’estimer les coûts supplémentaires qu’apportent des changements dans l’infrastructure avant qu’il ne soient “merged” sur la branche principale. Ce job va poster un récapitulatif en commentaire de cette MR, ce qui donne une vision plus rapide et “user friendly” que celle fournie dans les logs de la commande. 

Passons tout de suite à l’implémentation de ce job :

infracost:merge-request-checks:
 stage: infracost
 image:
   name: infracost/infracost:ci-0.10
   entrypoint: [""]
 script:
   - git clone $CI_REPOSITORY_URL --branch=$CI_MERGE_REQUEST_TARGET_BRANCH_NAME --single-branch /tmp/base
   # Generate an Infracost cost snapshot from the comparison branch, so that Infracost can compare the cost difference.
   - |
     infracost breakdown --path=/tmp/base/${TF_ROOT} \
                         --format=json \
                         --out-file=infracost-base.json


   # Generate an Infracost diff and save it to a JSON file.
   - |
     infracost diff --path=${TF_ROOT} \
                    --compare-to=infracost-base.json \
                    --format=json \
                    --out-file=infracost.json
   - |
     infracost comment gitlab --path=infracost.json \
                              --repo=$CI_PROJECT_PATH \
                              --merge-request=$CI_MERGE_REQUEST_IID \
                              --gitlab-server-url=$CI_SERVER_URL \
                              --gitlab-token=$GITLAB_TOKEN \
                              --behavior=update
 variables:
   GITLAB_TOKEN: $GITLAB_TOKEN
 rules:
   - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

Ici le job va dans un premier temps cloner la branche cible de la MR dans un répertoire temporaire, faire une estimation des coûts actuels et stocker le résultat dans un fichier nommé “infracost-base.json”. Ensuite infracost va venir comparer le résultat avec avec la branche source de la MR et en tirer la différence de coûts. Pour finir, le résultat est envoyé dans un commentaire de la MR.

Voici ce que cela nous donne :

Ici on voit donc que les changements de cette merge request vont apporter un surcoût de 272$ à mon infrastructure. On y voit aussi le nouveau coût mensuel.

Conclusion

L’adoption d’une approche CI/CD dans vos projets de développement cloud est essentielle et offre une voie prometteuse vers l’amélioration continue du développement et de la sécurité de votre application. La liste des outils abordés aujourd’hui ne représente que ceux avec lesquels j’ai eu l’habitude de travailler

Il existe bien sûr énormément d’outils open source intéressants à mettre en place mais avec cette liste vous avez une bonne base utilisable sur tous vos projets pour vous lancer.

J’espère que cet article vous donnera des idées pour vos futures chaînes de CI/CD.

Commentaires :

A lire également sur le sujet :