Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
| eadl:bloc4:fm4:td3 [2026/06/24 07:55] – [1. Analyse de l'existant] jcheron | eadl:bloc4:fm4:td3 [2026/06/25 03:35] (Version actuelle) – jcheron | ||
|---|---|---|---|
| Ligne 5: | Ligne 5: | ||
| * Supprimer les secrets en clair dans une infrastructure Terraform | * Supprimer les secrets en clair dans une infrastructure Terraform | ||
| * Utiliser AWS Secrets Manager pour stocker des identifiants sensibles | * Utiliser AWS Secrets Manager pour stocker des identifiants sensibles | ||
| - | * Activer le chiffrement des données au repos sur RDS et S3 | + | |
| + | | ||
| * Comprendre la différence entre chiffrement au repos et en transit | * Comprendre la différence entre chiffrement au repos et en transit | ||
| * Mettre en place une traçabilité avec CloudTrail | * Mettre en place une traçabilité avec CloudTrail | ||
| * Identifier une action suspecte via les logs | * Identifier une action suspecte via les logs | ||
| + | * Récupérer des ressources existantes via des data sources AWS | ||
| ===== Contexte ===== | ===== Contexte ===== | ||
| Ligne 18: | Ligne 20: | ||
| Un nouvel audit de sécurité interne est réalisé. | Un nouvel audit de sécurité interne est réalisé. | ||
| - | Trois failles critiques sont identifiées malgré les corrections du TD2 : | + | Quatre |
| * le mot de passe de la base de données est stocké en clair dans `instances.tf` | * le mot de passe de la base de données est stocké en clair dans `instances.tf` | ||
| * la base de données RDS n'est pas chiffrée au repos | * la base de données RDS n'est pas chiffrée au repos | ||
| + | * le bucket S3 applicatif n'est pas chiffré | ||
| * aucune trace des actions réalisées sur le compte AWS n'est conservée | * aucune trace des actions réalisées sur le compte AWS n'est conservée | ||
| - | Ces trois points constituent des non-conformités bloquantes pour toute certification de sécurité (ISO 27001, SOC 2, RGPD). | + | Ces points constituent des non-conformités bloquantes pour toute certification de sécurité (ISO 27001, SOC 2, RGPD). |
| - | Votre mission est de corriger ces trois failles en prolongeant l' | + | Votre mission est de corriger ces failles en prolongeant l' |
| ===== Pré-requis ===== | ===== Pré-requis ===== | ||
| * Projet Terraform du TD2 déployé et fonctionnel | * Projet Terraform du TD2 déployé et fonctionnel | ||
| + | * Ressources TD2 taguées avec `Name = " | ||
| * ALB opérationnel, | * ALB opérationnel, | ||
| - | * Accès AWS avec droits suffisants : IAM, Secrets Manager, S3, RDS, CloudTrail | + | * Accès AWS avec droits suffisants : IAM, Secrets Manager, KMS, S3, RDS, CloudTrail |
| * Terraform en version 1.5 ou supérieure | * Terraform en version 1.5 ou supérieure | ||
| ===== Structure du projet ===== | ===== Structure du projet ===== | ||
| - | Le TD3 s' | + | Le TD3 est un projet Terraform indépendant. |
| - | <sxh bash; | + | Il ne modifie pas le code du TD2. |
| - | td3/ | + | |
| - | ├── main.tf | + | |
| - | ├── variables.tf | + | |
| - | ├── outputs.tf | + | |
| - | ├── providers.tf | + | |
| - | └── modules/ | + | |
| - | ├── secrets/ | + | |
| - | │ | + | |
| - | ├── database/ | + | |
| - | │ | + | |
| - | ├── storage/ | + | |
| - | │ | + | |
| - | └── logging/ | + | |
| - | └── main.tf | + | |
| - | </ | + | |
| - | Le dossier `td2/` n'est pas modifié directement. | + | Il récupère les ressources existantes via des data sources AWS interrogeant l'API par tags. |
| - | Les ressources existantes du TD2 sont récupérées via des data sources Terraform. | + | Fichier : `td3/ |
| + | <sxh bash> | ||
| + | tp-aws/ | ||
| + | ├── td1/ # IAM – roles, policies | ||
| + | ├── td2/ # Réseau – VPC, subnets, ALB, Security Groups | ||
| + | └── td3/ # Secrets, KMS, chiffrement, | ||
| + | ├── main.tf | ||
| + | ├── variables.tf | ||
| + | ├── outputs.tf | ||
| + | └── modules/ | ||
| + | ├── secrets/ | ||
| + | │ | ||
| + | │ | ||
| + | │ | ||
| + | ├── kms/ | ||
| + | │ | ||
| + | │ | ||
| + | │ | ||
| + | ├── storage/ | ||
| + | │ | ||
| + | │ | ||
| + | │ | ||
| + | └── logging/ | ||
| + | ├── main.tf | ||
| + | ├── variables.tf | ||
| + | └── outputs.tf | ||
| + | </ | ||
| - | <WRAP round question> | + | ===== 1. Récupération |
| - | Pourquoi utilise-t-on | + | |
| - | Que se passerait-il si on déclarait | + | Objectif : comprendre comment un projet Terraform peut interroger |
| - | </ | + | |
| - | ===== 1. Analyse de l' | + | TD3 utilise des data sources AWS natifs. |
| - | <WRAP round todo> | + | Ces data sources envoient des requêtes directement à l'API AWS et filtrent les ressources par tags. |
| - | Ouvrir le fichier `td2/ | + | |
| - | Localiser la ressource `aws_db_instance.db`. | + | Cela suppose que les ressources TD2 ont été correctement taguées. |
| - | </ | + | |
| <WRAP round question> | <WRAP round question> | ||
| - | Relever | + | Pourquoi TD3 ne peut-il pas utiliser directement |
| - | Pour chaque problème, indiquer : | + | Quelle |
| - | * ce qui est en cause dans le code | + | |
| - | * ce qu'un attaquant ou un développeur malveillant pourrait faire | + | |
| - | * si ce problème est visible dans Git | + | |
| </ | </ | ||
| - | <WRAP round question> | + | Fichier : `td3/main.tf` |
| - | Le fichier | + | <sxh js> |
| + | provider " | ||
| + | region = " | ||
| + | } | ||
| - | Un développeur a poussé ce fichier il y a trois mois sur le dépôt de l' | + | # Récupération du VPC créé dans TD2 |
| + | data " | ||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| - | Le mot de passe a été changé depuis, mais il reste dans l' | + | # Récupération du subnet privé créé |
| + | data " | ||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| - | Comment retrouver ce mot de passe dans l' | + | # Récupération du subnet privé secondaire créé |
| + | data " | ||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| - | Pourquoi changer le mot de passe ne suffit-il pas si le fichier a déjà été commité ? | + | # Récupération du Security Group RDS créé dans TD2 |
| + | data " | ||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | # Récupération du Security Group de l' | ||
| + | data " | ||
| + | tags = { | ||
| + | Name = "td2-sg-app" | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | <WRAP round todo> | ||
| + | Vérifier dans le code TD2 que chaque ressource est bien taguée avec les valeurs attendues ci-dessus. | ||
| + | |||
| + | Si un tag est absent ou différent, le data source échouera avec une erreur `no matching resource found`. | ||
| + | |||
| + | Corriger les tags dans TD2 si nécessaire avant de poursuivre. | ||
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | La ressource | + | Que se passe-t-il si deux ressources AWS portent le même tag `Name` dans la même région |
| - | + | ||
| - | Quelle est la valeur par défaut de ce paramètre dans AWS ? | + | |
| - | Que signifie concrètement une base non chiffrée au repos ? | + | Quel comportement Terraform adopte-t-il dans ce cas ? |
| </ | </ | ||
| - | ===== 2. Declaration des providers | + | ===== 2. Identification du problème |
| - | Le module `storage` utilisera le provider `random` pour générer des suffixes uniques sur les noms de buckets S3. | + | Objectif : identifier |
| - | Les noms de buckets S3 sont globaux sur AWS : deux comptes ne peuvent pas avoir le même nom. | + | Voici un extrait du fichier `instances.tf` tel qu'il existe après |
| - | <WRAP round todo> | + | Fichier : `td2/ |
| - | Créer le fichier de déclaration des providers. | + | <sxh js> |
| - | </ | + | resource " |
| + | identifier | ||
| + | engine | ||
| + | instance_class | ||
| + | | ||
| - | Fichier : `td3/ | + | username |
| - | <sxh hcl> | + | |
| - | terraform { | + | |
| - | required_providers { | + | |
| - | aws = { | + | |
| - | source | + | |
| - | | + | |
| - | } | + | |
| - | random = { | + | |
| - | source | + | |
| - | version = "~> 3.0" | + | |
| - | } | + | |
| - | } | + | |
| - | } | + | |
| - | provider " | + | skip_final_snapshot |
| - | region | + | |
| } | } | ||
| </ | </ | ||
| - | <WRAP round help> | + | <WRAP round question> |
| - | Pourquoi faut-il déclarer explicitement le provider `random` alors qu'il ne communique pas avec AWS ? | + | Identifier au moins trois problèmes de sécurité dans ce bloc. |
| - | Que se passe-t-il si on l' | + | Pour chaque problème, expliquer la conséquence concrète en cas de fuite du fichier |
| </ | </ | ||
| - | ===== 3. Declaration des variables ===== | + | <WRAP round todo> |
| + | Ne pas appliquer ce fichier tel quel. | ||
| - | Le mot de passe initial doit être injecté au moment de l' | + | Ne pas modifier le TD2 non plus. |
| - | Il ne doit jamais apparaître | + | La correction se fait uniquement |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Créer le fichier de variables. | + | |
| </ | </ | ||
| - | Fichier : `td3/ | + | ===== 3. Création du module secrets ===== |
| - | < | + | |
| - | variable "db_initial_password" { | + | Objectif : stocker les identifiants de la base de données dans AWS Secrets Manager. |
| - | description = "Mot de passe initial | + | |
| + | Fichier : `td3/ | ||
| + | < | ||
| + | variable "secret_name" { | ||
| + | type = string | ||
| + | description = "Nom du secret dans Secrets Manager" | ||
| + | default | ||
| + | } | ||
| + | |||
| + | variable " | ||
| + | type = string | ||
| + | description = "Nom d' | ||
| + | default | ||
| + | } | ||
| + | |||
| + | variable "db_password" | ||
| type = string | type = string | ||
| + | description = "Mot de passe de la base de données" | ||
| sensitive | sensitive | ||
| } | } | ||
| </ | </ | ||
| - | <WRAP round todo> | + | Fichier : `td3/ |
| - | Créer le fichier de valeurs local pour les tests. | + | <sxh js> |
| - | </ | + | resource " |
| + | name = var.secret_name | ||
| + | | ||
| - | Fichier : `td3/ | + | tags = { |
| - | <sxh hcl> | + | Name = "td3-secret-db" |
| - | db_initial_password | + | |
| + | } | ||
| + | } | ||
| + | |||
| + | resource " | ||
| + | secret_id = aws_secretsmanager_secret.db_credentials.id | ||
| + | |||
| + | secret_string = jsonencode({ | ||
| + | username = var.db_username | ||
| + | password = var.db_password | ||
| + | }) | ||
| + | } | ||
| </ | </ | ||
| - | <WRAP round todo> | + | Fichier : `td3/ |
| - | Créer le fichier | + | <sxh js> |
| - | </WRAP> | + | output " |
| + | value = aws_secretsmanager_secret.db_credentials.arn | ||
| + | description = "ARN du secret contenant les identifiants de la base de données" | ||
| + | } | ||
| - | Fichier : `td3/ | + | output " |
| - | <sxh bash> | + | |
| - | *.tfvars | + | |
| - | *.tfstate | + | } |
| - | *.tfstate.backup | + | |
| - | .terraform/ | + | |
| - | .terraform.lock.hcl | + | |
| </ | </ | ||
| - | <WRAP round help> | + | <WRAP round question> |
| - | Le paramètre `sensitive = true` est déclaré | + | Le paramètre `sensitive = true` sur la variable |
| - | Est-ce que cela empêche | + | Cela signifie-t-il que le mot de passe est chiffré |
| - | Pourquoi | + | Où le mot de passe est-il |
| </ | </ | ||
| - | <WRAP round help> | + | <WRAP round question> |
| - | En production, on n'utilise | + | Le paramètre `recovery_window_in_days = 7` signifie que le secret |
| - | Comment injecter une valeur sensible sans fichier, uniquement via le terminal | + | Pourquoi AWS impose-t-il cette fenêtre de récupération par défaut |
| - | Quelle variable d' | + | Comment forcer la suppression immédiate si nécessaire, |
| </ | </ | ||
| - | ===== 4. Recuperation des ressources existantes du TD2 ===== | + | ===== 4. Erreur volontaire – secret introuvable |
| - | Plutôt que de recréer les ressources, on les référence via des data sources. | + | Objectif : comprendre ce qui se passe quand une application tente de lire un secret inexistant. |
| - | <WRAP round todo> | + | Voici un data source intentionnellement incorrect. |
| - | Créer le fichier principal du TD3. | + | |
| - | </ | + | |
| - | Fichier : `td3/main.tf` | + | Fichier : `td3/debug/ |
| - | < | + | < |
| - | # Recuperation du VPC existant du TD2 | + | data "aws_secretsmanager_secret" "db_credentials" { |
| - | data " | + | name = "td3/db/credential" |
| - | filter { | + | |
| - | name = " | + | |
| - | values = [" | + | |
| - | } | + | |
| - | } | + | |
| - | + | ||
| - | # Recuperation du Security Group du backend TD2 | + | |
| - | data " | + | |
| - | filter { | + | |
| - | name = " | + | |
| - | values = [" | + | |
| - | } | + | |
| - | vpc_id = data.aws_vpc.td2.id | + | |
| - | } | + | |
| - | + | ||
| - | # Recuperation du subnet group RDS existant | + | |
| - | data "aws_db_subnet_group" "main" { | + | |
| - | name = "td2-db-subnet-group" | + | |
| } | } | ||
| </ | </ | ||
| - | <WRAP round help> | + | <WRAP round todo> |
| - | Une data source ne crée rien dans AWS. | + | Lire le code ci-dessus sans l' |
| - | Que fait Terraform quand il rencontre un bloc `data` lors d'un `terraform plan` ? | + | Identifier l'erreur sans regarder la réponse ci-dessous. |
| - | Que se passe-t-il si la ressource référencée n' | + | Écrire |
| </ | </ | ||
| - | ===== 5. Module secrets ===== | + | Le nom du secret dans Secrets Manager est `td3/ |
| - | Objectif : stocker le mot de passe dans AWS Secrets Manager, pas dans le code. | + | Le data source ci-dessus cherche `td3/ |
| - | <WRAP round todo> | + | L' |
| - | Créer | + | |
| + | Fichier : `td3/ | ||
| + | <sxh bash> | ||
| + | Error: reading Secrets Manager Secret: ResourceNotFoundException: | ||
| + | Secrets Manager can't find the specified secret. | ||
| + | </ | ||
| + | |||
| + | <WRAP round question> | ||
| + | Ce type d' | ||
| + | |||
| + | Citer au moins deux situations réelles où cette erreur peut apparaître même si le nom est correct. | ||
| </ | </ | ||
| - | Fichier : `td3/ | + | ===== 5. Récupération du secret dans RDS ===== |
| - | <sxh hcl> | + | |
| - | variable " | + | |
| - | type | + | |
| - | sensitive | + | |
| - | } | + | |
| - | resource " | + | Objectif : utiliser le secret Secrets Manager dans la configuration RDS plutôt que des valeurs en clair. |
| - | name = " | + | |
| - | description | + | |
| - | recovery_window_in_days = 0 | + | |
| - | } | + | |
| - | resource | + | Fichier : `td3/ |
| + | <sxh js> | ||
| + | data " | ||
| secret_id = aws_secretsmanager_secret.db_credentials.id | secret_id = aws_secretsmanager_secret.db_credentials.id | ||
| - | | + | |
| - | username = " | + | |
| - | password = var.db_password | + | |
| - | }) | + | |
| } | } | ||
| - | output " | + | locals |
| - | | + | |
| - | | + | |
| + | ) | ||
| } | } | ||
| </ | </ | ||
| - | <WRAP round help> | + | <WRAP round question> |
| - | Le secret est stocké au format JSON avec deux champs : `username` et `password`. | + | Pourquoi utilise-t-on |
| - | + | ||
| - | Pourquoi utilise-t-on | + | |
| - | Quel avantage | + | Que se passerait-il |
| </ | </ | ||
| - | <WRAP round help> | + | ===== 6. Création du module KMS ===== |
| - | Le paramètre `recovery_window_in_days | + | |
| - | Quelle est la valeur | + | Objectif : créer une clé de chiffrement gérée |
| - | Pourquoi cette valeur par défaut existe-t-elle en production ? | + | < |
| - | </WRAP> | + | Avant de créer la clé, répondre aux questions suivantes : |
| - | <WRAP round todo> | + | Quelle est la différence entre une clé AWS managée et une CMK (Customer Managed Key) ? |
| - | Ajouter l' | + | |
| + | Dans quel cas une CMK est-elle obligatoire plutôt que recommandée ? | ||
| </ | </ | ||
| - | Fichier : `td3/main.tf` — ajouter après les data sources | + | Fichier : `td3/modules/ |
| - | < | + | < |
| - | module | + | variable |
| - | | + | |
| - | | + | description |
| + | default | ||
| + | } | ||
| + | |||
| + | variable " | ||
| + | | ||
| + | description = "ID du compte AWS courant" | ||
| } | } | ||
| </ | </ | ||
| - | ===== 6. Probleme volontaire | + | Fichier |
| + | <sxh js> | ||
| + | data " | ||
| - | <WRAP round todo> | + | resource " |
| - | Avant d' | + | description |
| + | deletion_window_in_days = 10 | ||
| + | enable_key_rotation | ||
| + | |||
| + | policy = jsonencode({ | ||
| + | Version = " | ||
| + | Statement = [ | ||
| + | { | ||
| + | Sid = " | ||
| + | Effect = " | ||
| + | Principal = { | ||
| + | AWS = "arn:aws: | ||
| + | } | ||
| + | Action | ||
| + | Resource = " | ||
| + | } | ||
| + | ] | ||
| + | }) | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | TD = " | ||
| + | } | ||
| + | } | ||
| - | <sxh hcl> | + | resource |
| - | data "aws_secretsmanager_secret_version" "test" { | + | |
| - | | + | target_key_id = aws_kms_key.main.key_id |
| } | } | ||
| </ | </ | ||
| - | Ajouter ce bloc temporairement dans `main.tf` et lancer : | + | Fichier : `td3/ |
| + | <sxh js> | ||
| + | output " | ||
| + | value = aws_kms_key.main.arn | ||
| + | description = "ARN de la clé KMS" | ||
| + | } | ||
| - | <sxh bash> | + | output " |
| - | terraform plan | + | |
| + | description = "ID de la clé KMS" | ||
| + | } | ||
| </ | </ | ||
| - | </ | ||
| - | <WRAP round help> | + | <WRAP round question> |
| - | Que se passe-t-il lors du plan ? | + | Le paramètre `enable_key_rotation = true` active la rotation automatique de la clé. |
| - | Terraform signale-t-il | + | Que signifie concrètement la rotation d'une clé KMS ? |
| - | Pourquoi Terraform ne peut-il pas garantir que le secret existe au moment où la data source est évaluée | + | Les données chiffrées avec l' |
| </ | </ | ||
| - | <WRAP round help> | + | <WRAP round question> |
| - | Quelle est la différence fondamentale entre un bloc `resource` et un bloc `data` dans le cycle d' | + | La policy KMS donne accès à `arn: |
| - | Lequel | + | Pourquoi cette entrée |
| - | </ | + | |
| - | <WRAP round todo> | + | Que se passe-t-il si elle est absente ? |
| - | Supprimer ce bloc temporaire avant de continuer. | + | |
| </ | </ | ||
| - | ===== 7. Module database | + | ===== 7. Création du module storage |
| - | Objectif : remplacer la ressource | + | Objectif : déployer une instance |
| - | La dépendance entre le secret et la base est gérée implicitement via le passage de l'ARN en variable. | + | Fichier : `td3/ |
| + | <sxh js> | ||
| + | variable " | ||
| + | type = string | ||
| + | description = "ARN de la clé KMS utilisée pour le chiffrement" | ||
| + | } | ||
| - | Terraform comprend qu'il doit créer le secret avant de lire son contenu car la valeur vient d'un output du module `secrets`. | + | variable "db_username" { |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Créer le module base de données. | + | |
| - | </ | + | |
| - | + | ||
| - | Fichier : `td3/ | + | |
| - | <sxh hcl> | + | |
| - | variable "secret_arn" { | + | |
| type = string | type = string | ||
| - | description = "ARN du secret contenant les identifiants de la base" | + | description = "Nom d' |
| } | } | ||
| - | variable "db_subnet_group_name" { | + | variable "db_password" { |
| type = string | type = string | ||
| - | description = "Nom du subnet group RDS existant" | + | description = "Mot de passe transmis depuis Secrets Manager" |
| + | sensitive | ||
| } | } | ||
| - | variable "vpc_security_group_ids" { | + | variable "subnet_ids" { |
| type = list(string) | type = list(string) | ||
| - | description = "Liste des Security Group IDs autorises a acceder a la base" | + | description = "Liste des IDs de subnets privés récupérés depuis TD2" |
| } | } | ||
| - | data "aws_secretsmanager_secret_version" | + | variable |
| - | | + | |
| + | description = "ID du Security Group RDS récupéré depuis TD2" | ||
| } | } | ||
| - | locals | + | variable " |
| - | | + | |
| + | description = "Nom du bucket S3 applicatif" | ||
| } | } | ||
| + | </ | ||
| - | resource "aws_db_instance" " | + | Fichier : `td3/ |
| - | identifier | + | <sxh js> |
| + | resource "aws_db_subnet_group" "main" { | ||
| + | name = " | ||
| + | subnet_ids = var.subnet_ids | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | TD = " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | resource " | ||
| + | identifier | ||
| engine | engine | ||
| engine_version | engine_version | ||
| Ligne 381: | Ligne 470: | ||
| allocated_storage = 20 | allocated_storage = 20 | ||
| - | | + | username = var.db_username |
| - | | + | password = var.db_password |
| - | password = local.db_creds[" | + | |
| - | db_subnet_group_name | + | db_subnet_group_name |
| - | vpc_security_group_ids = var.vpc_security_group_ids | + | vpc_security_group_ids = [var.security_group_id] |
| + | |||
| + | storage_encrypted = true | ||
| + | kms_key_id | ||
| - | storage_encrypted | ||
| skip_final_snapshot = true | skip_final_snapshot = true | ||
| tags = { | tags = { | ||
| - | Name = "td3-database" | + | Name = "td3-db" |
| + | TD = "td3" | ||
| } | } | ||
| } | } | ||
| - | output " | + | resource " |
| - | description = " | + | bucket = var.bucket_name |
| - | value = aws_db_instance.main.endpoint | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Ajouter l' | + | |
| - | </ | + | |
| - | + | ||
| - | Fichier : `td3/ | + | |
| - | <sxh hcl> | + | |
| - | module " | + | |
| - | source | + | |
| - | secret_arn | + | |
| - | + | ||
| - | db_subnet_group_name | + | |
| - | vpc_security_group_ids = [data.aws_security_group.backend.id] | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round help> | + | |
| - | La valeur `module.secrets.secret_arn` est transmise en variable au module database. | + | |
| - | + | ||
| - | Terraform en déduit-il automatiquement que le module `database` dépend du module `secrets` ? | + | |
| - | + | ||
| - | Dans quel cas faudrait-il ajouter un `depends_on` explicite malgré tout ? | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round help> | + | |
| - | Le paramètre `storage_encrypted = true` a été ajouté. | + | |
| - | + | ||
| - | Que protège exactement ce chiffrement ? | + | |
| - | + | ||
| - | Ce chiffrement protège-t-il les données qui transitent entre le backend et la base de données ? | + | |
| - | + | ||
| - | Quel mécanisme complémentaire protège les données en transit vers RDS ? | + | |
| - | </ | + | |
| - | + | ||
| - | ===== 8. Module storage ===== | + | |
| - | + | ||
| - | Objectif : créer un bucket S3 avec chiffrement et blocage des accès publics. | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Créer le module de stockage. | + | |
| - | </ | + | |
| - | + | ||
| - | Fichier : `td3/ | + | |
| - | <sxh hcl> | + | |
| - | resource " | + | |
| - | byte_length = 4 | + | |
| - | } | + | |
| - | + | ||
| - | resource " | + | |
| - | bucket = " | + | |
| tags = { | tags = { | ||
| - | Name = "td3-secure-data" | + | Name = "td3-s3-app" |
| + | TD = "td3" | ||
| } | } | ||
| } | } | ||
| - | resource " | + | resource " |
| - | bucket = aws_s3_bucket.secure_data.id | + | bucket = aws_s3_bucket.app.id |
| rule { | rule { | ||
| apply_server_side_encryption_by_default { | apply_server_side_encryption_by_default { | ||
| - | sse_algorithm = "AES256" | + | sse_algorithm |
| + | kms_master_key_id = var.kms_key_arn | ||
| } | } | ||
| bucket_key_enabled = true | bucket_key_enabled = true | ||
| Ligne 468: | Ligne 508: | ||
| } | } | ||
| - | resource " | + | resource " |
| - | bucket = aws_s3_bucket.secure_data.id | + | bucket = aws_s3_bucket.app.id |
| block_public_acls | block_public_acls | ||
| Ligne 475: | Ligne 515: | ||
| ignore_public_acls | ignore_public_acls | ||
| restrict_public_buckets = true | restrict_public_buckets = true | ||
| - | } | ||
| - | |||
| - | output " | ||
| - | description = "Nom du bucket S3 securise" | ||
| - | value = aws_s3_bucket.secure_data.id | ||
| } | } | ||
| </ | </ | ||
| - | <WRAP round todo> | + | <WRAP round question> |
| - | Ajouter | + | Le chiffrement `storage_encrypted = true` sur RDS protège-t-il les données en transit ou au repos ? |
| + | |||
| + | Quel mécanisme protège les données en transit entre l'application et RDS ? | ||
| </ | </ | ||
| - | Fichier : `td3/ | + | <WRAP round question> |
| - | <sxh hcl> | + | Le paramètre `bucket_key_enabled |
| - | module " | + | |
| - | source | + | |
| - | } | + | |
| - | </ | + | |
| - | <WRAP round help> | + | Rechercher dans la documentation AWS ce que cette option apporte. |
| - | La configuration `apply_server_side_encryption_by_default` est activée. | + | |
| - | Que se passe-t-il si un développeur uploade un fichier sans spécifier de chiffrement dans son appel SDK ou CLI ? | + | Quel est l'impact sur les coûts et les performances |
| - | + | ||
| - | La configuration par défaut s'applique-t-elle automatiquement | + | |
| </ | </ | ||
| - | <WRAP round help> | + | ===== 8. Erreur volontaire – chiffrement impossible ===== |
| - | Les quatre paramètres `block_public_acls`, | + | |
| - | Les Security Groups du TD2 contrôlent déjà le trafic réseau vers les instances. | + | Objectif : comprendre pourquoi RDS ne peut pas chiffrer une instance existante non chiffrée. |
| - | Pourquoi bloquer également les accès publics au niveau du bucket S3 ? | + | Imaginer le scénario suivant : une instance RDS existe déjà en production sans chiffrement, |
| - | S3 est-il soumis aux Security Groups | + | <WRAP round question> |
| + | Avant de chercher la réponse, réfléchir à ce qui va se passer. | ||
| + | |||
| + | Est-ce que Terraform va modifier l' | ||
| + | |||
| + | Est-ce que le chiffrement peut être activé sur une instance RDS existante sans interruption ? | ||
| + | |||
| + | Quelle est la procédure correcte dans ce cas ? | ||
| </ | </ | ||
| - | ===== 9. Module logging ===== | + | <WRAP round todo> |
| + | Rechercher dans la documentation AWS RDS la section relative au chiffrement d'une instance existante. | ||
| - | Objectif : tracer toutes les actions réalisées sur le compte AWS via CloudTrail. | + | Décrire en cinq étapes la procédure à suivre pour migrer une instance non chiffrée vers une instance chiffrée. |
| + | </ | ||
| - | CloudTrail écrit ses logs dans un bucket S3 dédié. | + | ===== 9. Création du module logging ===== |
| - | AWS exige une policy bucket explicite pour autoriser | + | Objectif : activer |
| - | Sans cette policy, la ressource | + | Fichier : `td3/ |
| + | <sxh js> | ||
| + | variable " | ||
| + | type = string | ||
| + | description = "Nom du bucket S3 destiné à recevoir les logs CloudTrail" | ||
| + | } | ||
| - | <WRAP round todo> | + | variable " |
| - | Créer le module | + | |
| - | </ | + | description = " |
| + | } | ||
| - | Fichier : `td3/ | + | variable " |
| - | <sxh hcl> | + | |
| - | data "aws_caller_identity" | + | |
| + | } | ||
| + | </ | ||
| - | resource " | + | Fichier : `td3/ |
| - | bucket = " | + | <sxh js> |
| + | resource " | ||
| + | bucket = var.bucket_name | ||
| tags = { | tags = { | ||
| - | Name = "td3-cloudtrail-logs" | + | Name = "td3-s3-cloudtrail" |
| + | TD = "td3" | ||
| } | } | ||
| } | } | ||
| - | resource " | + | resource " |
| - | bucket = aws_s3_bucket.cloudtrail_logs.id | + | bucket = aws_s3_bucket.cloudtrail.id |
| block_public_acls | block_public_acls | ||
| Ligne 547: | Ligne 596: | ||
| } | } | ||
| - | resource " | + | resource " |
| - | bucket = aws_s3_bucket.cloudtrail_logs.id | + | bucket = aws_s3_bucket.cloudtrail.id |
| policy = jsonencode({ | policy = jsonencode({ | ||
| Ligne 560: | Ligne 609: | ||
| } | } | ||
| Action | Action | ||
| - | Resource = aws_s3_bucket.cloudtrail_logs.arn | + | Resource = aws_s3_bucket.cloudtrail.arn |
| }, | }, | ||
| { | { | ||
| Ligne 569: | Ligne 618: | ||
| } | } | ||
| Action | Action | ||
| - | Resource = " | + | Resource = " |
| Condition = { | Condition = { | ||
| StringEquals = { | StringEquals = { | ||
| Ligne 578: | Ligne 627: | ||
| ] | ] | ||
| }) | }) | ||
| - | |||
| - | depends_on = [aws_s3_bucket_public_access_block.cloudtrail_logs] | ||
| } | } | ||
| resource " | resource " | ||
| - | name = "td3-audit-trail" | + | name = "td3-cloudtrail" |
| - | s3_bucket_name | + | s3_bucket_name |
| include_global_service_events = true | include_global_service_events = true | ||
| is_multi_region_trail | is_multi_region_trail | ||
| enable_log_file_validation | enable_log_file_validation | ||
| + | kms_key_id | ||
| tags = { | tags = { | ||
| - | Name = "td3-audit-trail" | + | Name = "td3-cloudtrail" |
| + | TD = "td3" | ||
| } | } | ||
| - | depends_on = [aws_s3_bucket_policy.cloudtrail_logs] | + | depends_on = [aws_s3_bucket_policy.cloudtrail] |
| } | } | ||
| + | </ | ||
| - | output "cloudtrail_name" { | + | Fichier : `td3/ |
| - | description = "Nom du trail CloudTrail actif" | + | <sxh js> |
| - | value = aws_cloudtrail.main.name | + | output "cloudtrail_arn" { |
| + | value = aws_cloudtrail.main.arn | ||
| + | description = "ARN du trail CloudTrail" | ||
| } | } | ||
| - | </ | ||
| - | <WRAP round todo> | + | output |
| - | Ajouter l' | + | |
| - | </ | + | description |
| - | + | ||
| - | Fichier : `td3/ | + | |
| - | <sxh hcl> | + | |
| - | module | + | |
| - | | + | |
| } | } | ||
| </ | </ | ||
| - | <WRAP round help> | + | <WRAP round question> |
| - | La policy S3 contient deux instructions distinctes : `AWSCloudTrailAclCheck` et `AWSCloudTrailWrite`. | + | Le paramètre |
| - | Pourquoi CloudTrail a-t-il besoin | + | Quel problème cela permet-il de détecter |
| - | Que se serait-il passé si on avait appliqué `aws_cloudtrail` sans cette policy | + | Quel mécanisme technique AWS utilise-t-il pour garantir |
| </ | </ | ||
| - | <WRAP round help> | + | <WRAP round question> |
| - | Le paramètre `enable_log_file_validation = true` est activé. | + | CloudTrail trace les appels API AWS. |
| - | Que génère AWS pour permettre cette validation ? | + | Citer trois exemples d' |
| - | Comment un administrateur peut-il vérifier qu'un fichier de log n' | + | Citer deux exemples d'actions qui ne sont pas tracées par CloudTrail. |
| </ | </ | ||
| - | <WRAP round help> | + | ===== 10. Assemblage dans main.tf ===== |
| - | CloudTrail enregistre les appels API réalisés sur le compte AWS. | + | |
| - | Les modifications de Security Groups réalisées dans le TD2 via Terraform auraient-elles été enregistrées si CloudTrail avait été actif à ce moment ? | + | Objectif : relier tous les modules entre eux et injecter les data sources |
| - | Quelle différence entre CloudTrail et VPC Flow Logs ? | + | Fichier : `td3/ |
| - | </WRAP> | + | <sxh js> |
| + | variable " | ||
| + | type = string | ||
| + | description = "Mot de passe de la base de données – ne pas committer" | ||
| + | sensitive | ||
| + | } | ||
| - | ===== 10. Fichier outputs global ===== | + | variable " |
| - | + | type | |
| - | <WRAP round todo> | + | description |
| - | Créer le fichier d' | + | default |
| - | </ | + | } |
| - | Fichier : `td3/ | + | variable |
| - | <sxh hcl> | + | type = string |
| - | output | + | description = "Nom du bucket S3 pour les logs CloudTrail" |
| - | description = "Endpoint de la base de donnees TD3" | + | |
| - | | + | |
| } | } | ||
| + | </ | ||
| - | output | + | Fichier : `td3/ |
| - | | + | <sxh js> |
| - | | + | data "aws_caller_identity" |
| + | |||
| + | module "kms" { | ||
| + | | ||
| + | | ||
| } | } | ||
| - | output | + | module |
| - | | + | |
| - | | + | |
| + | db_password = var.db_password | ||
| } | } | ||
| - | </ | ||
| - | ===== 11. Deploiement complet ===== | + | module " |
| + | source | ||
| - | <WRAP round todo> | + | kms_key_arn = module.kms.key_arn |
| - | Initialiser le projet et vérifier le plan avant d' | + | |
| - | <sxh bash> | + | db_username = module.secrets.db_username |
| - | cd td3/ | + | |
| - | terraform init | + | |
| - | terraform plan | + | |
| - | terraform apply | + | |
| - | </ | + | |
| - | Relever les outputs après application. | + | subnet_ids = [ |
| - | </ | + | data.aws_subnet.private.id, |
| + | data.aws_subnet.private_b.id | ||
| + | ] | ||
| - | <WRAP round help> | + | security_group_id = data.aws_security_group.rds.id |
| - | Lors du `terraform plan`, combien de ressources sont listées comme a créer ? | + | |
| - | Les data sources vers le TD2 apparaissent-elles dans le plan ? | + | bucket_name = var.s3_app_bucket_name |
| + | } | ||
| - | Que se passe-t-il si le VPC `td2-vpc` n' | + | module " |
| - | </WRAP> | + | source |
| + | bucket_name = var.s3_logs_bucket_name | ||
| + | kms_key_arn = module.kms.key_arn | ||
| + | account_id | ||
| + | } | ||
| + | </sxh> | ||
| - | ===== 12. Simulation d'un incident | + | <WRAP round question> |
| + | Le module `secrets` expose `db_username` | ||
| - | Objectif : vérifier que CloudTrail trace bien les actions réalisées sur le compte. | + | Regarder la définition des outputs dans `td3/ |
| - | < | + | Que faut-il ajouter sur l' |
| - | Modifier temporairement un Security Group du TD2 via Terraform. | + | </WRAP> |
| - | Par exemple, rouvrir le port 5432 sur `0.0.0.0/0` dans `td2/network/security_groups.tf`. | + | Fichier : `td3/modules/secrets/outputs.tf` — ajouter les outputs manquants |
| + | <sxh js> | ||
| + | output " | ||
| + | value = var.db_username | ||
| + | description = "Nom d' | ||
| + | } | ||
| - | Appliquer ce changement : | + | output " |
| + | value = var.db_password | ||
| + | description = "Mot de passe de la base de données" | ||
| + | sensitive | ||
| + | } | ||
| + | </ | ||
| + | ===== 11. Déploiement et vérification ===== | ||
| + | |||
| + | Fichier : `td3/ | ||
| <sxh bash> | <sxh bash> | ||
| - | cd td2/ | + | # Se placer dans le dossier td3 |
| - | terraform apply | + | cd tp-aws/td3 |
| - | </sxh> | + | |
| - | Attendre deux minutes. | + | # Initialiser le projet |
| + | terraform init | ||
| - | Se connecter à la console AWS et naviguer vers : | + | # Vérifier le plan – inspecter les ressources créées |
| + | terraform plan -var=" | ||
| - | CloudTrail > Event history | + | # Appliquer |
| + | terraform apply -var=" | ||
| - | Filtrer par nom d' | + | # Vérifier les outputs |
| - | </ | + | terraform output |
| - | <WRAP round help> | + | # Vérifier le secret dans AWS CLI |
| - | L' | + | aws secretsmanager get-secret-value --secret-id td3/ |
| - | Quelles informations sont disponibles dans le détail de l' | + | # Vérifier que le bucket S3 est bien chiffré |
| - | * quel utilisateur ou rôle IAM a réalisé l' | + | aws s3api get-bucket-encryption --bucket td3-app-bucket-demo --region eu-west-3 |
| - | * depuis quelle adresse IP ? | + | |
| - | * à quelle heure exactement ? | + | |
| - | * quelle ressource a été modifiée ? | + | |
| - | </ | + | |
| - | <WRAP round help> | + | # Vérifier que CloudTrail est actif |
| - | Un Security Group du TD2 a été ouvert sur Internet par erreur un vendredi soir. | + | aws cloudtrail get-trail-status --name td3-cloudtrail --region eu-west-3 |
| + | </sxh> | ||
| - | L' | + | <WRAP round todo> |
| + | Après | ||
| - | Comment CloudTrail permet-il de répondre aux questions suivantes sans CloudTrail et avec CloudTrail | + | * Secrets Manager |
| - | * depuis combien de temps la faille | + | * KMS : la clé `alias/ |
| - | * qui a fait la modification ? | + | * RDS : l' |
| - | * s'agit-il d'une erreur ou d'un acte malveillant ? | + | * S3 : le bucket applicatif indique `SSE-KMS` dans ses propriétés de chiffrement |
| + | * CloudTrail : le trail `td3-cloudtrail` est actif et envoie des logs dans le bucket | ||
| </ | </ | ||
| + | |||
| + | ===== 12. Lecture des logs CloudTrail ===== | ||
| + | |||
| + | Objectif : naviguer dans les logs CloudTrail pour retrouver une action spécifique. | ||
| <WRAP round todo> | <WRAP round todo> | ||
| - | Remettre le Security Group du TD2 dans son état sécurisé après l' | + | Dans la console AWS, ouvrir CloudTrail puis Event History. |
| - | <sxh bash> | + | Filtrer les événements par type `CreateSecret`. |
| - | cd td2/ | + | |
| - | terraform apply | + | |
| - | </ | + | |
| - | </ | + | |
| - | <WRAP round help> | + | Retrouver l' |
| - | Pourquoi est-il important de conserver les logs CloudTrail pendant au moins un an ? | + | |
| - | Quelles obligations légales ou réglementaires peuvent imposer cette durée en France et en Europe ? | + | Identifier les champs suivants dans l' |
| + | |||
| + | * `userIdentity` : qui a réalisé l' | ||
| + | * `eventTime` : quand | ||
| + | * `sourceIPAddress` : depuis quelle IP | ||
| + | * `requestParameters` : quel secret a été créé | ||
| </ | </ | ||
| - | ===== 13. Vérification globale ===== | + | <WRAP round question> |
| + | Un collègue vous signale qu'une clé KMS a été désactivée à 3h du matin sans explication. | ||
| - | <WRAP round todo> | + | Quelle requête effectuer dans CloudTrail pour retrouver cet événement ? |
| - | Vérifier les points suivants avant de passer au challenge : | + | |
| - | * aucun mot de passe n'apparait dans les fichiers `.tf` | + | Quels champs |
| - | * le fichier `terraform.tfvars` est présent dans `.gitignore` | + | |
| - | * le secret est visible dans la console AWS Secrets Manager sous le nom `td3/ | + | |
| - | * la ressource `aws_db_instance` dans le module database contient `storage_encrypted = true` | + | |
| - | * le bucket S3 applicatif a le chiffrement AES256 actif | + | |
| - | * le bucket S3 applicatif a les quatre blocages | + | |
| - | * CloudTrail est actif dans la console AWS | + | |
| - | * la validation des fichiers | + | |
| </ | </ | ||
| ===== Challenge final ===== | ===== Challenge final ===== | ||
| - | **Axe secrets** | + | L' |
| - | <WRAP round help> | + | Rédiger un rapport |
| - | Où est physiquement stocké le mot de passe de la base de données maintenant ? | + | |
| - | Comparer avec la situation du TD2 : qui pouvait y accéder avant, qui peut y accéder maintenant ? | + | **Axe 1 – Secrets** |
| - | Comment restreindre l' | + | Expliquer comment les identifiants RDS sont maintenant gérés. |
| - | </ | + | |
| - | **Axe chiffrement** | + | Comparer l' |
| - | <WRAP round help> | + | Indiquer ce qui resterait à améliorer si l' |
| - | Le paramètre `storage_encrypted = true` sur RDS protège-t-il les données pendant leur transfert entre le backend et la base ? | + | |
| - | Quel mécanisme complémentaire faut-il activer pour protéger les données en transit ? | + | **Axe 2 – Clés KMS** |
| - | Dans quel cas le chiffrement AES256 | + | Décrire |
| - | </ | + | |
| - | **Axe traçabilité** | + | Expliquer ce qui se passe lors de la rotation automatique. |
| - | <WRAP round help> | + | Indiquer |
| - | Un stagiaire supprime accidentellement la ressource CloudTrail via `terraform destroy` sur le module logging. | + | |
| - | Quelles sont les conséquences immédiates pour la traçabilité du compte ? | + | **Axe 3 – Chiffrement des données** |
| - | Comment aurait-on pu protéger cette ressource contre une suppression accidentelle ? | + | Compléter le tableau suivant : |
| - | Chercher le paramètre Terraform qui permet de protéger une ressource contre la destruction. | + | Fichier : `td3/ |
| - | </WRAP> | + | <sxh bash> |
| + | | Ressource | ||
| + | |------------|---------------------|---------------------|---------------------------| | ||
| + | | RDS | SSE-KMS | ||
| + | | S3 app | SSE-KMS | ||
| + | | S3 logs | ? | ? | ? | | ||
| + | | Transit RDS| ? | ? | ? | | ||
| + | </sxh> | ||
| - | **Axe architecture** | + | **Axe 4 – Traçabilité** |
| + | |||
| + | Décrire ce que CloudTrail trace dans le contexte de ce TD. | ||
| + | |||
| + | Expliquer pourquoi le bucket de logs est lui-même protégé par une policy stricte. | ||
| + | |||
| + | Indiquer comment détecter une tentative de suppression des logs CloudTrail. | ||
| <WRAP round todo> | <WRAP round todo> | ||
| - | Produire un schéma de l'architecture | + | Produire un schéma de l'infrastructure TD3 complète |
| - | Le schéma doit inclure : | + | |
| - | | + | * les ressources récupérées en data sources depuis TD2 |
| - | * les ajouts du TD3 : Secrets Manager, RDS chiffré, S3 applicatif, CloudTrail et son bucket | + | |
| * les flux de données entre chaque composant avec les ports concernés | * les flux de données entre chaque composant avec les ports concernés | ||
| * une légende distinguant les flux chiffrés et les flux non chiffrés | * une légende distinguant les flux chiffrés et les flux non chiffrés | ||
| Ligne 819: | Ligne 895: | ||
| Fichier : `td3/ | Fichier : `td3/ | ||
| - | < | + | < |
| variable " | variable " | ||
| type = string | type = string | ||
| Ligne 837: | Ligne 913: | ||
| </ | </ | ||
| - | <WRAP round help> | + | <WRAP round question> |
| Le bloc `count = var.rotation_lambda_arn != "" | Le bloc `count = var.rotation_lambda_arn != "" | ||
| Que signifie ce pattern dans Terraform ? | Que signifie ce pattern dans Terraform ? | ||
| - | Pourquoi est-il utile de rendre la rotation optionnelle plutôt | + | Pourquoi est-il utile de rendre la rotation optionnelle plutôt |
| </ | </ | ||
| - | <WRAP round help> | + | <WRAP round question> |
| - | Après une rotation automatique | + | Après une rotation automatique, |
| Si l' | Si l' | ||