Différences
Ci-dessous, les différences entre deux révisions de la page.
| Prochaine révision | Révision précédente | ||
| eadl:bloc4:fm4:td2 [2026/06/14 12:42] – créée jcheron | eadl:bloc4:fm4:td2 [2026/06/14 17:07] (Version actuelle) – [6. Correction – restriction du backend] jcheron | ||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| - | ====== TD2 – Sécurisation réseau AWS (VPC, Subnets, Security Groups) ====== | + | ====== TD2 – Sécurisation réseau AWS (VPC, Security Groups, ALB) ====== |
| ===== Objectifs ===== | ===== Objectifs ===== | ||
| - | * Comprendre | + | * Comprendre |
| - | * Identifier les mauvaises pratiques réseau | + | * Identifier les mauvaises pratiques |
| - | * Sécuriser une infrastructure existante | + | * Mettre en place un point d' |
| - | * Implémenter une segmentation | + | * Contrôler les flux réseau |
| - | * Industrialiser avec Terraform | + | * Appliquer le principe du moindre privilège sur chaque couche |
| ===== Contexte ===== | ===== Contexte ===== | ||
| - | Vous intervenez dans la même startup. | + | Vous intervenez |
| - | Une première infrastructure | + | L' |
| - | Objectif initial : mettre en ligne une application web. | + | Le déploiement a été fait rapidement pour respecter un délai. |
| Résultat : | Résultat : | ||
| - | * tout fonctionne | + | * l' |
| - | * mais aucune règle de sécurité réseau n’a été pensée | + | * mais toute l' |
| - | Un audit révèle | + | Un audit de sécurité interne a identifié trois problèmes critiques |
| - | * exposition publique excessive | + | * le port applicatif du backend est ouvert à Internet |
| - | * absence | + | * le port de la base de données est ouvert à Internet |
| - | * règles | + | * aucun point d' |
| - | ===== Objectif technique ===== | + | Votre mission : transformer l' |
| - | Reconcevoir le réseau pour : | + | ===== Architecture cible ===== |
| - | * isoler les composants | + | L' |
| - | | + | |
| - | | + | |
| + | Internet → DB (port 5432 ouvert partout) | ||
| + | |||
| + | L' | ||
| + | |||
| + | | ||
| ===== Contraintes ===== | ===== Contraintes ===== | ||
| - | * Ne pas casser l’accès à l’application | + | * L'application |
| - | * Appliquer le principe du moindre accès réseau | + | * Le backend ne doit plus être accessible directement depuis Internet |
| - | * Utiliser Terraform uniquement | + | * La base de données ne doit être accessible que depuis le backend |
| - | * Architecture simple et compréhensible | + | * Tout le code est en Terraform |
| - | ===== Infrastructure existante | + | ===== Pré-requis |
| - | Fichier : `network/ | + | * Terraform installé et configuré |
| + | * Credentials AWS valides | ||
| + | * Notions de base : VPC, subnet, Security Group | ||
| + | |||
| + | ===== Infrastructure de départ ===== | ||
| + | |||
| + | L' | ||
| + | |||
| + | Lire le code attentivement avant de l' | ||
| + | |||
| + | Fichier : `td2/network/ | ||
| <sxh js> | <sxh js> | ||
| provider " | provider " | ||
| - | region = " | + | region = " |
| } | } | ||
| + | # VPC principal | ||
| resource " | resource " | ||
| - | cidr_block = " | + | cidr_block |
| + | enable_dns_hostnames = true | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| } | } | ||
| - | resource "aws_subnet" "public" { | + | # Passerelle Internet |
| - | vpc_id | + | resource "aws_internet_gateway" "igw" { |
| - | | + | vpc_id = aws_vpc.main.id |
| + | |||
| + | | ||
| + | Name = "td2-igw" | ||
| + | } | ||
| } | } | ||
| - | resource "aws_instance" "web" { | + | # Subnet public – AZ a |
| - | | + | resource "aws_subnet" "public_a" { |
| - | | + | |
| - | | + | cidr_block |
| + | | ||
| + | | ||
| + | |||
| + | tags = { | ||
| + | Name = "td2-public-a" | ||
| + | } | ||
| } | } | ||
| - | resource " | + | # Subnet public – AZ b (requis par l' |
| - | name = "web-sg" | + | resource " |
| + | vpc_id | ||
| + | cidr_block | ||
| + | availability_zone | ||
| + | map_public_ip_on_launch = true | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | # Subnet privé – DB | ||
| + | resource " | ||
| + | vpc_id | ||
| + | cidr_block | ||
| + | availability_zone = " | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | # Table de routage publique | ||
| + | resource " | ||
| + | vpc_id = aws_vpc.main.id | ||
| + | |||
| + | route { | ||
| + | cidr_block = " | ||
| + | gateway_id = aws_internet_gateway.igw.id | ||
| + | } | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | resource " | ||
| + | subnet_id | ||
| + | route_table_id = aws_route_table.public.id | ||
| + | } | ||
| + | |||
| + | resource " | ||
| + | subnet_id | ||
| + | route_table_id = aws_route_table.public.id | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Fichier : `td2/ | ||
| + | <sxh js> | ||
| + | # Security Group backend – PROBLEME VOLONTAIRE | ||
| + | # Ce fichier sera modifié au cours du TD | ||
| + | resource " | ||
| + | name = "backend-sg" | ||
| vpc_id = aws_vpc.main.id | vpc_id = aws_vpc.main.id | ||
| ingress { | ingress { | ||
| + | description = "HTTP depuis Internet" | ||
| + | from_port | ||
| + | to_port | ||
| + | protocol | ||
| + | cidr_blocks = [" | ||
| + | } | ||
| + | |||
| + | egress { | ||
| from_port | from_port | ||
| to_port | to_port | ||
| protocol | protocol | ||
| + | cidr_blocks = [" | ||
| + | } | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | # Security Group DB – PROBLEME VOLONTAIRE | ||
| + | resource " | ||
| + | name = " | ||
| + | vpc_id = aws_vpc.main.id | ||
| + | |||
| + | ingress { | ||
| + | description = " | ||
| + | from_port | ||
| + | to_port | ||
| + | protocol | ||
| cidr_blocks = [" | cidr_blocks = [" | ||
| } | } | ||
| Ligne 82: | Ligne 191: | ||
| protocol | protocol | ||
| cidr_blocks = [" | cidr_blocks = [" | ||
| + | } | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Fichier : `td2/ | ||
| + | <sxh js> | ||
| + | resource " | ||
| + | ami = " | ||
| + | instance_type | ||
| + | subnet_id | ||
| + | vpc_security_group_ids = [aws_security_group.backend_sg.id] | ||
| + | |||
| + | user_data = << | ||
| + | #!/bin/bash | ||
| + | yum install -y python3 | ||
| + | python3 -m http.server 80 & | ||
| + | EOF | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | resource " | ||
| + | identifier | ||
| + | engine | ||
| + | engine_version | ||
| + | instance_class | ||
| + | allocated_storage | ||
| + | username | ||
| + | password | ||
| + | db_subnet_group_name | ||
| + | vpc_security_group_ids = [aws_security_group.db_sg.id] | ||
| + | skip_final_snapshot | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | resource " | ||
| + | name = " | ||
| + | subnet_ids = [aws_subnet.private.id, | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| } | } | ||
| } | } | ||
| </ | </ | ||
| - | ===== Ressources | + | ===== Commandes Terraform |
| - | Fichier : `commandes/ | + | Fichier : `td2/commandes/ |
| <sxh bash> | <sxh bash> | ||
| + | # Initialiser le projet | ||
| terraform init | terraform init | ||
| + | |||
| + | # Vérifier le plan sans appliquer | ||
| terraform plan | terraform plan | ||
| + | |||
| + | # Appliquer la configuration | ||
| terraform apply | terraform apply | ||
| + | |||
| + | # Récupérer les outputs | ||
| + | terraform output | ||
| + | |||
| + | # Détruire l' | ||
| terraform destroy | terraform destroy | ||
| </ | </ | ||
| - | ===== 1. Compréhension de l’existant | + | ===== 1. Lecture du code ===== |
| + | |||
| + | Lire les trois fichiers avant toute manipulation. | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Quels composants réseau sont présents ? | + | Lister les ressources présentes dans le code. |
| - | Que manque-t-il pour une architecture AWS correcte ? | + | Pour chaque ressource, indiquer : |
| + | * son rôle | ||
| + | * le subnet dans lequel elle est placée | ||
| + | * les ports ouverts en entrée | ||
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Pourquoi | + | Pourquoi |
| - | Quels sont les risques immédiats | + | Quelle contrainte AWS cela reflète-t-il |
| </ | </ | ||
| - | ===== 2. Analyse | + | <WRAP round question> |
| + | La base de données est une instance RDS et non une instance EC2. | ||
| + | |||
| + | Quelle est la différence en termes de gestion et de sécurité ? | ||
| + | </ | ||
| + | |||
| + | ===== 2. Identification | ||
| + | |||
| + | <WRAP round todo> | ||
| + | Identifier dans `security_groups.tf` les deux règles les plus dangereuses. | ||
| + | |||
| + | Pour chaque règle : | ||
| + | * expliquer ce qu' | ||
| + | * expliquer ce qu'un attaquant pourrait faire avec cet accès | ||
| + | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Identifier les problèmes | + | Le mot de passe de la base de données est en clair dans le fichier Terraform. |
| - | * au niveau du VPC | + | Quel problème cela pose-t-il ? |
| - | * au niveau des subnets | + | |
| - | * au niveau | + | Ce problème sera traité dans un TD ultérieur sur la gestion |
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Quelle | + | La base de données RDS est placée |
| - | Pourquoi ? | + | Est-ce un problème ? Pourquoi ? |
| </ | </ | ||
| - | ===== 3. Mise en pratique – segmentation réseau | + | ===== 3. Déploiement de l' |
| <WRAP round todo> | <WRAP round todo> | ||
| + | Appliquer la configuration telle quelle : | ||
| - | Modifier l’architecture pour : | + | terraform apply |
| - | * créer 2 subnets | + | Relever les outputs suivants |
| - | * public (web) | + | * IP publique de l' |
| - | * privé (backend ou base de données) | + | * endpoint |
| + | </ | ||
| - | * ajouter | + | Fichier : `td2/ |
| + | <sxh js> | ||
| + | output " | ||
| + | value = aws_instance.backend.public_ip | ||
| + | description = "IP publique du backend" | ||
| + | } | ||
| + | |||
| + | output " | ||
| + | value = aws_db_instance.db.endpoint | ||
| + | description = " | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | <WRAP round todo> | ||
| + | Tester l' | ||
| + | |||
| + | curl http://< | ||
| + | |||
| + | Tenter une connexion à la base de données depuis votre poste : | ||
| + | |||
| + | psql -h < | ||
| + | |||
| + | Noter le résultat de chaque test. | ||
| + | </ | ||
| + | |||
| + | <WRAP round question> | ||
| + | Le backend répond-il ? | ||
| - | * rendre uniquement le subnet public accessible | + | La connexion à la base de données est-elle possible |
| + | Pourquoi ce comportement est-il problématique dans un contexte de production ? | ||
| </ | </ | ||
| - | ===== 4. Mise en pratique – sécurisation des flux ===== | + | ===== 4. Mise en place de l' |
| <WRAP round todo> | <WRAP round todo> | ||
| + | Créer le fichier suivant sans modifier les Security Groups existants. | ||
| + | </ | ||
| - | Corriger les Security Groups | + | Fichier |
| + | <sxh js> | ||
| + | # Security Group de l' | ||
| + | resource " | ||
| + | name = " | ||
| + | vpc_id = aws_vpc.main.id | ||
| - | | + | |
| - | | + | |
| - | | + | |
| + | to_port | ||
| + | protocol | ||
| + | cidr_blocks = [" | ||
| + | } | ||
| - | | + | |
| + | from_port | ||
| + | to_port | ||
| + | protocol | ||
| + | cidr_blocks = [" | ||
| + | } | ||
| - | </ | + | tags = { |
| + | Name = " | ||
| + | } | ||
| + | } | ||
| - | ===== Point d’attention (erreur volontaire) ===== | + | # Application Load Balancer |
| + | resource " | ||
| + | name = " | ||
| + | internal | ||
| + | load_balancer_type | ||
| + | security_groups | ||
| + | subnets | ||
| - | Le code actuel autorise : | + | tags = { |
| + | Name = " | ||
| + | } | ||
| + | } | ||
| - | * tous les ports | + | # Target Group |
| - | | + | resource " |
| - | | + | |
| + | | ||
| + | protocol = " | ||
| + | vpc_id | ||
| - | <WRAP round question> | + | health_check { |
| - | Pourquoi cette configuration est-elle fréquente en phase de développement ? | + | |
| + | healthy_threshold | ||
| + | unhealthy_threshold = 2 | ||
| + | interval | ||
| + | } | ||
| - | Pourquoi devient-elle critique en production ? | + | tags = { |
| - | </ | + | Name = "td2-backend-tg" |
| + | } | ||
| + | } | ||
| - | ===== 5. Apparition d’un problème ===== | + | # Attachement du backend au Target Group |
| + | resource " | ||
| + | target_group_arn | ||
| + | target_id | ||
| + | port = 80 | ||
| + | } | ||
| - | Après sécurisation : | + | # Listener HTTP |
| + | resource " | ||
| + | load_balancer_arn = aws_lb.main.arn | ||
| + | port = 80 | ||
| + | protocol | ||
| - | | + | |
| + | type = " | ||
| + | target_group_arn = aws_lb_target_group.backend.arn | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | <WRAP round todo> | ||
| + | Ajouter l' | ||
| + | |||
| + | output " | ||
| + | value = aws_lb.main.dns_name | ||
| + | description = "DNS public de l' | ||
| + | } | ||
| + | |||
| + | Appliquer : | ||
| + | |||
| + | terraform apply | ||
| + | |||
| + | Tester l' | ||
| + | |||
| + | curl http://< | ||
| + | |||
| + | Tester également l' | ||
| + | |||
| + | curl http://< | ||
| + | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Quelles sont les causes possibles | + | Les deux accès fonctionnent-ils |
| - | Quels éléments réseau faut-il vérifier | + | Qu' |
| + | |||
| + | L'ALB est-il suffisant seul pour sécuriser le backend | ||
| </ | </ | ||
| - | ===== 6. Analyse | + | ===== 5. Erreur courante – le backend reste exposé |
| + | |||
| + | L'ALB est en place mais le Security Group du backend n'a pas été modifié. | ||
| + | |||
| + | Le port 80 du backend est toujours ouvert sur `0.0.0.0/ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Vérifier : | + | Pourquoi ajouter un ALB ne suffit-il pas à protéger le backend ? |
| - | * association | + | Quel composant contrôle réellement les accès réseau sur une instance AWS ? |
| - | * table de routage | + | </ |
| - | * subnet public vs privé | + | |
| - | * présence d’une IP publique | + | ===== 6. Correction – restriction |
| + | |||
| + | <WRAP round todo> | ||
| + | Modifier `security_groups.tf`. | ||
| - | Lequel | + | Remplacer la règle ingress du Security Group `backend_sg` : |
| + | * supprimer l' | ||
| + | * autoriser uniquement le trafic depuis le Security Group de l'ALB | ||
| </ | </ | ||
| - | ===== 7. Correction | + | Fichier : `td2/ |
| + | <sxh js> | ||
| + | resource " | ||
| + | name = " | ||
| + | vpc_id | ||
| + | |||
| + | ingress { | ||
| + | description | ||
| + | from_port | ||
| + | to_port | ||
| + | protocol | ||
| + | security_groups = [aws_security_group.alb_sg.id] | ||
| + | } | ||
| + | |||
| + | egress { | ||
| + | from_port | ||
| + | to_port | ||
| + | protocol | ||
| + | cidr_blocks = [" | ||
| + | } | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | resource " | ||
| + | name = " | ||
| + | vpc_id = aws_vpc.main.id | ||
| + | |||
| + | ingress { | ||
| + | description | ||
| + | from_port | ||
| + | to_port | ||
| + | protocol | ||
| + | security_groups = [aws_security_group.backend_sg.id] | ||
| + | } | ||
| + | |||
| + | egress { | ||
| + | from_port | ||
| + | to_port | ||
| + | protocol | ||
| + | cidr_blocks = [" | ||
| + | } | ||
| + | |||
| + | tags = { | ||
| + | Name = " | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| <WRAP round todo> | <WRAP round todo> | ||
| + | Appliquer les modifications : | ||
| - | Corriger l’infrastructure pour : | + | terraform apply |
| - | * restaurer l’accès web | + | Tester |
| - | * conserver | + | |
| + | # Via l'ALB – doit fonctionner | ||
| + | curl http://< | ||
| + | |||
| + | # Direct – doit échouer | ||
| + | curl http://< | ||
| </ | </ | ||
| - | ===== 8. Extension ===== | + | <WRAP round question> |
| + | L' | ||
| - | <WRAP round todo> | + | L' |
| - | Ajouter : | + | Que se passe-t-il si le Health Check de l'ALB échoue ? |
| + | Comment vérifier l' | ||
| + | </ | ||
| - | * un NAT Gateway pour le subnet privé | + | ===== 7. Problème potentiel – Health Check ===== |
| - | * permettre au backend d’accéder à Internet sans être exposé | + | |
| + | Après modification des Security Groups, l'ALB peut afficher le backend comme " | ||
| + | |||
| + | <WRAP round question> | ||
| + | Expliquer ce qu'est un Health Check d'ALB. | ||
| + | |||
| + | Quel flux réseau le Health Check génère-t-il ? | ||
| + | |||
| + | Depuis quelle source arrive ce flux sur le backend ? | ||
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Pourquoi ne faut-il jamais exposer directement une base de données sur Internet | + | Si le backend est " |
| + | |||
| + | Le service devient indisponible même si le backend fonctionne. | ||
| + | |||
| + | Quelle règle dans le Security Group du backend pourrait causer ce blocage | ||
| </ | </ | ||
| - | ===== 9. Bonnes pratiques | + | <WRAP round todo> |
| + | Vérifier dans la console AWS : | ||
| + | * aller dans EC2 > Target Groups | ||
| + | * vérifier le statut de l' | ||
| + | * lire le message d' | ||
| + | </ | ||
| + | |||
| + | ===== 8. Diagnostic et correction ===== | ||
| + | |||
| + | <WRAP round todo> | ||
| + | Si le Health Check échoue, identifier la cause : | ||
| + | |||
| + | * le port 80 est-il bien ouvert depuis le Security Group de l'ALB ? | ||
| + | * le service python3 est-il démarré sur l' | ||
| + | * la règle egress de l'ALB permet-elle les réponses ? | ||
| + | |||
| + | Corriger la règle manquante et réappliquer. | ||
| + | </ | ||
| + | |||
| + | ===== 9. Vérification finale | ||
| + | |||
| + | <WRAP round todo> | ||
| + | Réaliser les tests suivants et noter les résultats dans un tableau : | ||
| + | |||
| + | * accès HTTP via l'ALB → attendu : 200 | ||
| + | * accès HTTP direct au backend → attendu : timeout ou refus | ||
| + | * connexion PostgreSQL depuis votre poste → attendu : timeout ou refus | ||
| + | * connexion PostgreSQL depuis le backend → attendu : possible | ||
| + | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Pourquoi faut-il privilégier : | + | Comment vérifier qu'un flux est bien bloqué et non juste lent ? |
| - | * plusieurs subnets ? | + | Quelle différence entre un timeout et un Connection refused en termes de Security |
| - | * des Security | + | |
| - | * une séparation public / privé | + | |
| </ | </ | ||
| - | ===== Challenge final ===== | + | ===== 10. Extension – déplacement du backend en subnet privé |
| - | Objectif : | + | Le backend a une IP publique car il est dans un subnet public. |
| - | * Le serveur web doit être accessible depuis Internet (HTTP) | + | Ce n'est pas nécessaire si l'ALB gère tout le trafic entrant. |
| - | * Le backend doit être inaccessible depuis Internet | + | |
| - | * Le backend doit être accessible uniquement depuis | + | |
| <WRAP round todo> | <WRAP round todo> | ||
| + | Modifier `instances.tf` pour déplacer le backend dans le subnet privé : | ||
| - | Implémenter cette architecture avec : | + | subnet_id = aws_subnet.private.id |
| - | * 2 Security Groups | + | Supprimer également le `map_public_ip_on_launch` implicite. |
| - | * règles de flux précises | + | |
| + | Appliquer et vérifier que l'ALB continue de fonctionner. | ||
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Pourquoi utiliser un Security Group comme source plutôt qu’une | + | Le backend n'a plus d'IP publique. |
| + | |||
| + | Est-il encore accessible depuis Internet ? | ||
| + | |||
| + | Comment l'ALB peut-il atteindre une instance dans un subnet privé ? | ||
| + | |||
| + | Quel composant réseau manque pour que le backend puisse faire des appels sortants (mises à jour, appels API externes) | ||
| </ | </ | ||
| - | ===== Bonus ===== | + | ===== Challenge final ===== |
| + | |||
| + | Objectif : valider l' | ||
| <WRAP round todo> | <WRAP round todo> | ||
| + | Dessiner l' | ||
| + | * les subnets (public_a, public_b, private) | ||
| + | * les composants dans chaque subnet | ||
| + | * les Security Groups et leurs règles | ||
| + | * les flux autorisés avec les ports | ||
| + | </ | ||
| - | Ajouter | + | <WRAP round todo> |
| + | Répondre aux questions suivantes par écrit | ||
| - | * un Load Balancer en frontal | + | * Quel composant est le seul point d' |
| - | * plusieurs instances web | + | * Quelles ressources ne sont plus accessibles directement depuis Internet ? |
| + | * Quel principe de sécurité est appliqué sur chaque Security Group ? | ||
| + | * Que se passe-t-il si l'ALB est supprimé ? L' | ||
| + | </ | ||
| + | |||
| + | ===== Bonus ===== | ||
| + | |||
| + | <WRAP round todo> | ||
| + | Ajouter une seconde instance backend dans `public_b` : | ||
| + | |||
| + | resource " | ||
| + | ami = " | ||
| + | instance_type | ||
| + | subnet_id | ||
| + | vpc_security_group_ids = [aws_security_group.backend_sg.id] | ||
| + | ... | ||
| + | } | ||
| + | Attacher cette instance au même Target Group. | ||
| </ | </ | ||
| <WRAP round question> | <WRAP round question> | ||
| - | Quel est l’impact sécurité | + | Quels sont les avantages |
| + | |||
| + | Répondre selon deux axes : | ||
| + | * disponibilité : que se passe-t-il si une instance | ||
| + | * sécurité : l' | ||
| </ | </ | ||