Skip to main content

AWS : VPC

· 11 min read

Un Virtual Private Cloud (VPC) résout un problème simple : isoler un ensemble de ressources AWS dans un réseau privé.

Sans VPC, toutes les ressources d'un compte AWS partagent le même réseau - pas d'isolation, pas de contrôle. Avec un VPC, chaque application obtient son propre espace réseau, contrôlé de A à Z.

La progression logique est :

  1. Créer un VPC → Bloc d'adressage isolé
  2. Diviser en subnets → Résilience multi-AZ et isolation logique
  3. Router le trafic → Contrôler où va chaque paquet (Internet, NAT, local)
  4. Sécuriser avec pare-feu → Refuser par défaut, autoriser explicitement
  5. Architecturer en tiers → Isoler web/app/db en couches distinctes

Commencer simple : le VPC

Un VPC occupe une plage d'adresses IPv4 définie par un bloc CIDR (Classless Inter-Domain Routing).

VPC "prod"   : 10.0.0.0/16
├─ Toutes les adresses de 10.0.0.0 à 10.0.255.255 sont disponibles
└─ Isolé des autres VPCs du compte
Bloc CIDRAdressesUsage
/1665 536Production multi-projets
/204 096Projets moyens
/24256Dev/tests

Point critique : Le bloc CIDR est immuable après création. Mal choisi = migration coûteuse plus tard.

Diviser pour la résilience : les subnets

Un VPC seul c'est un seul bloc d'adresses dans un seul endroit physique. Problème : si cette zone de disponibilité tombe, tout tombe.

Solution : diviser le VPC en subnets, chacun dans une AZ différente. AWS peut alors distribuer les ressources.

VPC 10.0.0.0/16
├─ Subnet public 1a : 10.0.1.0/24 (zone us-east-1a)
├─ Subnet privé 1a : 10.0.2.0/24 (zone us-east-1a)
├─ Subnet public 1b : 10.0.3.0/24 (zone us-east-1b)
└─ Subnet privé 1b : 10.0.4.0/24 (zone us-east-1b)

Chaque subnet est une subdivision du VPC avec son propre bloc CIDR contenu dans le parent.

Public vs Privé : La visibilité Internet est déterminée par la table de routage (voir section suivante). Pour l'instant, comprendre juste que :

  • Public : Accessible depuis Internet (instances peuvent être jointes)
  • Privé : Pas d'accès direct depuis Internet (instances peuvent initier des sorties)

AWS réserve 5 adresses par subnet qu'on ne peut pas utiliser :

Subnet 10.0.1.0/24256 adresses
├─ 10.0.1.0 : réseau
├─ 10.0.1.1 : passerelle
├─ 10.0.1.2 : DNS AWS
├─ 10.0.1.3 : réservée
├─ 10.0.1.4-254 : utilisables (251)
└─ 10.0.1.255 : broadcast

Router le trafic : tables de route et gateways

Problème : Maintenant qu'on a plusieurs subnets dispersés en AZs différentes, comment on contrôle où va le trafic ? Une instance veut parler à une autre instance, faire une requête API Internet, accéder une base de données. Comment le réseau sait-il où diriger le paquet ?

Solution : Les tables de routage.

Tables de routage - Le coordinateur du trafic

Chaque subnet est associé à une table de routage qui décide où diriger chaque paquet basé sur son adresse de destination.

Question simple : Une instance dans subnet1 envoie un paquet à 8.8.8.8. Où va-t-il ?

Réponse : La table de routage du subnet1 cherche la route la plus spécifique qui match 8.8.8.8. S'il trouve 0.0.0.0/0 → igw-xxxxx, le paquet part à l'Internet Gateway.

Exemple : Table pour subnet public

DestinationTargetSignification
10.0.0.0/16localTrafic pour autres subnets du VPC : reste local
0.0.0.0/0igw-abcTrafic pour Internet (default route) : via IGW

Exemple : Table pour subnet privé

DestinationTargetSignification
10.0.0.0/16localTrafic pour autres subnets du VPC : reste local
0.0.0.0/0nat-xyzTrafic pour Internet (default route) : via NAT

Le prefixe 0.0.0.0/0 représente "tout ce qui n'est pas explicitement listé". C'est la route par défaut (default route).

En pratique : tu crées une route table, tu l'associes à des subnets, tu y ajoutes des routes. Deux subnets dans la même AZ peuvent différer juste par leurs routes → l'un devient public, l'autre privé.

Internet Gateway (IGW) - Accès bidirectionnel depuis Internet

Problème : Une instance EC2 dans le subnet public a une adresse IP publique assignée. Elle peut envoyer du trafic vers Internet (grâce à la route). Mais comment reçoit-elle le trafic depuis Internet ? Comment un client externe contacte-t-il cette instance ?

Seul problème : les adresses 10.x.x.x ne sont pas routable sur Internet. Internet ne sait pas comment rediriger vers 10.0.1.5.

Solution : L'IGW traduit entre les adresses publiques (visibles d'Internet) et les adresses privées (10.x.x.x).

Trafic entrant
├─ Client Internet : "Je veux joindre 54.1.2.3:443"
├─ IGW : "54.1.2.3 = instance 10.0.1.5"
└─ Instance EC2 : reçoit le paquet

Trafic sortant
├─ Instance EC2 : "Je veux parler à 8.8.8.8"
├─ Route : "0.0.0.0/0 → igw"
├─ IGW : "Je remplace 10.0.1.5 par 54.1.2.3"
└─ Internet reçoit depuis 54.1.2.3

Configuration minimale :

  1. Créer IGW
  2. Attacher au VPC
  3. Ajouter route 0.0.0.0/0 → IGW dans la table du subnet public

Sans IGW attaché, même une instance avec IP publique ne peut pas communiquer avec Internet.

NAT Gateway - Trafic sortant sécurisé du privé

Problème : Une instance dans le subnet privé doit faire des appels sortants :

  • npm install (télécharge dépendances)
  • apt update (met à jour paquets système)
  • Appels API vers services externes

Mais elle n'a pas d'IP publique, n'est pas directement accessible depuis Internet, et on veut absolument qu'elle le reste.

Comment faire ? La route 0.0.0.0/0 → local ne suffira pas, elle ne connaît pas Internet.

Solution : NAT Gateway. Agit comme intermédiaire : l'instance privée parle au NAT, le NAT parle à Internet en se faisant passer pour l'instance.

Flux détaillé
└─ Instance privée 10.0.2.50 : "Je veux télécharger npm du registre"
└─ Paquet : SRC=10.0.2.50, DST=8.8.8.8
└─ Table de route privée : "0.0.0.0/0 → nat-gateway"
└─ NAT Gateway 10.0.1.100 (IP publique 54.1.2.3) reçoit
└─ Remplace SRC : SRC=54.1.2.3, DST=8.8.8.8
└─ Envoie vers Internet
└─ Réponse en retour : SRC=8.8.8.8, DST=54.1.2.3
└─ NAT le reçoit
└─ Remplace DST : SRC=8.8.8.8, DST=10.0.2.50
└─ Route vers instance privée

Du point de vue Internet : il y a une connexion sortante depuis 54.1.2.3. L'instance 10.0.2.50 est complètement invisible (remain hidden inside).

Configuration :

  1. Allouer une Elastic IP (IP publique fixe, ~0 USD si associée)
  2. Créer NAT Gateway dans un subnet public, affecter l'EIP
  3. Ajouter route 0.0.0.0/0 → NAT Gateway dans la table de route privée

Coût : ~32 USD/mois pour le NAT Gateway lui-même, + frais de transfert de données sortantes. Pour dev/test, considérer NAT Instance (EC2 configurée comme routeur) pour économiser, mais c'est moins robuste.

Sécuriser : pare-feu en couches

Problème : OK, on a routing, on a communication. Mais maintenant n'importe qui peut parler à n'importe quoi. Une instance web devrait recevoir du HTTP/HTTPS, mais pas SSH depuis Internet. Une instance app doit parler à la DB mais pas l'inverse. Comment on applique ces règles de communication ?

Réponse : Configuration réseau de sécurité sur deux couches.

Security Groups - Pare-feu par instance

Pare-feu stateful au niveau de l'instance EC2. Chaque instance est associée à un ou plusieurs Security Groups qui énumèrent explicitement qui peut parler à qui, et via quel port.

Logique :

  • Trafic entrant : Refusé par défaut. Seules les règles explicites l'autorisent ("deny by default").
  • Trafic sortant : Autorisé par défaut. Peut être restreint si besoin.
  • Stateful : Si une connexion est autorisée entrant (ex. TCP 443), la réponse sortante est automatiquement permise.

Exemple : Security Group pour serveur web

Inbound Rules:
├─ TCP 80 from 0.0.0.0/0 (HTTP depuis n'importe où)
├─ TCP 443 from 0.0.0.0/0 (HTTPS depuis n'importe où)
└─ TCP 22 from 203.0.113.0/32 (SSH seulement depuis office)

Outbound Rules:
└─ All protocols to 0.0.0.0/0 (Tout sortant autorisé)

Référencer d'autres SGs : Au lieu de lister des IPs brutes, on peut autoriser "trafic venant d'un autre SG". Exemple :

Security Group APP:
├─ Inbound :
│ ├─ TCP 5000 from WEB-SG (traffic from web tier)
│ └─ TCP 22 from ADMIN-SG (SSH from bastion)
└─ Outbound:
└─ All to 0.0.0.0/0

Ça c'est flexible : si tu ajoutes/retires des instances au SG web, les règles app s'adaptent automatiquement.

Network ACLs - Pare-feu par subnet

Pare-feu stateless au niveau du subnet entier. Chaque subnet peut avoir une NACL qui évalue toutent le trafic entrant et sortant selon des règles ordonnées par nombre (100, 110, 120...).

Contrairement à Security Groups :

  • Stateless : Une connexion autorisée entrant ne donne pas automatiquement la sortie. Faut traiter retour en sortie explicitement.
  • Niveau : Protège tout le subnet, pas une seule instance
  • Ordre : Les règles sont évaluées dans l'ordre; première match gagne.

Comparaison résumée :

AspectSecurity GroupNACL
NiveauInstanceSubnet
ÉvaluationStatefulStateless
Default InboundDenyAllow
Default OutboundAllowAllow
ComplexitéSimpleModérée
UsageFin par serviceBulk blocking

Quand les utiliser :

  • SGs : Toujours, obligatoire par instance. Configure qui accède à quel service.
  • NACLs : Rarement. Sauf si tu veux bloquer un range IP entier (ex. une pays entière) ou appliquer une règle au niveau subnet.

Pour du contrôle applicatif (port, protocole par instance), utiliser Security Groups. Pour du filtering réseau brutal (bloquer toute une CIDR), utiliser NACLs.

Mise en place (CLI/Terraform)

Console AWS ou Terraform. Étapes essentielles : VPC (créer avec /16) → IGW (créer + attacher) → Subnets (2+ AZs, public/privé chacun) → Routes (0.0.0.0/0 → IGW pour public, → NAT pour privé) → Security Groups.

Architecturer en tiers : séparation des responsabilités

Problème : Jusque là on a parlé de subnets publics vs privés. Mais dans une vraie app (web, API, DB), il y a trois besoins distincts :

  • Serveur web : reçoit traffic Internet, le plus exposé
  • Serveur applicatif : traite requêtes, parle à la DB, n'est pas directement exposé
  • Base de données : stocke les données, n'est joignable que depuis app, jamais depuis le web

Un subnet ne suffit pas, il faut une stratégie d'isolation par couche.

Solution : Découper le VPC en trois tiers, chacun avec ses propres subnets, règles SG, et responsabilités.

Internet

┌─────────────────────┐
│ Public Tier (1a/1b) │ ALB, Bastion
10.0.1.0/24, 10.0.2.0/24 │ IP publiques, accepte trafic entrant Internet
└──────────┬──────────┘
(1)
┌─────────────────────┐
│ App Tier (1a/1b) │ Instances app, workers
10.0.3.0/24, 10.0.4.0/24 │ Pas d'IP publiques, reçoit du public tier
└──────────┬──────────┘
(2)
┌─────────────────────┐
│ DB Tier (1a/1b) │ RDS, ElastiCache
10.0.5.0/24, 10.0.6.0/24 │ Aucune IP publique, reçoit du app tier
└─────────────────────┘

Flux de communication :

  1. Public tier → App tier : ALB fait arrêter au port applicatif. Bastion fait SSH sur app.
  2. App tier → DB tier : Requêtes MySQL/PostgreSQL uniquement
  3. DB tier → extérieur : Rien (ou NAT pour backups/logs)

Chaque tier oublie ce qui existe au-delà du voisin immédiat.

Exemple : Security Group workflow 3-tier

Tier 1 : Public (web-sg)

Inbound:
├─ TCP 80 from 0.0.0.0/0 (HTTP from Internet)
├─ TCP 443 from 0.0.0.0/0 (HTTPS from Internet)
└─ TCP 22 from admin-office-ip (SSH for emergency)

Outbound:
└─ All to 0.0.0.0/0 (Can reach anywhere)

Tier 2 : App (app-sg)

Inbound:
├─ TCP 8000-8080 from web-sg (App port only from web tier)
└─ TCP 22 from bastion-sg (SSH from bastion, not directly from Internet)

Outbound:
├─ TCP 3306/5432 to db-sg (MySQL/PostgreSQL to DB tier)
└─ TCP/UDP 53 to 0.0.0.0/0 (DNS queries outbound)

Tier 3 : Database (db-sg)

Inbound:
├─ TCP 3306 from app-sg (MySQL from app tier only)
└─ TCP 5432 from app-sg (PostgreSQL from app tier only)

Outbound:
└─ None, or TCP 443 to 0.0.0.0/0 (HTTPS for backups/export only if needed)

Résultat : Une instance web ne peut JAMAIS parler directement à la DB. Elle doit passer par app. C'est du Zero Trust en action.

Diagnostiquer : troubleshooting

Checklist réseau : Instancene peut pas joindre une ressource ?

  1. Route table : la destination est-elle routée ? (aws ec2 describe-route-tables)
  2. Security Group : la règle inbound/outbound existe ? (VPC console → SGs)
  3. NACL : la règle subnet-level existe ? (rare mais vérifier si SG OK)
  4. DNS : nslookup OK ? Si oui, problème au-dessus de layer 3

Outils AWS : VPC Flow Logs (CloudWatch Logs / S3) pour auditer tous les flux. Reachability Analyzer pour tester route source → destination et trouver l'obstacle. CLI : ping, telnet port, traceroute, ip route show.

Bonnes pratiques

  • Multi-AZ dès le jour 1 : Évite les crises si une zone tombe
  • CIDR /16 : Assez pour 10 ans (65k adresses)
  • Nommage cohérent : prod-public-1a, pas subnet-1
  • Zero Trust : Refuser par défaut, autoriser explicitement
  • SG restrictifs : Pas de 0.0.0.0/0 inbound sauf nécessaire
  • VPC Flow Logs : ~5 USD/mois CloudWatch, essentiel pour debuggage
  • CloudTrail : Audit des changements VPC/SG
  • NAT cost : ~32 USD/mois. Alternative : NAT Instance
  • Bande passante : Cross-AZ et sortant coûtent. CloudFront pour réduire

Cas avancés

VPC Peering : Connexion entre deux VPCs, utile pour multi-env (prod/staging/dev isolés). Requiert routes + SG autorisations croisées.

Site-to-Site VPN : Tunnel IPSec datacenter → VPC AWS. Requiert Customer Gateway (chez toi) + VPN Gateway (AWS).

Transit Gateway : Hub centralisé pour 3+ VPCs/sites. Réduit complexité peering (N peerings vs N² pour mesh).