Aller au contenu principal

AWS : VPC

· 16 minutes de lecture

Un Virtual Private Cloud (VPC) est un réseau privé virtuel, logiquement isolé, dans lequel s'exécutent les ressources AWS d'un compte : instances EC2, bases RDS, nœuds Kubernetes. Sa conception (plan d'adressage, découpage en sous-réseaux, routage, filtrage) détermine ce qui est joignable depuis Internet, ce qui peut en sortir, et quelles ressources peuvent communiquer entre elles.

Chaque région d'un compte AWS dispose d'un VPC par défaut, avec un sous-réseau public par zone de disponibilité : une instance y est immédiatement joignable depuis Internet. Ce réglage convient aux essais, mais pas à une application de production, qui a besoin de séparer ce qui est exposé de ce qui ne doit pas l'être. La construction d'un VPC dédié suit une progression :

  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. Filtrer → refuser par défaut, autoriser explicitement
  5. Organiser en tiers → isoler web, application et base de données dans des couches distinctes

Le VPC et son bloc d'adresses​

Un VPC occupe une plage d'adresses IPv4 privées (RFC 1918) définie par un bloc CIDR (Classless Inter-Domain Routing), de taille comprise entre /16 et /28.

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

Le bloc CIDR principal ne peut plus être modifié après la création. Des blocs secondaires peuvent être ajoutés ensuite (cinq par défaut), mais le découpage initial conditionne toute l'évolution du réseau. Le point le plus contraignant n'est pas la taille mais le chevauchement : deux VPC dont les plages se recouvrent ne peuvent pas être reliés par peering ou Transit Gateway, et un VPC qui recouvre le réseau d'un site distant ne peut pas y être connecté par VPN. Un plan d'adressage attribue donc des plages disjointes à chaque VPC et à chaque site.

Les subnets et les zones de disponibilité​

Un VPC s'étend sur toutes les zones de disponibilité (AZ) de sa région ; un subnet, lui, appartient à une seule AZ. Répartir les ressources sur des subnets de plusieurs AZ permet à l'application de survivre à la panne d'une zone.

VPC 10.0.0.0/16 (région eu-west-3)
├─ Subnet public 3a : 10.0.1.0/24 (zone eu-west-3a)
├─ Subnet privé 3a : 10.0.2.0/24 (zone eu-west-3a)
├─ Subnet public 3b : 10.0.3.0/24 (zone eu-west-3b)
└─ Subnet privé 3b : 10.0.4.0/24 (zone eu-west-3b)

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

Public ou privé : la distinction ne tient pas au subnet lui-même mais à sa table de routage (section suivante) :

  • Public : sa table contient une route vers une Internet Gateway ; ses instances dotées d'une IP publique sont joignables depuis Internet.
  • Privé : aucune route vers une Internet Gateway ; ses instances ne sont pas joignables depuis Internet, mais peuvent initier des connexions sortantes via une NAT Gateway.

AWS réserve 5 adresses dans chaque subnet :

Subnet 10.0.1.0/24 → 256 adresses
├─ 10.0.1.0 : adresse réseau
├─ 10.0.1.1 : routeur du VPC
├─ 10.0.1.2 : serveur DNS fourni par AWS
├─ 10.0.1.3 : réservée pour un usage futur
├─ 10.0.1.4-254 : utilisables (251)
└─ 10.0.1.255 : adresse de broadcast (le broadcast n'est pas supporté dans un VPC)

Router le trafic : tables de routage et passerelles​

Une fois le VPC découpé en subnets répartis sur plusieurs AZ, il reste à déterminer le chemin de chaque paquet : vers une autre instance du VPC, vers Internet, vers un service AWS.

Tables de routage​

Chaque subnet est associé à une table de routage (la table principale du VPC à défaut d'association explicite). Pour chaque paquet, le routeur du VPC retient la route la plus spécifique (préfixe le plus long) qui contient l'adresse de destination.

Pour un paquet à destination de 8.8.8.8 émis depuis un subnet public, aucune route plus spécifique ne correspond : la route par défaut 0.0.0.0/0 → igw-... s'applique et le paquet part vers l'Internet Gateway.

Exemple : table d'un subnet public

DestinationTargetSignification
10.0.0.0/16localTrafic vers les autres subnets du VPC : reste local
0.0.0.0/0igw-abcTrafic vers Internet (route par défaut) : via l'IGW

Exemple : table d'un subnet privé

DestinationTargetSignification
10.0.0.0/16localTrafic vers les autres subnets du VPC : reste local
0.0.0.0/0nat-xyzTrafic vers Internet (route par défaut) : via la NAT Gateway

La route local est créée automatiquement et ne peut pas être supprimée : tous les subnets d'un VPC peuvent toujours se joindre au niveau du routage, le filtrage relevant des Security Groups et des NACL. Le préfixe 0.0.0.0/0 englobe toute adresse non couverte par une route plus spécifique : c'est la route par défaut.

Deux subnets d'une même AZ peuvent ne différer que par leur table de routage : l'un est public, l'autre privé.

Internet Gateway (IGW)​

Les adresses 10.x.x.x ne sont pas routables sur Internet. Une instance d'un subnet public possède une adresse privée sur son interface réseau et, en plus, une adresse IPv4 publique qui n'apparaît pas dans le système d'exploitation. L'Internet Gateway effectue la traduction un pour un entre les deux :

Trafic entrant
├─ Client Internet : paquet vers 54.1.2.3:443
├─ IGW : 54.1.2.3 correspond à l'instance 10.0.1.5, réécriture de la destination
└─ Instance EC2 : reçoit un paquet destiné à 10.0.1.5

Trafic sortant
├─ Instance EC2 : paquet de 10.0.1.5 vers 8.8.8.8
├─ Table de routage : 0.0.0.0/0 → igw
├─ IGW : réécriture de la source 10.0.1.5 en 54.1.2.3
└─ Internet reçoit un paquet émis par 54.1.2.3

Configuration minimale :

  1. Créer l'IGW
  2. L'attacher au VPC (une IGW par VPC)
  3. Ajouter la route 0.0.0.0/0 → IGW dans la table du subnet public

Sans IGW attachée et routée, même une instance dotée d'une IP publique ne peut pas communiquer avec Internet. L'IGW est un composant managé, redondant et sans limite de bande passante ; elle n'est pas facturée en tant que telle.

NAT Gateway​

Une instance d'un subnet privé a souvent besoin de connexions sortantes : installation de paquets (apt update, npm install), appels à des API externes, téléchargement d'images de conteneurs. Elle n'a pas d'IP publique et doit rester injoignable depuis Internet. Une route 0.0.0.0/0 vers l'IGW ne servirait à rien : sans adresse publique, l'IGW n'a aucune traduction à appliquer.

La NAT Gateway, placée dans un subnet public, sert d'intermédiaire : elle remplace l'adresse source des paquets sortants par sa propre adresse publique et garde en mémoire la correspondance pour réacheminer les réponses. Aucune connexion ne peut être initiée depuis Internet vers l'instance privée.

Flux détaillé
└─ Instance privée 10.0.2.50 : téléchargement depuis le registre npm
└─ Paquet : SRC=10.0.2.50, DST=104.16.0.35
└─ Table de routage privée : 0.0.0.0/0 → nat-gateway
└─ NAT Gateway 10.0.1.100 (Elastic IP 54.1.2.3) reçoit
└─ Remplace SRC : SRC=10.0.1.100, DST=104.16.0.35, puis l'IGW traduit en 54.1.2.3
└─ Réponse : SRC=104.16.0.35, DST=54.1.2.3
└─ La NAT retrouve la connexion dans sa table
└─ Remplace DST : SRC=104.16.0.35, DST=10.0.2.50
└─ Routage vers l'instance privée

Vu d'Internet, seule l'adresse 54.1.2.3 apparaît ; l'instance 10.0.2.50 reste invisible.

Configuration :

  1. Allouer une Elastic IP (adresse publique fixe, facturée comme toute IPv4 publique depuis février 2024, environ 3,6 USD par mois)
  2. Créer la NAT Gateway dans un subnet public, avec cette Elastic IP
  3. Ajouter la route 0.0.0.0/0 → NAT Gateway dans la table de routage privée

Coût et disponibilité : une NAT Gateway coûte environ 0,045 USD par heure (environ 33 USD par mois) plus 0,045 USD par Go traité, en plus du transfert sortant. Elle est rattachée à une seule AZ : une architecture résiliente en déploie une par AZ, chaque subnet privé routant vers celle de sa zone, ce qui évite aussi les frais de trafic inter-AZ. Une NAT Instance (instance EC2 configurée en routeur NAT) réduit le coût pour un environnement de test, au prix de la maintenance et de la disponibilité.

Une partie de ce trafic peut éviter la NAT : les VPC endpoints donnent accès aux services AWS sans passer par Internet. Les gateway endpoints pour S3 et DynamoDB sont gratuits et s'ajoutent comme une route dans la table ; les interface endpoints (PrivateLink) placent une interface réseau du service (ECR, Secrets Manager, STS...) directement dans le subnet, moyennant un coût horaire.

Filtrer : pare-feu en couches​

Le routage détermine les chemins possibles, pas les flux autorisés : au sein du VPC, toutes les instances peuvent se joindre. Une instance web doit recevoir du HTTP/HTTPS, mais pas de SSH depuis Internet ; une instance applicative doit joindre la base de données, mais pas l'inverse. Deux mécanismes de filtrage complémentaires appliquent ces règles.

Security Groups : pare-feu par interface réseau​

Un Security Group est un pare-feu stateful appliqué à chaque interface réseau (ENI) : instance EC2, nœud Kubernetes, instance RDS, load balancer. Chaque ENI est associée à un ou plusieurs Security Groups, dont les règles s'additionnent.

Logique :

  • Trafic entrant : refusé par défaut. Seules les règles explicites l'autorisent.
  • Trafic sortant : tout autorisé à la création d'un Security Group ; peut être restreint.
  • Autorisations uniquement : un Security Group ne contient pas de règle de refus.
  • Stateful : si une connexion est autorisée en entrée (ex. TCP 443), les paquets de réponse sont automatiquement autorisés en sortie, et inversement.

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.10/32 (SSH depuis l'adresse du bureau uniquement)

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

Référencer d'autres Security Groups : une règle peut autoriser le trafic provenant de toute interface membre d'un autre Security Group, plutôt que d'une liste d'adresses :

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

Quand des instances rejoignent ou quittent WEB-SG (par exemple au gré d'un groupe d'autoscaling), les règles de APP s'appliquent automatiquement, sans modification d'adresses.

Network ACLs : pare-feu par subnet​

Une Network ACL (NACL) est un pare-feu stateless appliqué à la frontière d'un subnet : chaque paquet qui entre dans le subnet ou en sort est évalué, indépendamment des paquets précédents. Les règles sont numérotées (100, 110, 120...) et évaluées par ordre croissant ; la première règle correspondante s'applique, qu'elle autorise ou refuse.

Différences avec les Security Groups :

  • Stateless : autoriser une connexion entrante n'autorise pas sa réponse. Le trafic de retour doit être autorisé explicitement en sortie, vers les ports éphémères du client (1024-65535).
  • Niveau : protège tout le subnet, pas une interface.
  • Refus explicites : une NACL peut contenir des règles DENY, ce qu'un Security Group ne permet pas.
  • Valeur par défaut : la NACL par défaut d'un VPC autorise tout ; une NACL personnalisée refuse tout tant qu'aucune règle n'est ajoutée.
NACL du subnet public (extrait)
Inbound:
├─ 90 DENY all from 198.51.100.0/24 (plage à bloquer, évaluée en premier)
├─ 100 ALLOW TCP 443 from 0.0.0.0/0
├─ 110 ALLOW TCP 1024-65535 from 0.0.0.0/0 (réponses aux connexions sortantes)
└─ * DENY all (règle finale implicite)
Outbound:
├─ 100 ALLOW TCP 1024-65535 to 0.0.0.0/0 (réponses aux clients HTTPS)
├─ 110 ALLOW TCP 443 to 0.0.0.0/0 (connexions sortantes)
└─ * DENY all

Comparaison résumée :

AspectSecurity GroupNACL
NiveauInterface réseauSubnet
ÉvaluationStatefulStateless
RèglesAutorisation uniquementAutorisation et refus, ordonnées
Entrant par défautRefuséAutorisé (NACL par défaut) / refusé (NACL personnalisée)
Sortant par défautAutoriséAutorisé (NACL par défaut) / refusé (NACL personnalisée)
UsageContrôle fin par serviceBlocage de plages d'adresses

Répartition des rôles :

  • Security Groups : le mécanisme principal, présent sur chaque interface. Il définit quel service accède à quel port.
  • NACL : un filet de sécurité grossier, par exemple pour bloquer une plage d'adresses entière ou isoler un subnet sensible, en complément des Security Groups.

Mise en place avec Terraform​

La construction suit toujours le même ordre : VPC → IGW → subnets (au moins deux AZ) → tables de routage et associations → NAT → Security Groups. Exemple réduit à une AZ, avec Terraform :

resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true # noms DNS publics pour les instances dotées d'une IP publique
tags = { Name = "prod" }
}

resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
}

resource "aws_subnet" "public_a" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "eu-west-3a"
map_public_ip_on_launch = true
tags = { Name = "prod-public-3a" }
}

resource "aws_subnet" "private_a" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
availability_zone = "eu-west-3a"
tags = { Name = "prod-private-3a" }
}

resource "aws_eip" "nat_a" {
domain = "vpc"
}

resource "aws_nat_gateway" "a" {
allocation_id = aws_eip.nat_a.id
subnet_id = aws_subnet.public_a.id # la NAT vit dans le subnet public
depends_on = [aws_internet_gateway.igw]
}

resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
}

resource "aws_route_table" "private_a" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.a.id
}
}

resource "aws_route_table_association" "public_a" {
subnet_id = aws_subnet.public_a.id
route_table_id = aws_route_table.public.id
}

resource "aws_route_table_association" "private_a" {
subnet_id = aws_subnet.private_a.id
route_table_id = aws_route_table.private_a.id
}

La route local n'apparaît pas : AWS la crée d'office dans chaque table. En pratique, le module communautaire terraform-aws-modules/vpc/aws génère cet ensemble pour plusieurs AZ à partir de quelques variables.

Architecture en tiers​

La distinction public/privé ne suffit pas à une application composée d'un point d'entrée web, d'un service applicatif et d'une base de données. Les trois ont des besoins d'exposition différents :

  • Point d'entrée web (load balancer) : reçoit le trafic Internet, c'est la couche la plus exposée
  • Serveur applicatif : traite les requêtes, interroge la base, n'est jamais exposé directement
  • Base de données : stocke les données, n'est joignable que depuis la couche applicative

Le VPC se découpe alors en trois tiers, chacun avec ses subnets (un par AZ), ses Security Groups et ses règles de routage.

Internet
↓
┌──────────────────────────────┐
│ Public Tier (3a/3b) │ ALB, bastion
│ 10.0.1.0/24, 10.0.2.0/24 │ IP publiques, trafic entrant Internet
└──────────────┬───────────────┘
↓ (1)
┌──────────────────────────────┐
│ App Tier (3a/3b) │ Instances applicatives, workers
│ 10.0.3.0/24, 10.0.4.0/24 │ Pas d'IP publique, sortie via NAT
└──────────────┬───────────────┘
↓ (2)
┌──────────────────────────────┐
│ DB Tier (3a/3b) │ RDS, ElastiCache
│ 10.0.5.0/24, 10.0.6.0/24 │ Pas d'IP publique, pas de route vers Internet
└──────────────────────────────┘

Flux de communication :

  1. Public tier → App tier : l'ALB transmet les requêtes au port applicatif ; le bastion ouvre des sessions SSH vers les instances applicatives.
  2. App tier → DB tier : connexions MySQL/PostgreSQL uniquement.
  3. DB tier → extérieur : aucun flux. Les sauvegardes RDS passent par le plan de contrôle d'AWS et non par le réseau du VPC.

Chaque tier ne communique qu'avec son voisin immédiat.

Exemple : Security Groups d'une architecture 3-tier​

Tier 1 : load balancer (alb-sg)

Inbound:
├─ TCP 80 from 0.0.0.0/0 (HTTP depuis Internet, redirigé vers HTTPS)
└─ TCP 443 from 0.0.0.0/0 (HTTPS depuis Internet)

Outbound:
└─ TCP 8000 to app-sg (vers le port applicatif uniquement)

Tier 2 : application (app-sg)

Inbound:
├─ TCP 8000 from alb-sg (port applicatif, depuis le load balancer uniquement)
└─ TCP 22 from bastion-sg (SSH depuis le bastion, jamais directement depuis Internet)

Outbound:
├─ TCP 5432 to db-sg (PostgreSQL vers le tier base de données)
└─ TCP 443 to 0.0.0.0/0 (API AWS, dépôts de paquets, via la NAT)

Tier 3 : base de données (db-sg)

Inbound:
└─ TCP 5432 from app-sg (PostgreSQL depuis le tier applicatif uniquement)

Outbound:
└─ (aucune règle nécessaire) (les réponses sont autorisées par le suivi d'état)

Les requêtes DNS vers le résolveur du VPC (adresse .2) ne sont pas filtrées par les Security Groups et n'ont pas besoin de règle.

Résultat : une requête arrivant d'Internet ne peut atteindre la base qu'en traversant successivement le load balancer et le service applicatif. La compromission du point d'entrée ne donne pas d'accès réseau direct aux données : c'est le principe de défense en profondeur appliqué au réseau.

Diagnostiquer​

Démarche lorsqu'une instance ne joint pas une ressource :

  1. Résolution DNS : le nom se résout-il vers l'adresse attendue (dig, getent hosts) ? Un échec à ce stade précède tout problème réseau.
  2. Table de routage du subnet source : existe-t-il une route vers la destination (aws ec2 describe-route-tables) ? Pour Internet, une IGW (subnet public) ou une NAT (subnet privé) ?
  3. Security Groups : règle sortante côté source, règle entrante côté destination.
  4. NACL des deux subnets, dans les deux sens, y compris les ports éphémères de retour.
  5. Service : le processus écoute-t-il sur la bonne interface et le bon port (ss -tlnp sur la destination) ?

Outils : les VPC Flow Logs (vers CloudWatch Logs ou S3) enregistrent les flux acceptés et rejetés par interface ; un flux REJECT identifie un filtrage par Security Group ou NACL. Reachability Analyzer calcule, sans envoyer de trafic, le chemin entre une source et une destination et désigne le composant bloquant. Depuis une instance, nc -zv <hôte> <port> teste l'ouverture d'un port TCP ; ping est souvent trompeur, l'ICMP n'étant pas autorisé par défaut dans les Security Groups.

Recommandations de conception​

  • Multi-AZ dès le départ : au moins deux AZ pour chaque tier ; ajouter une AZ après coup impose de redécouper l'adressage.
  • Plan d'adressage : un /16 par VPC laisse de la marge ; les plages doivent être disjointes entre VPC et avec les réseaux d'entreprise.
  • Nommage cohérent : prod-public-3a plutôt que subnet-1.
  • Moindre privilège réseau : pas de 0.0.0.0/0 en entrée hors du load balancer ; références entre Security Groups plutôt qu'adresses.
  • VPC Flow Logs : activés au moins sur les subnets exposés, pour l'audit et le diagnostic ; leur coût dépend du volume de logs ingérés.
  • CloudTrail : trace les modifications de VPC, routes et Security Groups.
  • Coût de la NAT : environ 33 USD par mois et par AZ, plus le volume traité ; les VPC endpoints réduisent la facture pour le trafic vers S3, DynamoDB et ECR.
  • Transfert de données : le trafic entre AZ (environ 0,01 USD par Go dans chaque sens) et vers Internet est facturé ; CloudFront réduit le second.

Cas avancés​

VPC Peering : connexion privée entre deux VPC (même compte ou non, même région ou non), par exemple pour relier des environnements. Les plages CIDR ne doivent pas se chevaucher, chaque côté doit ajouter des routes et des règles de Security Group, et le peering n'est pas transitif : A relié à B et B relié à C ne permet pas à A de joindre C.

Site-to-Site VPN : tunnels IPsec entre un datacenter et le VPC. Nécessite une Customer Gateway (l'équipement côté site) et une Virtual Private Gateway ou une Transit Gateway côté AWS.

Transit Gateway : routeur régional managé auquel se rattachent VPC et connexions VPN. Relier N réseaux en maillage complet demande N×(N-1)/2 peerings ; avec une Transit Gateway, N attachements suffisent, et le routage entre eux se contrôle par des tables de routage centralisées.

Ce VPC sert de socle réseau au cluster décrit dans l'article EKS.

Application / Projet lié​

Mis en pratique dans le projet2026TaskHorizonVPC dédié provisionné par Terraform en eu-west-3, avec deux sous-réseaux publics et deux privés sur deux zones de disponibilité, tags kubernetes.io/role/elb et kubernetes.io/role/internal-elb, et NAT Gateway unique pour la sortie des nœuds privés.FastAPIReactPostgreSQLTerraformHelmAWS EKSGitHub Actions