Lección 2 — Tester

Lint et analyse de sécurité

6 min de lectura

Este contenido aún no está traducido. Se muestra la versión original.

Une PR ajoute un module cache : une variable ttl jamais utilisée, une sortie bucketName sans description, et un bucket sans public_access_prevention. Les tests natifs passent, car ils ne testent que ce que leurs auteurs ont pensé à tester. Le relecteur voit les deux premiers défauts, pas le troisième, qui ne se remarque que par ce qui manque. Un linter et un analyseur de sécurité lisent ce que personne ne relit : chaque ressource, contre une liste de règles connues.

Le mécanisme

flowchart LR
  CODE["infra/**/*.tf"] --> TFL["tflint<br/>plugin terraform (recommended)<br/>plugin google 0.40.0"]
  CODE --> CK["checkov -d infra<br/>règles CKV_GCP_*"]
  TFL -- "conventions, inutilisé,<br/>valeurs Google invalides" --> R1["0 problème attendu"]
  CK -- "sécurité : accès public,<br/>chiffrement, journaux" --> R2["Failed checks: 0"]
  SKIP["checkov:skip=ID:justification<br/>dans le code"] -.-> CK

Les deux outils ne se recouvrent pas :

tflintcheckov
Question posée« ce code est-il correct et lisible ? »« cette configuration est-elle sûre ? »
Exemplesvariable inutilisée, sortie non documentée, type de machine inexistantbucket sans public_access_prevention, registre sans CMEK
Lit les modules appelésoui (call_module_type = "local")oui, avec les valeurs passées par la racine
Exception# tflint-ignore: règle# checkov:skip=ID:raison

tflint --recursive parcourt chaque dossier mais lit la configuration depuis le dossier courant : .tflint.hcl est donc passé par --config $(CURDIR)/.tflint.hcl, sinon les sous-dossiers tournent avec les règles par défaut, sans le plugin google.

La mise en œuvre

files/.tflint.hcl
config {
  call_module_type = "local"
}

plugin "terraform" {
  enabled = true
  preset  = "recommended"
}

plugin "google" {
  enabled = true
  version = "0.40.0"
  source  = "github.com/terraform-linters/tflint-ruleset-google"
}

rule "terraform_naming_convention" { enabled = true }
rule "terraform_documented_outputs" { enabled = true }

La version du plugin est épinglée : une nouvelle version ajoute des règles, et une CI qui casse un lundi sans changement de code fait perdre confiance dans l'outil. make setup exécute tflint --init ; en CI, il lui faut GITHUB_TOKEN pour ne pas buter sur la limite de l'API GitHub.

checkov s'installe dans le .venv du projet (checkov==3.3.20 dans requirements.txt). Trois exceptions sont posées dans le code, à côté de la ressource, avec leur raison :

files/infra/modules/artifact-registry/main.tf (extrait)
resource "google_artifact_registry_repository" "this" {
  # checkov:skip=CKV_GCP_84:chiffrement géré par Google accepté (images sans donnée client) ; CMEK à ajouter si la politique de l'entreprise l'impose

Une exception sans raison est refusée en revue. La raison dit pourquoi le risque est accepté et ce qui la ferait tomber.

make lint
make security

Résultat obtenu (2026-10-16)

Sur le code du lab :

$ make lint
tflint : OK
$ make security
Passed checks: 33, Failed checks: 0, Skipped checks: 5

Les 5 exceptions sont 3 commentaires dans le code, comptés une fois par chemin d'appel (le module buckets est appelé par dev et par prod). La PR du « module cache », reproduite sur une copie du module buckets :

$ tflint --recursive --format compact
4 issue(s) found:
infra/modules/buckets/outputs.tf:5:1: Notice - `bucketName` output has no description (terraform_documented_outputs)
infra/modules/buckets/outputs.tf:5:1: Notice - output name `bucketName` must match the following format: snake_case (terraform_naming_convention)
infra/modules/buckets/outputs.tf:9:1: Warning - `inutilisee` variable has no type (terraform_typed_variables)
infra/modules/buckets/outputs.tf:9:1: Warning - variable "inutilisee" is declared but not used (terraform_unused_declarations)

$ checkov -d . --framework terraform --compact    # bucket sans public_access_prevention ni accès uniforme
Passed checks: 0, Failed checks: 3, Skipped checks: 1
Check: CKV_GCP_29: "Ensure that Cloud Storage buckets have uniform bucket-level access enabled"
Check: CKV_GCP_114: "Ensure public access prevention is enforced on Cloud Storage bucket"
Check: CKV_GCP_78: "Ensure Cloud storage has versioning enabled"

Le troisième échec est instructif : analysé seul, le module a versioning = each.value.versioning, valeur inconnue ; checkov la considère comme absente. Analysé depuis la racine (-d infra), avec les valeurs réelles, il passe. Analysez toujours depuis les racines.

Ce qui casse en production

  • Le linter qu'on fait taire. Trente avertissements au premier passage, on ajoute --minimum-failure-severity=error, plus rien ne remonte jamais. Parade : partir de zéro problème sur la branche principale, puis bloquer toute PR qui en ajoute un.
  • L'exception globale. --skip-check CKV_GCP_62 en ligne de commande : la règle disparaît pour tout le dépôt, y compris pour un futur bucket de données client. Parade : checkov:skip sur la ressource concernée, avec la raison.
  • Plugin non épinglé. tflint --init télécharge la dernière version du ruleset ; une règle nouvelle casse la CI sans changement de code. Parade : version = "0.40.0", et une mise à jour volontaire.
  • Faux positifs sur des valeurs inconnues. Un module analysé hors contexte échoue sur des règles qui passent avec les vraies valeurs (CKV_GCP_78 ci-dessus). Parade : analyser depuis infra/ ; et vérifier aussi le plan (leçon 3), où toutes les valeurs sont résolues.

La règle de décision

tflint et checkov à chaque PR, à zéro problème, en quelques secondes. Une exception = un commentaire sur la ressource avec une raison, relu comme du code. N'écrivez pas de règle maison dans checkov pour ce qui relève de la politique de l'entreprise (régions, labels) : ces règles-là dépendent de valeurs résolues, elles vont sur le plan (leçon 3).

Pratique

checkov signale CKV_GCP_62 (journaux d'accès) sur le bucket d'archive. L'équipe sécurité confirme que les journaux d'audit Data Access de Cloud Storage sont activés au niveau de l'organisation. Que faites-vous ?

En CI, tflint passe sur infra/modules/buckets mais signale terraform_documented_outputs sur votre poste. Même version de tflint. Cause probable ?

Exercice. Activez dans .tflint.hcl la règle qui impose une description à chaque variable, lancez make lint, et corrigez ce qui remonte.

Voir la solution
rule "terraform_documented_variables" {
  enabled = true
}

make lint signale alors chaque variable sans description (75 signalements sur ce dépôt, racines et modules : project_id, region…). Ajoutez une description d'une ligne qui dit ce que la valeur change, pas ce qu'elle est (« Région de toutes les ressources ; europe-* uniquement »). Relancez jusqu'à tflint : OK.

  • Je configure tflint avec un plugin épinglé et je le lance en --recursive avec une configuration absolue.
  • Je lance checkov depuis les racines et je lis un échec.
  • J'écris une exception justifiée sur une ressource, jamais globale.
  • Je sais quelles règles vont dans checkov et lesquelles vont sur le plan.
Siguiente