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 08:09] – [3. Declaration des variables] jcheron | eadl:bloc4:fm4:td3 [2026/06/25 03:35] (Version actuelle) – jcheron | ||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| - | ====== TD3 – Gestion des secrets, chiffrement et traçabilité AWS ====== | ||
| - | |||
| - | ===== Objectifs ===== | ||
| - | |||
| - | * Supprimer les secrets en clair dans une infrastructure Terraform | ||
| - | * 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 | ||
| - | * Mettre en place une traçabilité avec CloudTrail | ||
| - | * Identifier une action suspecte via les logs | ||
| - | |||
| - | ===== Contexte ===== | ||
| - | |||
| - | Suite au TD2, l' | ||
| - | |||
| - | Le VPC est segmenté, l'ALB filtre le trafic entrant, les Security Groups isolent chaque couche. | ||
| - | |||
| - | Un nouvel audit de sécurité interne est réalisé. | ||
| - | |||
| - | Trois failles critiques sont identifiées malgré les corrections du TD2 : | ||
| - | |||
| - | * 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 | ||
| - | * 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). | ||
| - | |||
| - | Votre mission est de corriger ces trois failles en prolongeant l' | ||
| - | |||
| - | ===== Pré-requis ===== | ||
| - | |||
| - | * Projet Terraform du TD2 déployé et fonctionnel | ||
| - | * ALB opérationnel, | ||
| - | * Accès AWS avec droits suffisants : IAM, Secrets Manager, S3, RDS, CloudTrail | ||
| - | * Terraform en version 1.5 ou supérieure | ||
| - | |||
| - | ===== Structure du projet ===== | ||
| - | |||
| - | Le TD3 s' | ||
| - | |||
| - | <sxh bash; | ||
| - | td3/ | ||
| - | ├── main.tf | ||
| - | ├── variables.tf | ||
| - | ├── outputs.tf | ||
| - | ├── providers.tf | ||
| - | └── modules/ | ||
| - | ├── secrets/ | ||
| - | │ | ||
| - | ├── database/ | ||
| - | │ | ||
| - | ├── storage/ | ||
| - | │ | ||
| - | └── logging/ | ||
| - | └── main.tf | ||
| - | </ | ||
| - | |||
| - | Le dossier `td2/` n'est pas modifié directement. | ||
| - | |||
| - | Les ressources existantes du TD2 sont récupérées via des data sources Terraform. | ||
| - | |||
| - | <WRAP round question> | ||
| - | Pourquoi utilise-t-on des data sources plutôt que de copier les ressources du TD2 dans TD3 ? | ||
| - | |||
| - | Que se passerait-il si on déclarait une deuxième fois la même ressource avec le même nom ? | ||
| - | </ | ||
| - | |||
| - | ===== 1. Analyse de l' | ||
| - | |||
| - | <WRAP round todo> | ||
| - | Ouvrir le fichier `td2/ | ||
| - | |||
| - | Localiser la ressource `aws_db_instance.db`. | ||
| - | </ | ||
| - | |||
| - | <WRAP round question> | ||
| - | Relever les trois problèmes de sécurité présents dans cette ressource. | ||
| - | |||
| - | Pour chaque problème, indiquer : | ||
| - | * 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> | ||
| - | Le fichier `instances.tf` est versionné dans Git. | ||
| - | |||
| - | Un développeur a poussé ce fichier il y a trois mois sur le dépôt de l' | ||
| - | |||
| - | Le mot de passe a été changé depuis, mais il reste dans l' | ||
| - | |||
| - | Comment retrouver ce mot de passe dans l' | ||
| - | |||
| - | Pourquoi changer le mot de passe ne suffit-il pas si le fichier a déjà été commité ? | ||
| - | </ | ||
| - | |||
| - | <WRAP round question> | ||
| - | La ressource `aws_db_instance.db` dans le TD2 ne contient pas le paramètre `storage_encrypted`. | ||
| - | |||
| - | Quelle est la valeur par défaut de ce paramètre dans AWS ? | ||
| - | |||
| - | Que signifie concrètement une base non chiffrée au repos ? | ||
| - | </ | ||
| - | |||
| - | ===== 2. Declaration des providers ===== | ||
| - | |||
| - | Le module `storage` utilisera le provider `random` pour générer des suffixes uniques sur les noms de buckets S3. | ||
| - | |||
| - | Les noms de buckets S3 sont globaux sur AWS : deux comptes ne peuvent pas avoir le même nom. | ||
| - | |||
| - | <WRAP round todo> | ||
| - | Créer le fichier de déclaration des providers. | ||
| - | </ | ||
| - | |||
| - | Fichier : `td3/ | ||
| - | <sxh js> | ||
| - | terraform { | ||
| - | required_providers { | ||
| - | aws = { | ||
| - | source | ||
| - | version = "~> 5.0" | ||
| - | } | ||
| - | random = { | ||
| - | source | ||
| - | version = "~> 3.0" | ||
| - | } | ||
| - | } | ||
| - | } | ||
| - | |||
| - | provider " | ||
| - | region = " | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | <WRAP round question> | ||
| - | Pourquoi faut-il déclarer explicitement le provider `random` alors qu'il ne communique pas avec AWS ? | ||
| - | |||
| - | Que se passe-t-il si on l' | ||
| - | </ | ||
| - | |||
| ====== TD3 – Gestion des secrets, chiffrement et traçabilité AWS ====== | ====== TD3 – Gestion des secrets, chiffrement et traçabilité AWS ====== | ||
| Ligne 150: | Ligne 10: | ||
| * 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 173: | Ligne 34: | ||
| * 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, KMS, S3, RDS, CloudTrail | * Accès AWS avec droits suffisants : IAM, Secrets Manager, KMS, S3, RDS, CloudTrail | ||
| Ligne 179: | Ligne 41: | ||
| ===== Structure du projet ===== | ===== Structure du projet ===== | ||
| - | Le TD3 s' | + | Le TD3 est un projet Terraform indépendant. |
| + | |||
| + | Il ne modifie pas le code du TD2. | ||
| + | |||
| + | Il récupère | ||
| Fichier : `td3/ | Fichier : `td3/ | ||
| <sxh bash> | <sxh bash> | ||
| - | td3/ | + | tp-aws/ |
| - | ├── main.tf | + | ├── td1/ # IAM – roles, policies |
| - | ├── variables.tf | + | ├── td2/ # Réseau – VPC, subnets, ALB, Security Groups |
| - | ├── outputs.tf | + | └── |
| - | └── modules/ | + | ├── main.tf |
| - | ├── secrets/ | + | ├── variables.tf |
| - | │ └── main.tf | + | ├── outputs.tf |
| - | ├── kms/ | + | └── modules/ |
| - | │ └── main.tf | + | ├── secrets/ |
| - | ├── storage/ | + | │ ├── main.tf |
| - | │ └── main.tf | + | |
| - | └── | + | │ |
| - | └── main.tf | + | |
| + | │ ├── main.tf | ||
| + | | ||
| + | │ | ||
| + | | ||
| + | │ ├── main.tf | ||
| + | | ||
| + | │ └── | ||
| + | └── logging/ | ||
| + | ├── main.tf | ||
| + | ├── variables.tf | ||
| + | └── outputs.tf | ||
| </ | </ | ||
| - | Le fichier `td3/main.tf` orchestre les quatre modules et récupère les ressources du TD2 via des data sources. | + | ===== 1. Récupération des ressources du TD2 ===== |
| - | ===== 1. Analyse de l' | + | Objectif : comprendre comment un projet Terraform peut interroger une infrastructure existante sans accéder à son state. |
| - | Lire le fichier `td2/ | + | TD3 utilise des data sources AWS natifs. |
| - | <WRAP round question> | + | Ces data sources envoient des requêtes directement à l'API AWS et filtrent les ressources par tags. |
| - | Localiser la ligne où le mot de passe de la base de données est défini. | + | |
| - | Répondre aux questions suivantes : | + | Cela suppose que les ressources TD2 ont été correctement taguées. |
| - | * Qui peut lire ce mot de passe concrètement ? | + | |
| - | * Ce fichier est-il souvent versionné dans Git en entreprise ? | + | |
| - | * Quelles sont les conséquences si ce dépôt est public ou partagé avec un prestataire ? | + | |
| - | </ | + | |
| <WRAP round question> | <WRAP round question> | ||
| - | Dans `td2/ | + | Pourquoi TD3 ne peut-il |
| - | Quelle est la valeur par défaut de ce paramètre dans AWS ? | + | Quelle est la différence entre un data source |
| - | + | ||
| - | Comment vérifier l' | + | |
| </ | </ | ||
| - | |||
| - | <WRAP round question> | ||
| - | Aucun service de logging n'est présent dans le TD2. | ||
| - | |||
| - | Citer deux situations concrètes en production où l' | ||
| - | </ | ||
| - | |||
| - | ===== 2. Récupération des ressources du TD2 ===== | ||
| - | |||
| - | Le TD3 ne redéploie pas les ressources du TD2. | ||
| - | |||
| - | Il les récupère via des data sources Terraform pour les réutiliser. | ||
| Fichier : `td3/ | Fichier : `td3/ | ||
| <sxh js> | <sxh js> | ||
| - | terraform { | ||
| - | required_providers { | ||
| - | aws = { | ||
| - | source | ||
| - | version = "~> 5.0" | ||
| - | } | ||
| - | random = { | ||
| - | source | ||
| - | version = "~> 3.0" | ||
| - | } | ||
| - | } | ||
| - | } | ||
| - | |||
| provider " | provider " | ||
| region = " | region = " | ||
| } | } | ||
| - | # Recuperation du compte AWS courant | + | # Récupération |
| - | data " | + | data " |
| - | + | | |
| - | # Recuperation de la region courante | + | Name = " |
| - | data " | + | |
| - | + | ||
| - | # Recuperation | + | |
| - | data " | + | |
| - | | + | |
| - | | + | |
| - | values | + | |
| } | } | ||
| } | } | ||
| - | # Recuperation du Security Group backend | + | # Récupération |
| - | data "aws_security_group" "backend_sg" { | + | data "aws_subnet" "private" { |
| - | | + | |
| - | | + | Name = "td2-private" |
| - | values | + | |
| } | } | ||
| } | } | ||
| - | module | + | # Récupération du subnet privé secondaire créé dans TD2 |
| - | | + | data "aws_subnet" |
| + | | ||
| + | Name = "td2-private-b" | ||
| + | } | ||
| } | } | ||
| - | module | + | # Récupération du Security Group RDS créé dans TD2 |
| - | | + | data "aws_security_group" |
| - | | + | |
| - | region | + | Name = "td2-sg-rds" |
| + | | ||
| } | } | ||
| - | module | + | # Récupération du Security Group de l' |
| - | | + | data "aws_security_group" |
| - | | + | |
| + | Name = "td2-sg-app" | ||
| + | | ||
| } | } | ||
| + | </ | ||
| - | module | + | <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> | ||
| + | Que se passe-t-il si deux ressources AWS portent le même tag `Name` dans la même région ? | ||
| + | |||
| + | Quel comportement Terraform adopte-t-il dans ce cas ? | ||
| + | </ | ||
| + | |||
| + | ===== 2. Identification du problème ===== | ||
| + | |||
| + | Objectif : identifier les secrets en clair dans l' | ||
| + | |||
| + | Voici un extrait du fichier `instances.tf` tel qu'il existe après le TD2. | ||
| + | |||
| + | Fichier : `td2/ | ||
| + | <sxh js> | ||
| + | resource " | ||
| + | | ||
| + | | ||
| + | | ||
| + | allocated_storage = 20 | ||
| + | |||
| + | username = " | ||
| + | password = " | ||
| + | |||
| + | skip_final_snapshot = true | ||
| } | } | ||
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Pourquoi utilise-t-on des data sources plutôt que de redéclarer les ressources du TD2 ? | + | Identifier au moins trois problèmes |
| - | Que se passerait-il si on redéclarait | + | Pour chaque problème, expliquer la conséquence concrète en cas de fuite du fichier |
| </ | </ | ||
| - | ===== 3. Gestion des secrets avec Secrets Manager ===== | + | <WRAP round todo> |
| + | Ne pas appliquer ce fichier tel quel. | ||
| - | Objectif : supprimer | + | Ne pas modifier |
| - | <WRAP round todo> | + | La correction se fait uniquement dans TD3. |
| - | Créer le module secrets. | + | |
| </ | </ | ||
| - | Fichier : `td3/ | + | ===== 3. Création du module secrets ===== |
| + | |||
| + | Objectif : stocker les identifiants de la base de données dans AWS Secrets Manager. | ||
| + | |||
| + | Fichier : `td3/ | ||
| <sxh js> | <sxh js> | ||
| + | variable " | ||
| + | type = string | ||
| + | description = "Nom du secret dans Secrets Manager" | ||
| + | default | ||
| + | } | ||
| + | |||
| variable " | variable " | ||
| type = string | type = string | ||
| description = "Nom d' | description = "Nom d' | ||
| - | default | + | default |
| } | } | ||
| Ligne 323: | Ligne 205: | ||
| sensitive | sensitive | ||
| } | } | ||
| + | </ | ||
| + | Fichier : `td3/ | ||
| + | <sxh js> | ||
| resource " | resource " | ||
| - | name = "td3/db/ | + | name = var.secret_name |
| - | | + | recovery_window_in_days = 7 |
| + | |||
| + | tags = { | ||
| + | Name = "td3-secret-db" | ||
| + | | ||
| + | } | ||
| } | } | ||
| resource " | resource " | ||
| secret_id = aws_secretsmanager_secret.db_credentials.id | secret_id = aws_secretsmanager_secret.db_credentials.id | ||
| + | |||
| secret_string = jsonencode({ | secret_string = jsonencode({ | ||
| username = var.db_username | username = var.db_username | ||
| Ligne 336: | Ligne 227: | ||
| }) | }) | ||
| } | } | ||
| + | </ | ||
| + | Fichier : `td3/ | ||
| + | <sxh js> | ||
| output " | output " | ||
| value = aws_secretsmanager_secret.db_credentials.arn | value = aws_secretsmanager_secret.db_credentials.arn | ||
| - | description = "ARN du secret contenant les identifiants | + | description = "ARN du secret contenant les identifiants |
| } | } | ||
| - | </ | ||
| - | Fichier : `td3/ | + | output |
| - | <sxh js> | + | |
| - | variable | + | description = "Nom du secret dans Secrets Manager" |
| - | | + | |
| - | description = "Mot de passe initial de la base de donnees" | + | |
| - | sensitive | + | |
| } | } | ||
| </ | </ | ||
| - | Le mot de passe est transmis via une variable | + | <WRAP round question> |
| + | Le paramètre `sensitive = true` sur la variable | ||
| - | Pour appliquer, Terraform demandera la valeur au moment | + | Cela signifie-t-il que le mot de passe est chiffré dans le state Terraform ? |
| - | Fichier : `td3/ | + | Où le mot de passe est-il stocké en clair malgré ce paramètre ? |
| - | <sxh bash> | + | </WRAP> |
| - | # Appliquer en fournissant | + | |
| - | terraform apply -var=" | + | |
| - | + | ||
| - | # Ou via une variable d' | + | |
| - | export TF_VAR_db_password=" | + | |
| - | terraform apply | + | |
| - | </sxh> | + | |
| <WRAP round question> | <WRAP round question> | ||
| - | Pourquoi utilise-t-on | + | Le paramètre |
| - | Que change ce paramètre dans l' | + | Pourquoi AWS impose-t-il cette fenêtre de récupération par défaut |
| - | Est-ce suffisant pour garantir que le mot de passe ne sera jamais visible | + | Comment forcer la suppression immédiate si nécessaire, |
| </ | </ | ||
| - | <WRAP round question> | + | ===== 4. Erreur volontaire – secret |
| - | Pourquoi utilise-t-on un JSON pour stocker le secret | + | |
| - | Quel avantage cela apporte-t-il si l'application | + | Objectif : comprendre ce qui se passe quand une application |
| - | </ | + | |
| - | ===== 4. Problème volontaire – dépendance entre modules ===== | + | Voici un data source intentionnellement incorrect. |
| + | |||
| + | Fichier : `td3/ | ||
| + | <sxh js> | ||
| + | data " | ||
| + | name = " | ||
| + | } | ||
| + | </ | ||
| <WRAP round todo> | <WRAP round todo> | ||
| - | Tenter d' | + | Lire le code ci-dessus sans l' |
| - | terraform apply -target=module.secrets | + | Identifier l' |
| - | Puis observer ce qui se passe si on tente de lire le secret immédiatement dans le module storage sans `depends_on`. | + | Écrire la correction avant de lire la suite. |
| </ | </ | ||
| - | <WRAP round question> | + | Le nom du secret dans Secrets Manager est `td3/ |
| - | Terraform gère les dépendances implicites entre ressources | + | |
| - | Pourquoi cette dépendance implicite ne fonctionne-t-elle pas toujours entre modules différents ? | + | Le data source ci-dessus cherche `td3/ |
| - | Dans quels cas faut-il utiliser | + | L' |
| + | |||
| + | 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. | ||
| </ | </ | ||
| - | ===== 5. Création d'une clé KMS ===== | + | ===== 5. Récupération du secret dans RDS ===== |
| - | Objectif : créer une clé de chiffrement gérée par le client (CMK). | + | Objectif : utiliser |
| - | <WRAP round question> | + | Fichier : `td3/ |
| - | AWS propose deux types de clés KMS : | + | <sxh js> |
| - | | + | data " |
| - | * les clés Customer Managed Keys (CMK) | + | |
| - | Quelle est la différence concrète entre les deux en termes de contrôle ? | + | depends_on = [aws_secretsmanager_secret_version.db_credentials] |
| + | } | ||
| - | Qui peut utiliser une clé AWS managée ? | + | locals { |
| + | db_secret = jsondecode( | ||
| + | data.aws_secretsmanager_secret_version.db_credentials.secret_string | ||
| + | ) | ||
| + | } | ||
| + | </ | ||
| - | Qui décide des droits d' | + | <WRAP round question> |
| + | Pourquoi utilise-t-on `depends_on` ici ? | ||
| + | |||
| + | Que se passerait-il si Terraform tentait de lire le secret avant que la version soit créée | ||
| </ | </ | ||
| - | <WRAP round todo> | + | ===== 6. Création du module KMS ===== |
| - | Créer le module KMS. | + | |
| + | Objectif : créer une clé de chiffrement gérée par le client (Customer Managed Key) pour chiffrer les données au repos. | ||
| + | |||
| + | <WRAP round question> | ||
| + | Avant de créer la clé, répondre aux questions suivantes : | ||
| + | |||
| + | Quelle est la différence entre une clé AWS managée et une CMK (Customer Managed Key) ? | ||
| + | |||
| + | Dans quel cas une CMK est-elle obligatoire plutôt que recommandée ? | ||
| </ | </ | ||
| - | Fichier : `td3/ | + | Fichier : `td3/ |
| <sxh js> | <sxh js> | ||
| - | variable "account_id" { | + | variable "key_alias" { |
| type = string | type = string | ||
| - | description = "ID du compte AWS courant" | + | description = "Alias de la clé KMS" |
| + | default | ||
| } | } | ||
| - | variable "region" { | + | variable "account_id" { |
| type = string | type = string | ||
| - | description = "Region | + | description = "ID du compte |
| } | } | ||
| + | </ | ||
| + | |||
| + | Fichier : `td3/ | ||
| + | <sxh js> | ||
| + | data " | ||
| - | resource " | + | resource " |
| - | description | + | description |
| - | deletion_window_in_days = 7 | + | deletion_window_in_days = 10 |
| enable_key_rotation | enable_key_rotation | ||
| Ligne 439: | Ligne 361: | ||
| Statement = [ | Statement = [ | ||
| { | { | ||
| - | Sid = "Acces administrateur au compte" | + | Sid = "EnableRootAccess" |
| Effect = " | Effect = " | ||
| Principal = { | Principal = { | ||
| Ligne 449: | Ligne 371: | ||
| ] | ] | ||
| }) | }) | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | TD = " | ||
| + | } | ||
| } | } | ||
| - | resource " | + | resource " |
| - | name = " | + | name = var.key_alias |
| - | target_key_id = aws_kms_key.td3.id | + | target_key_id = aws_kms_key.main.key_id |
| } | } | ||
| + | </ | ||
| + | Fichier : `td3/ | ||
| + | <sxh js> | ||
| output " | output " | ||
| - | value = aws_kms_key.td3.arn | + | value = aws_kms_key.main.arn |
| - | description = "ARN de la cle KMS TD3" | + | description = "ARN de la clé KMS" |
| } | } | ||
| output " | output " | ||
| - | value = aws_kms_key.td3.id | + | value = aws_kms_key.main.key_id |
| - | description = "ID de la cle KMS TD3" | + | description = "ID de la clé KMS" |
| } | } | ||
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Le paramètre `deletion_window_in_days | + | Le paramètre `enable_key_rotation |
| - | Que se passe-t-il si on supprime cette clé alors que des données RDS ou S3 sont chiffrées avec elle ? | + | Que signifie concrètement la rotation d' |
| - | Pourquoi AWS impose-t-il un délai minimum avant la suppression effective d' | + | Les données chiffrées avec l' |
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Le paramètre | + | La policy KMS donne accès à `arn: |
| - | Que signifie la rotation d'une clé KMS concrètement | + | Pourquoi cette entrée est-elle obligatoire dans une policy |
| - | La rotation de la clé KMS invalide-t-elle | + | Que se passe-t-il si elle est absente |
| </ | </ | ||
| - | <WRAP round question> | + | ===== 7. Création du module storage ===== |
| - | La key policy autorise `kms:*` pour le root du compte. | + | |
| - | Pourquoi cette règle est-elle nécessaire dans une key policy ? | + | Objectif : déployer |
| - | Que se passerait-il si on supprimait cette règle et qu' | + | Fichier |
| - | </ | + | |
| - | + | ||
| - | ===== 6. Chiffrement de la base de données RDS ===== | + | |
| - | + | ||
| - | Objectif | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Modifier | + | |
| - | </ | + | |
| - | + | ||
| - | Fichier : `td2/network/instances.tf` | + | |
| <sxh js> | <sxh js> | ||
| - | # Recuperation du secret cree dans le module TD3 | + | variable |
| - | data "aws_secretsmanager_secret_version" | + | |
| - | | + | description |
| + | } | ||
| - | depends_on | + | variable " |
| - | aws_secretsmanager_secret_version.db_credentials | + | type |
| - | | + | |
| } | } | ||
| - | locals | + | variable " |
| - | | + | |
| - | | + | |
| - | | + | |
| } | } | ||
| - | resource | + | variable |
| - | | + | |
| - | | + | |
| - | | + | } |
| - | vpc_security_group_ids = [aws_security_group.backend_sg.id] | + | |
| - | user_data = << | + | variable " |
| - | # | + | |
| - | yum install -y python3 | + | |
| - | python3 -m http.server 80 & | + | |
| - | | + | |
| - | + | ||
| - | | + | |
| - | Name = "td2-backend" | + | |
| - | } | + | |
| } | } | ||
| - | resource | + | variable |
| - | | + | |
| - | engine | + | |
| - | engine_version | + | |
| - | instance_class | + | |
| - | allocated_storage | + | |
| - | + | ||
| - | username = local.db_secret.username | + | |
| - | password = local.db_secret.password | + | |
| - | + | ||
| - | storage_encrypted = true | + | |
| - | kms_key_id | + | |
| - | + | ||
| - | | + | |
| - | vpc_security_group_ids = [aws_security_group.db_sg.id] | + | |
| - | skip_final_snapshot | + | |
| - | + | ||
| - | tags = { | + | |
| - | Name = "td3-db" | + | |
| - | } | + | |
| } | } | ||
| + | </ | ||
| + | Fichier : `td3/ | ||
| + | <sxh js> | ||
| resource " | resource " | ||
| name = " | name = " | ||
| - | subnet_ids = [aws_subnet.private.id, aws_subnet.public_b.id] | + | subnet_ids = var.subnet_ids |
| tags = { | tags = { | ||
| Name = " | Name = " | ||
| + | TD = " | ||
| } | } | ||
| } | } | ||
| - | variable | + | resource |
| - | | + | |
| - | | + | |
| - | } | + | |
| - | </ | + | |
| + | allocated_storage = 20 | ||
| - | <WRAP round question> | + | username = var.db_username |
| - | Le chiffrement `storage_encrypted | + | |
| - | Quel mécanisme protège les données en transit entre le backend et RDS ? | + | db_subnet_group_name |
| + | vpc_security_group_ids = [var.security_group_id] | ||
| - | Le chiffrement au repos protège-t-il contre un accès non autorisé via SQL si les credentials sont compromis ? | + | storage_encrypted = true |
| - | </ | + | |
| - | <WRAP round question> | + | skip_final_snapshot = true |
| - | L' | + | |
| - | Que se passe-t-il si on tente de modifier `storage_encrypted` sur une instance RDS existante ? | + | tags = { |
| - | + | | |
| - | Pourquoi AWS impose-t-il cette contrainte ? | + | |
| - | </ | + | } |
| - | + | ||
| - | ===== 7. Problème volontaire – chiffrement RDS immuable ===== | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Tenter de modifier une instance RDS existante non chiffrée pour activer le chiffrement : | + | |
| - | + | ||
| - | terraform apply | + | |
| - | + | ||
| - | Observer le message d' | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round question> | + | |
| - | Terraform indique qu'il doit détruire et recréer l' | + | |
| - | + | ||
| - | Pourquoi le chiffrement d'une instance RDS ne peut-il pas être activé à chaud ? | + | |
| - | + | ||
| - | Quelles précautions prendre en production avant de forcer cette opération ? | + | |
| - | </ | + | |
| - | + | ||
| - | ===== 8. Création d'un bucket S3 sécurisé ===== | + | |
| - | + | ||
| - | Objectif : créer un bucket S3 avec chiffrement SSE-KMS et blocage d' | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Créer le module storage. | + | |
| - | </ | + | |
| - | + | ||
| - | Fichier : `td3/ | + | |
| - | <sxh js> | + | |
| - | variable | + | |
| - | | + | |
| - | description | + | |
| - | } | + | |
| - | + | ||
| - | resource " | + | |
| - | byte_length = 4 | + | |
| } | } | ||
| - | resource " | + | resource " |
| - | bucket = " | + | bucket = var.bucket_name |
| tags = { | tags = { | ||
| - | Name = "td3-app-data" | + | Name = "td3-s3-app" |
| + | TD = "td3" | ||
| } | } | ||
| } | } | ||
| - | resource " | + | resource " |
| - | bucket = aws_s3_bucket.app_data.id | + | bucket = aws_s3_bucket.app.id |
| - | + | ||
| - | block_public_acls | + | |
| - | block_public_policy | + | |
| - | ignore_public_acls | + | |
| - | restrict_public_buckets = true | + | |
| - | } | + | |
| - | + | ||
| - | resource " | + | |
| - | bucket = aws_s3_bucket.app_data.id | + | |
| rule { | rule { | ||
| Ligne 652: | Ligne 508: | ||
| } | } | ||
| - | output | + | resource |
| - | | + | |
| - | | + | |
| + | | ||
| + | block_public_policy | ||
| + | ignore_public_acls | ||
| + | restrict_public_buckets = true | ||
| } | } | ||
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Dans ce TD la valeur de `sse_algorithm` est `aws:kms`. | + | Le chiffrement |
| - | Dans le TD précédent la valeur était `AES256`. | + | Quel mécanisme protège les données en transit entre l' |
| - | + | ||
| - | Quelle est la différence concrète entre ces deux modes de chiffrement S3 ? | + | |
| - | + | ||
| - | Quel mode offre le plus de contrôle sur la clé de chiffrement | + | |
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Le paramètre `bucket_key_enabled = true` est activé. | + | Le paramètre `bucket_key_enabled = true` est activé |
| - | Rechercher ce que fait ce paramètre. | + | Rechercher |
| - | Quel impact | + | Quel est l'impact sur les coûts |
| </ | </ | ||
| + | |||
| + | ===== 8. Erreur volontaire – chiffrement impossible ===== | ||
| + | |||
| + | Objectif : comprendre pourquoi RDS ne peut pas chiffrer une instance existante non chiffrée. | ||
| + | |||
| + | Imaginer le scénario suivant : une instance RDS existe déjà en production sans chiffrement, | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Les quatre paramètres | + | Avant de chercher la réponse, réfléchir |
| - | Expliquer | + | Est-ce que Terraform va modifier l' |
| - | Pourquoi ce bloc est-il indépendant du chiffrement ? | + | Est-ce que le chiffrement |
| + | |||
| + | Quelle est la procédure correcte dans ce cas ? | ||
| </ | </ | ||
| - | |||
| - | ===== 9. Mise en place du logging avec CloudTrail ===== | ||
| - | |||
| - | Objectif : tracer toutes les actions réalisées sur le compte AWS. | ||
| <WRAP round todo> | <WRAP round todo> | ||
| - | Créer le module logging. | + | Rechercher dans la documentation AWS RDS la section relative au chiffrement d'une instance existante. |
| + | |||
| + | Décrire en cinq étapes la procédure à suivre pour migrer une instance non chiffrée vers une instance chiffrée. | ||
| </ | </ | ||
| - | Fichier : `td3/ | + | ===== 9. Création du module logging ===== |
| + | |||
| + | Objectif : activer CloudTrail pour conserver une trace de toutes les actions réalisées sur le compte AWS. | ||
| + | |||
| + | Fichier : `td3/ | ||
| <sxh js> | <sxh js> | ||
| - | variable "account_id" { | + | variable "bucket_name" { |
| type = string | type = string | ||
| - | description = "ID du compte AWS courant" | + | description = "Nom du bucket S3 destiné à recevoir les logs CloudTrail" |
| } | } | ||
| variable " | variable " | ||
| type = string | type = string | ||
| - | description = "ARN de la cle KMS utilisee | + | description = "ARN de la clé KMS pour chiffrer les logs CloudTrail" |
| } | } | ||
| - | resource | + | variable |
| - | | + | |
| + | description = "ID du compte AWS courant" | ||
| } | } | ||
| + | </ | ||
| - | 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 725: | Ligne 596: | ||
| } | } | ||
| - | resource " | + | resource " |
| - | bucket = aws_s3_bucket.cloudtrail_logs.id | + | bucket = aws_s3_bucket.cloudtrail.id |
| policy = jsonencode({ | policy = jsonencode({ | ||
| Ligne 738: | Ligne 609: | ||
| } | } | ||
| Action | Action | ||
| - | Resource = aws_s3_bucket.cloudtrail_logs.arn | + | Resource = aws_s3_bucket.cloudtrail.arn |
| }, | }, | ||
| { | { | ||
| Ligne 747: | Ligne 618: | ||
| } | } | ||
| Action | Action | ||
| - | Resource = " | + | Resource = " |
| Condition = { | Condition = { | ||
| StringEquals = { | StringEquals = { | ||
| Ligne 760: | Ligne 631: | ||
| resource " | resource " | ||
| name = " | name = " | ||
| - | s3_bucket_name | + | s3_bucket_name |
| include_global_service_events = true | include_global_service_events = true | ||
| is_multi_region_trail | is_multi_region_trail | ||
| Ligne 766: | Ligne 637: | ||
| kms_key_id | kms_key_id | ||
| - | | + | |
| - | | + | |
| - | ] | + | TD = " |
| + | | ||
| + | |||
| + | depends_on = [aws_s3_bucket_policy.cloudtrail] | ||
| } | } | ||
| + | </ | ||
| + | Fichier : `td3/ | ||
| + | <sxh js> | ||
| output " | output " | ||
| value = aws_cloudtrail.main.arn | value = aws_cloudtrail.main.arn | ||
| description = "ARN du trail CloudTrail" | description = "ARN du trail CloudTrail" | ||
| + | } | ||
| + | |||
| + | output " | ||
| + | value = aws_s3_bucket.cloudtrail.bucket | ||
| + | description = "Nom du bucket contenant les logs CloudTrail" | ||
| } | } | ||
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | La bucket policy pour CloudTrail contient deux statements distincts. | + | Le paramètre `enable_log_file_validation = true` active la validation de l' |
| - | Expliquer le rôle de chacun : | + | Quel problème cela permet-il |
| - | * `AWSCloudTrailAclCheck` | + | |
| - | * `AWSCloudTrailWrite` | + | |
| - | Que se passerait-il si cette policy était absente | + | Quel mécanisme technique AWS utilise-t-il pour garantir |
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Le paramètre `enable_log_file_validation = true` est activé. | + | CloudTrail trace les appels API AWS. |
| - | Que permet concrètement la validation des fichiers | + | Citer trois exemples d' |
| - | Dans quel scénario cette fonctionnalité est-elle particulièrement utile ? | + | Citer deux exemples d' |
| </ | </ | ||
| - | <WRAP round question> | + | ===== 10. Assemblage dans main.tf ===== |
| - | CloudTrail enregistre les appels API réalisés sur le compte AWS. | + | |
| - | Cocher parmi ces actions celles qui sont tracées par CloudTrail | + | Objectif |
| - | * création d'un Security Group | + | |
| - | * modification d'une règle IAM | + | |
| - | * upload d'un fichier | + | |
| - | * requête SQL vers RDS | + | |
| - | * connexion SSH vers une instance EC2 | + | |
| - | * suppression d'un secret Secrets Manager | + | |
| - | Justifier les réponses non évidentes. | + | Fichier : `td3/variables.tf` |
| - | </ | + | |
| - | + | ||
| - | ===== 10. Simulation d'un incident ===== | + | |
| - | + | ||
| - | Objectif : utiliser CloudTrail pour retracer une action suspecte. | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Réaliser une action volontairement suspecte dans AWS. | + | |
| - | + | ||
| - | Modifier un Security Group existant du TD2 pour ouvrir temporairement le port 22 depuis `0.0.0.0/0` : | + | |
| - | + | ||
| - | terraform apply -target=module.network | + | |
| - | + | ||
| - | Attendre deux minutes, puis supprimer cette règle : | + | |
| - | + | ||
| - | terraform apply | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Aller dans la console AWS. | + | |
| - | + | ||
| - | Naviguer vers : CloudTrail > Event history. | + | |
| - | + | ||
| - | Rechercher l' | + | |
| - | + | ||
| - | Identifier : | + | |
| - | * l' | + | |
| - | * l' | + | |
| - | * les paramètres de la règle ajoutée | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round question> | + | |
| - | CloudTrail enregistre l' | + | |
| - | + | ||
| - | Si plusieurs développeurs partagent le même utilisateur IAM, que perd-on en termes de traçabilité ? | + | |
| - | + | ||
| - | Quel principe de gestion des identités du TD1 est directement lié à cette problématique ? | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round question> | + | |
| - | Les logs CloudTrail sont stockés dans S3. | + | |
| - | + | ||
| - | Par défaut, combien de temps sont-ils conservés dans CloudTrail Event History ? | + | |
| - | + | ||
| - | Pourquoi stocker les logs dans S3 permet-il une conservation plus longue ? | + | |
| - | </ | + | |
| - | + | ||
| - | ===== 11. Vérification globale ===== | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Vérifier les points suivants dans le code Terraform et dans la console AWS : | + | |
| - | + | ||
| - | * le mot de passe n' | + | |
| - | * la variable `db_password` est marquée `sensitive = true` | + | |
| - | * la base RDS a `storage_encrypted = true` et un `kms_key_id` renseigné | + | |
| - | * le bucket S3 applicatif a `sse_algorithm = " | + | |
| - | * le bucket S3 applicatif a les quatre paramètres `public_access_block` à `true` | + | |
| - | * CloudTrail est actif et écrit dans son bucket dédié | + | |
| - | * la clé KMS a `enable_key_rotation = true` | + | |
| - | </ | + | |
| - | + | ||
| - | ===== Challenge final ===== | + | |
| - | + | ||
| - | **Axe secrets** | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Répondre aux questions suivantes par écrit : | + | |
| - | + | ||
| - | * Où est stocké physiquement le secret de la base après le TD3 ? | + | |
| - | * Qui peut lire ce secret dans AWS ? | + | |
| - | * Que se passe-t-il si le secret Secrets Manager est lui-même chiffré avec la clé KMS TD3 et que cette clé est supprimée ? | + | |
| - | * Quelles seraient les trois premières actions à réaliser si le mot de passe de la base était compromis ? | + | |
| - | </ | + | |
| - | + | ||
| - | **Axe KMS** | + | |
| - | + | ||
| - | <WRAP round question> | + | |
| - | La clé KMS est utilisée à la fois pour RDS, S3 et CloudTrail. | + | |
| - | + | ||
| - | Quels sont les avantages de centraliser sur une seule clé ? | + | |
| - | + | ||
| - | Quels sont les risques de cette approche ? | + | |
| - | + | ||
| - | Comment organiserait-on les clés KMS dans un environnement de production avec plusieurs équipes et plusieurs applications ? | + | |
| - | </ | + | |
| - | + | ||
| - | **Axe chiffrement** | + | |
| - | + | ||
| - | <WRAP round question> | + | |
| - | Dresser un tableau récapitulatif avec les colonnes suivantes : | + | |
| - | + | ||
| - | * Ressource (RDS, S3, Secrets Manager, CloudTrail) | + | |
| - | * Type de chiffrement (au repos, en transit, les deux) | + | |
| - | * Algorithme ou mécanisme utilisé | + | |
| - | * Clé utilisée (AWS managée ou CMK TD3) | + | |
| - | + | ||
| - | Dans quel cas le chiffrement AES256 de S3 ne suffit-il pas à garantir la confidentialité des données ? | + | |
| - | </ | + | |
| - | + | ||
| - | **Axe traçabilité** | + | |
| - | + | ||
| - | <WRAP round question> | + | |
| - | 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 ? | + | |
| - | + | ||
| - | Comment aurait-on pu protéger cette ressource contre une suppression accidentelle ? | + | |
| - | + | ||
| - | Rechercher le paramètre Terraform qui permet de protéger une ressource contre la destruction. | + | |
| - | </ | + | |
| - | + | ||
| - | **Axe architecture** | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Produire un schéma de l' | + | |
| - | + | ||
| - | Le schéma doit inclure : | + | |
| - | * les composants du TD2 : ALB, backend, Security Groups, subnets | + | |
| - | * les ajouts du TD3 : Secrets Manager, KMS, RDS chiffré, S3 applicatif, CloudTrail et son bucket | + | |
| - | * 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 | + | |
| - | </ | + | |
| - | + | ||
| - | ===== Bonus ===== | + | |
| - | + | ||
| - | **Rotation automatique des secrets** | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Rechercher dans la documentation AWS comment activer la rotation automatique d'un secret Secrets Manager pour une base PostgreSQL. | + | |
| - | + | ||
| - | Identifier l'ARN de la Lambda de rotation fournie par AWS pour PostgreSQL dans la région `eu-west-3`. | + | |
| - | + | ||
| - | Ajouter la rotation dans le module secrets. | + | |
| - | </ | + | |
| - | + | ||
| - | Fichier : `td3/modules/ | + | |
| <sxh js> | <sxh js> | ||
| - | variable "rotation_lambda_arn" { | + | variable "db_password" { |
| type = string | type = string | ||
| - | description = "ARN de la Lambda | + | description = "Mot de passe de la base de données – ne pas committer" |
| - | | + | |
| } | } | ||
| - | resource | + | variable |
| - | | + | |
| - | | + | description |
| - | rotation_lambda_arn = var.rotation_lambda_arn | + | |
| - | + | ||
| - | rotation_rules { | + | |
| - | automatically_after_days = 30 | + | |
| - | } | + | |
| } | } | ||
| - | </ | ||
| - | <WRAP round question> | + | variable |
| - | Le bloc `count = var.rotation_lambda_arn != "" | + | |
| - | + | | |
| - | Que signifie ce pattern dans Terraform ? | + | |
| - | + | ||
| - | Pourquoi est-il utile de rendre la rotation optionnelle plutôt qu' | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round question> | + | |
| - | Après une rotation automatique, | + | |
| - | + | ||
| - | Si l' | + | |
| - | + | ||
| - | Comment une application bien conçue doit-elle récupérer les secrets pour éviter ce problème ? | + | |
| - | </ | + | |
| - | + | ||
| - | ===== 4. Recuperation des ressources existantes du TD2 ===== | + | |
| - | + | ||
| - | Plutôt que de recréer les ressources, on les référence via des data sources. | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Créer le fichier principal du TD3. | + | |
| - | </ | + | |
| - | + | ||
| - | Fichier : `td3/ | + | |
| - | <sxh hcl> | + | |
| - | # Recuperation du VPC existant du TD2 | + | |
| - | data " | + | |
| - | | + | |
| - | name = " | + | |
| - | values = [" | + | |
| - | | + | |
| - | } | + | |
| - | + | ||
| - | # Recuperation du Security Group du backend TD2 | + | |
| - | data " | + | |
| - | filter { | + | |
| - | name = "tag: | + | |
| - | values = [" | + | |
| - | } | + | |
| - | vpc_id = data.aws_vpc.td2.id | + | |
| - | } | + | |
| - | + | ||
| - | # Recuperation | + | |
| - | data "aws_db_subnet_group" | + | |
| - | | + | |
| } | } | ||
| </ | </ | ||
| - | <WRAP round help> | + | Fichier : `td3/ |
| - | Une data source ne crée rien dans AWS. | + | <sxh js> |
| + | data " | ||
| - | Que fait Terraform quand il rencontre un bloc `data` lors d'un `terraform plan` ? | + | module " |
| - | + | | |
| - | Que se passe-t-il si la ressource référencée n' | + | |
| - | </ | + | |
| - | + | ||
| - | ===== 5. Module secrets ===== | + | |
| - | + | ||
| - | Objectif : stocker le mot de passe dans AWS Secrets Manager, pas dans le code. | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Créer le module de gestion des secrets. | + | |
| - | </ | + | |
| - | + | ||
| - | Fichier : `td3/modules/secrets/ | + | |
| - | <sxh hcl> | + | |
| - | variable | + | |
| - | | + | |
| - | sensitive = true | + | |
| } | } | ||
| - | resource " | ||
| - | name = " | ||
| - | description | ||
| - | recovery_window_in_days = 0 | ||
| - | } | ||
| - | |||
| - | resource " | ||
| - | secret_id = aws_secretsmanager_secret.db_credentials.id | ||
| - | |||
| - | secret_string = jsonencode({ | ||
| - | username = " | ||
| - | password = var.db_password | ||
| - | }) | ||
| - | } | ||
| - | |||
| - | output " | ||
| - | description = "ARN du secret a transmettre au module database" | ||
| - | value = aws_secretsmanager_secret.db_credentials.arn | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | <WRAP round help> | ||
| - | Le secret est stocké au format JSON avec deux champs : `username` et `password`. | ||
| - | |||
| - | Pourquoi utilise-t-on un objet JSON plutôt qu'une simple chaîne de caractères ? | ||
| - | |||
| - | Quel avantage si on doit ajouter le port ou le nom de la base plus tard ? | ||
| - | </ | ||
| - | |||
| - | <WRAP round help> | ||
| - | Le paramètre `recovery_window_in_days = 0` supprime le secret immédiatement lors d'un `terraform destroy`. | ||
| - | |||
| - | Quelle est la valeur par défaut d'AWS pour ce paramètre ? | ||
| - | |||
| - | Pourquoi cette valeur par défaut existe-t-elle en production ? | ||
| - | </ | ||
| - | |||
| - | <WRAP round todo> | ||
| - | Ajouter l' | ||
| - | </ | ||
| - | |||
| - | Fichier : `td3/ | ||
| - | <sxh hcl> | ||
| module " | module " | ||
| source | source | ||
| - | db_password = var.db_initial_password | + | |
| + | | ||
| } | } | ||
| - | </ | ||
| - | ===== 6. Probleme volontaire : ordre d' | + | module " |
| + | source | ||
| - | <WRAP round todo> | + | kms_key_arn = module.kms.key_arn |
| - | Avant d' | + | |
| - | <sxh hcl> | + | db_username = module.secrets.db_username |
| - | data " | + | |
| - | | + | |
| - | } | + | |
| - | </ | + | |
| - | Ajouter ce bloc temporairement dans `main.tf` et lancer : | + | subnet_ids = [ |
| + | data.aws_subnet.private.id, | ||
| + | data.aws_subnet.private_b.id | ||
| + | ] | ||
| - | <sxh bash> | + | security_group_id = data.aws_security_group.rds.id |
| - | terraform plan | + | |
| - | </ | + | |
| - | </ | + | |
| - | <WRAP round help> | + | bucket_name |
| - | Que se passe-t-il lors du plan ? | + | |
| - | + | ||
| - | Terraform signale-t-il une erreur, et si oui, laquelle ? | + | |
| - | + | ||
| - | Pourquoi Terraform ne peut-il pas garantir que le secret existe au moment où la data source est évaluée ? | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round help> | + | |
| - | Quelle est la différence fondamentale entre un bloc `resource` et un bloc `data` dans le cycle d' | + | |
| - | + | ||
| - | Lequel est évalué lors de la phase de lecture, avant toute création ? | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Supprimer ce bloc temporaire avant de continuer. | + | |
| - | </ | + | |
| - | + | ||
| - | ===== 7. Module database ===== | + | |
| - | + | ||
| - | Objectif : remplacer la ressource RDS du TD2 par une version sécurisée. | + | |
| - | + | ||
| - | La dépendance entre le secret et la base est gérée implicitement via le passage de l'ARN en variable. | + | |
| - | + | ||
| - | Terraform comprend qu'il doit créer le secret avant de lire son contenu car la valeur vient d'un output du module `secrets`. | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Créer le module base de données. | + | |
| - | </ | + | |
| - | + | ||
| - | Fichier : `td3/ | + | |
| - | <sxh hcl> | + | |
| - | variable " | + | |
| - | type = string | + | |
| - | description = "ARN du secret contenant les identifiants de la base" | + | |
| } | } | ||
| - | variable | + | module |
| - | | + | |
| - | description | + | |
| - | } | + | |
| - | + | | |
| - | variable " | + | |
| - | | + | |
| - | description = "Liste des Security Group IDs autorises a acceder a la base" | + | |
| - | } | + | |
| - | + | ||
| - | data " | + | |
| - | secret_id | + | |
| - | } | + | |
| - | + | ||
| - | locals { | + | |
| - | | + | |
| - | } | + | |
| - | + | ||
| - | resource " | + | |
| - | | + | |
| - | engine | + | |
| - | engine_version | + | |
| - | instance_class | + | |
| - | allocated_storage = 20 | + | |
| - | + | ||
| - | db_name | + | |
| - | username = local.db_creds[" | + | |
| - | password = local.db_creds[" | + | |
| - | + | ||
| - | db_subnet_group_name | + | |
| - | vpc_security_group_ids = var.vpc_security_group_ids | + | |
| - | + | ||
| - | storage_encrypted | + | |
| - | skip_final_snapshot = true | + | |
| - | + | ||
| - | tags = { | + | |
| - | Name = " | + | |
| - | } | + | |
| - | } | + | |
| - | + | ||
| - | output " | + | |
| - | description = " | + | |
| - | value = aws_db_instance.main.endpoint | + | |
| } | } | ||
| </ | </ | ||
| - | <WRAP round todo> | + | <WRAP round question> |
| - | Ajouter l' | + | Le module |
| - | </ | + | |
| - | Fichier : `td3/main.tf` — ajouter après le module secrets | + | Regarder la définition des outputs dans `td3/modules/ |
| - | <sxh hcl> | + | |
| - | module " | + | |
| - | source | + | |
| - | secret_arn | + | |
| - | db_subnet_group_name | + | Que faut-il ajouter sur l' |
| - | vpc_security_group_ids = [data.aws_security_group.backend.id] | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round help> | + | |
| - | La valeur | + | |
| - | + | ||
| - | Terraform en déduit-il automatiquement | + | |
| - | + | ||
| - | Dans quel cas faudrait-il ajouter | + | |
| </ | </ | ||
| - | <WRAP round help> | + | Fichier : `td3/ |
| - | Le paramètre `storage_encrypted = true` a été ajouté. | + | < |
| - | + | output | |
| - | Que protège exactement ce chiffrement ? | + | |
| - | + | description = "Nom d' | |
| - | 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/ | + | |
| - | < | + | |
| - | resource | + | |
| - | | + | |
| } | } | ||
| - | resource | + | output |
| - | | + | |
| - | + | | |
| - | | + | |
| - | Name = "td3-secure-data" | + | |
| - | | + | |
| - | } | + | |
| - | + | ||
| - | resource " | + | |
| - | bucket = aws_s3_bucket.secure_data.id | + | |
| - | + | ||
| - | rule { | + | |
| - | apply_server_side_encryption_by_default { | + | |
| - | sse_algorithm = " | + | |
| - | } | + | |
| - | bucket_key_enabled | + | |
| - | } | + | |
| - | } | + | |
| - | + | ||
| - | resource " | + | |
| - | bucket = aws_s3_bucket.secure_data.id | + | |
| - | + | ||
| - | block_public_acls | + | |
| - | block_public_policy | + | |
| - | ignore_public_acls | + | |
| - | restrict_public_buckets = true | + | |
| - | } | + | |
| - | + | ||
| - | output " | + | |
| - | description = "Nom du bucket S3 securise" | + | |
| - | value = aws_s3_bucket.secure_data.id | + | |
| } | } | ||
| </ | </ | ||
| - | <WRAP round todo> | + | ===== 11. Déploiement et vérification ===== |
| - | Ajouter l' | + | |
| - | </ | + | |
| - | Fichier : `td3/main.tf` — ajouter après le module database | + | Fichier : `td3/commandes/ |
| - | < | + | < |
| - | module " | + | # Se placer dans le dossier td3 |
| - | | + | cd tp-aws/td3 |
| - | } | + | |
| - | </ | + | |
| - | <WRAP round help> | + | # Initialiser le projet |
| - | La configuration `apply_server_side_encryption_by_default` est activée. | + | terraform init |
| - | Que se passe-t-il si un développeur uploade un fichier sans spécifier de chiffrement dans son appel SDK ou CLI ? | + | # Vérifier le plan – inspecter les ressources créées et les data sources résolus |
| + | terraform plan -var=" | ||
| - | La configuration par défaut s' | + | # Appliquer |
| - | </ | + | terraform apply -var=" |
| - | <WRAP round help> | + | # Vérifier les outputs |
| - | Les quatre paramètres `block_public_acls`, | + | terraform output |
| - | Les Security Groups du TD2 contrôlent déjà | + | # Vérifier |
| + | aws secretsmanager get-secret-value --secret-id td3/ | ||
| - | Pourquoi bloquer également les accès publics au niveau du bucket S3 ? | + | # Vérifier que le bucket S3 est bien chiffré |
| + | aws s3api get-bucket-encryption --bucket td3-app-bucket-demo --region eu-west-3 | ||
| - | S3 est-il soumis aux Security Groups ? | + | # Vérifier que CloudTrail |
| - | </ | + | aws cloudtrail |
| - | + | ||
| - | ===== 9. Module logging ===== | + | |
| - | + | ||
| - | Objectif : tracer toutes les actions réalisées sur le compte AWS via CloudTrail. | + | |
| - | + | ||
| - | CloudTrail écrit ses logs dans un bucket S3 dédié. | + | |
| - | + | ||
| - | AWS exige une policy bucket explicite pour autoriser CloudTrail à écrire. | + | |
| - | + | ||
| - | Sans cette policy, la ressource `aws_cloudtrail` échoue au déploiement. | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Créer le module de logging. | + | |
| - | </ | + | |
| - | + | ||
| - | Fichier : `td3/ | + | |
| - | <sxh hcl> | + | |
| - | data " | + | |
| - | + | ||
| - | resource " | + | |
| - | bucket = "td3-cloudtrail-${data.aws_caller_identity.current.account_id}" | + | |
| - | + | ||
| - | tags = { | + | |
| - | Name = "td3-cloudtrail-logs" | + | |
| - | } | + | |
| - | } | + | |
| - | + | ||
| - | resource " | + | |
| - | bucket = aws_s3_bucket.cloudtrail_logs.id | + | |
| - | + | ||
| - | block_public_acls | + | |
| - | block_public_policy | + | |
| - | ignore_public_acls | + | |
| - | restrict_public_buckets = true | + | |
| - | } | + | |
| - | + | ||
| - | resource " | + | |
| - | bucket = aws_s3_bucket.cloudtrail_logs.id | + | |
| - | + | ||
| - | policy = jsonencode({ | + | |
| - | Version = "2012-10-17" | + | |
| - | Statement = [ | + | |
| - | { | + | |
| - | Sid = " | + | |
| - | Effect = " | + | |
| - | Principal = { | + | |
| - | Service = "cloudtrail.amazonaws.com" | + | |
| - | } | + | |
| - | Action | + | |
| - | Resource = aws_s3_bucket.cloudtrail_logs.arn | + | |
| - | }, | + | |
| - | { | + | |
| - | Sid = " | + | |
| - | Effect = " | + | |
| - | Principal = { | + | |
| - | Service = " | + | |
| - | } | + | |
| - | Action | + | |
| - | Resource = " | + | |
| - | Condition = { | + | |
| - | StringEquals = { | + | |
| - | "s3:x-amz-acl" = " | + | |
| - | } | + | |
| - | } | + | |
| - | } | + | |
| - | ] | + | |
| - | }) | + | |
| - | + | ||
| - | depends_on = [aws_s3_bucket_public_access_block.cloudtrail_logs] | + | |
| - | } | + | |
| - | + | ||
| - | resource " | + | |
| - | name = " | + | |
| - | s3_bucket_name | + | |
| - | include_global_service_events = true | + | |
| - | is_multi_region_trail | + | |
| - | enable_log_file_validation | + | |
| - | + | ||
| - | tags = { | + | |
| - | Name = " | + | |
| - | } | + | |
| - | + | ||
| - | depends_on = [aws_s3_bucket_policy.cloudtrail_logs] | + | |
| - | } | + | |
| - | + | ||
| - | output " | + | |
| - | description = "Nom du trail CloudTrail actif" | + | |
| - | value = aws_cloudtrail.main.name | + | |
| - | } | + | |
| </ | </ | ||
| <WRAP round todo> | <WRAP round todo> | ||
| - | Ajouter l' | + | Après |
| - | </ | + | |
| - | Fichier | + | * Secrets Manager |
| - | <sxh hcl> | + | |
| - | module " | + | * RDS : l' |
| - | | + | * S3 : le bucket applicatif indique `SSE-KMS` dans ses propriétés |
| - | } | + | * CloudTrail : le trail `td3-cloudtrail` est actif et envoie des logs dans le bucket |
| - | </ | + | |
| - | + | ||
| - | <WRAP round help> | + | |
| - | La policy S3 contient deux instructions distinctes | + | |
| - | + | ||
| - | Pourquoi CloudTrail a-t-il besoin | + | |
| - | + | ||
| - | Que se serait-il passé si on avait appliqué | + | |
| </ | </ | ||
| - | <WRAP round help> | + | ===== 12. Lecture des logs CloudTrail ===== |
| - | Le paramètre `enable_log_file_validation | + | |
| - | Que génère AWS pour permettre cette validation ? | + | Objectif : naviguer dans les logs CloudTrail |
| - | + | ||
| - | Comment un administrateur peut-il vérifier qu'un fichier de log n'a pas été modifié ou supprimé ? | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round help> | + | |
| - | CloudTrail enregistre | + | |
| - | + | ||
| - | Les modifications de Security Groups réalisées dans le TD2 via Terraform auraient-elles été enregistrées si CloudTrail | + | |
| - | + | ||
| - | Quelle différence entre CloudTrail et VPC Flow Logs ? | + | |
| - | </ | + | |
| - | + | ||
| - | ===== 10. Fichier outputs global ===== | + | |
| <WRAP round todo> | <WRAP round todo> | ||
| - | Créer le fichier d' | + | Dans la console AWS, ouvrir CloudTrail puis Event History. |
| - | </ | + | |
| - | Fichier : `td3/ | + | Filtrer les événements par type `CreateSecret`. |
| - | <sxh hcl> | + | |
| - | output " | + | |
| - | description = " | + | |
| - | value = module.database.db_endpoint | + | |
| - | } | + | |
| - | output " | + | Retrouver l' |
| - | description = " | + | |
| - | value = module.storage.bucket_name | + | |
| - | } | + | |
| - | output " | + | Identifier les champs suivants dans l' |
| - | description = "Nom du trail CloudTrail actif" | + | |
| - | value = module.logging.cloudtrail_name | + | |
| - | } | + | |
| - | </ | + | |
| - | ===== 11. Deploiement complet ===== | + | * `userIdentity` : qui a réalisé l'action |
| - | + | * `eventTime` : quand | |
| - | <WRAP round todo> | + | * `sourceIPAddress` : depuis quelle IP |
| - | Initialiser le projet et vérifier le plan avant d'appliquer. | + | * `requestParameters` : quel secret a été créé |
| - | + | ||
| - | <sxh bash> | + | |
| - | cd td3/ | + | |
| - | terraform init | + | |
| - | terraform plan | + | |
| - | terraform apply | + | |
| - | </ | + | |
| - | + | ||
| - | Relever les outputs après application. | + | |
| </ | </ | ||
| - | <WRAP round help> | + | <WRAP round question> |
| - | Lors du `terraform plan`, combien de ressources sont listées comme a créer ? | + | Un collègue vous signale qu'une clé KMS a été désactivée à 3h du matin sans explication. |
| - | Les data sources vers le TD2 apparaissent-elles | + | Quelle requête effectuer |
| - | Que se passe-t-il si le VPC `td2-vpc` n'existe pas au moment du plan ? | + | Quels champs de l'événement permettent d' |
| </ | </ | ||
| - | ===== 12. Simulation d'un incident et lecture des logs ===== | + | ===== Challenge final ===== |
| - | Objectif : vérifier que CloudTrail trace bien les actions réalisées sur le compte. | + | L' |
| - | <WRAP round todo> | + | Rédiger |
| - | Modifier temporairement | + | |
| - | Par exemple, rouvrir le port 5432 sur `0.0.0.0/0` dans `td2/ | + | **Axe 1 – Secrets** |
| - | Appliquer ce changement : | + | Expliquer comment les identifiants RDS sont maintenant gérés. |
| - | <sxh bash> | + | Comparer l' |
| - | cd td2/ | + | |
| - | terraform apply | + | |
| - | </ | + | |
| - | Attendre deux minutes. | + | Indiquer ce qui resterait à améliorer si l' |
| - | Se connecter à la console AWS et naviguer vers : | + | **Axe 2 – Clés KMS** |
| - | CloudTrail > Event history | + | Décrire le cycle de vie de la clé créée dans ce TD. |
| - | Filtrer par nom d' | + | Expliquer ce qui se passe lors de la rotation automatique. |
| - | </ | + | |
| - | <WRAP round help> | + | Indiquer le délai de suppression et pourquoi |
| - | L' | + | |
| - | Quelles informations sont disponibles dans le détail de l' | + | **Axe 3 – Chiffrement des données** |
| - | | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | </ | + | |
| - | <WRAP round help> | + | Compléter |
| - | Un Security Group du TD2 a été ouvert sur Internet par erreur un vendredi soir. | + | |
| - | + | ||
| - | L' | + | |
| - | + | ||
| - | Comment CloudTrail permet-il de répondre aux questions suivantes sans CloudTrail et avec CloudTrail | + | |
| - | * depuis combien de temps la faille est-elle ouverte ? | + | |
| - | * qui a fait la modification ? | + | |
| - | * s' | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP round todo> | + | |
| - | Remettre le Security Group du TD2 dans son état sécurisé après l' | + | |
| + | Fichier : `td3/ | ||
| <sxh bash> | <sxh bash> | ||
| - | cd td2/ | + | | Ressource |
| - | terraform apply | + | |------------|---------------------|---------------------|---------------------------| |
| + | | RDS | SSE-KMS | ||
| + | | S3 app | SSE-KMS | ||
| + | | S3 logs | ? | ? | ? | | ||
| + | | Transit RDS| ? | ? | ? | | ||
| </ | </ | ||
| - | </ | ||
| - | <WRAP round help> | + | **Axe 4 – Traçabilité** |
| - | 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 ? | + | Décrire ce que CloudTrail trace dans le contexte de ce TD. |
| - | </ | + | |
| - | ===== 13. Vérification globale ===== | + | Expliquer pourquoi le bucket de logs est lui-même protégé par une policy stricte. |
| - | <WRAP round todo> | + | Indiquer comment détecter une tentative |
| - | Vérifier les points suivants avant de passer au challenge : | + | |
| - | + | ||
| - | * aucun mot de passe n' | + | |
| - | * 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 d' | + | |
| - | * CloudTrail est actif dans la console AWS | + | |
| - | * la validation | + | |
| - | </ | + | |
| - | + | ||
| - | ===== Challenge final ===== | + | |
| - | + | ||
| - | **Axe secrets** | + | |
| - | + | ||
| - | <WRAP round help> | + | |
| - | 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 ? | + | |
| - | + | ||
| - | Comment restreindre l' | + | |
| - | </ | + | |
| - | + | ||
| - | **Axe chiffrement** | + | |
| - | + | ||
| - | <WRAP round help> | + | |
| - | 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 ? | + | |
| - | + | ||
| - | Dans quel cas le chiffrement AES256 de S3 ne suffit-il pas a garantir la confidentialite des données ? | + | |
| - | </ | + | |
| - | + | ||
| - | **Axe traçabilité** | + | |
| - | + | ||
| - | <WRAP round help> | + | |
| - | Un stagiaire supprime accidentellement la ressource | + | |
| - | + | ||
| - | Quelles sont les conséquences immédiates pour la traçabilité du compte ? | + | |
| - | + | ||
| - | Comment aurait-on pu protéger cette ressource contre une suppression accidentelle ? | + | |
| - | + | ||
| - | Chercher le paramètre Terraform qui permet de protéger une ressource contre la destruction. | + | |
| - | </ | + | |
| - | + | ||
| - | **Axe architecture** | + | |
| <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 1600: | Ligne 895: | ||
| Fichier : `td3/ | Fichier : `td3/ | ||
| - | < | + | < |
| variable " | variable " | ||
| type = string | type = string | ||
| Ligne 1618: | 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' | ||