Aller au contenu

VPN Site-to-Site

Le VPN Site-to-Site (S2S) Scaleway relie le VPC applicatif à deux sites du réseau ADEME — Angers et Paris — par des tunnels IPsec avec routage dynamique BGP. Il permet aux workloads Kubernetes de joindre des ressources internes de l'ADEME (et inversement) sans transiter par Internet en clair. Les deux environnements applicatifs, hors-prod et prod, ont chacun leurs propres gateways et tunnels vers les mêmes FortiGate ADEME.


Généralités — le VPN Site-to-Site chez Scaleway

Le service est entièrement managé par Scaleway : aucune VM ni appliance à maintenir. Il s'articule autour de quatre objets, tous créés dans le projet Scaleway de l'environnement et rattachés à la région fr-par.

Composant Ressource Terraform Rôle
VPN Gateway scaleway_s2s_vpn_gateway Extrémité Scaleway du tunnel. Attachée à un Private Network d'un VPC, elle porte une IP publique et un ASN Scaleway (12876). C'est une ressource zonale (fr-par-1 ou fr-par-2) et dimensionnée par un type commercial : VGW-S, VGW-M, VGW-L.
Customer Gateway scaleway_s2s_vpn_customer_gateway Description de l'extrémité distante : son IP publique et son ASN. Un objet par site distant.
Routing Policy scaleway_s2s_vpn_routing_policy Filtres de préfixes BGP : prefix_filter_in (ce que le site distant a le droit d'annoncer) et prefix_filter_out (ce que Scaleway annonce au site distant). Tout préfixe hors filtre est ignoré.
VPN Connection scaleway_s2s_vpn_connection Le tunnel lui-même : lie une VPN Gateway à une Customer Gateway, porte les suites cryptographiques (IKEv2 pour la phase 1, ESP pour la phase 2), la session BGP et la politique d'initiation.

Comment ça fonctionne

  • IPsec / IKEv2 — le tunnel est chiffré de bout en bout. La clé pré-partagée (PSK) est générée par Scaleway à la création de la connexion et stockée dans Secret Manager ; elle est à transmettre hors bande à l'administrateur du site distant.
  • BGP — pas de routes statiques : chaque connexion établit une session BGP sur un lien point-à-point en 169.254.x.x/30 (private_ip côté Scaleway, peer_private_ip côté client). Le site distant annonce ses réseaux, Scaleway annonce les siens, chacun filtré par la routing policy.
  • Propagation de routes — avec enable_route_propagation = true, les préfixes appris par BGP sont injectés dans la table de routage du VPC. Le VPC doit avoir enable_routing et enable_custom_routes_propagation activés : c'est ce qui rend les réseaux ADEME joignables depuis les autres Private Networks du VPC (dont celui du cluster Kubernetes), sans configuration sur les nœuds.
  • Politique d'initiation — vpn_gateway (Scaleway initie), customer_gateway (le site distant initie) ou non défini. Chez nous, c'est Scaleway qui initie.

Haute disponibilité

Une VPN Gateway étant zonale, la HA consiste à créer une gateway par zone (fr-par-1 et fr-par-2) et, pour chaque site distant, deux connexions — une par gateway — avec des liens BGP distincts. Le site distant voit donc deux tunnels et deux sessions BGP vers les mêmes préfixes ; la perte d'une zone fait basculer le trafic sur l'autre via BGP.

Filtrage : deux niveaux

Niveau Objet Question à laquelle il répond
Routage Routing policy (préfixes BGP) Quels réseaux sont joignables à travers le tunnel ?
Firewall VPC ACL (scaleway_vpc_acl) Quel trafic est autorisé entre ces réseaux (source, destination, protocole, port) ?

Les ACL VPC sont stateless : une règle aller ne suffit pas, le trafic retour doit être autorisé explicitement (voir Modifier les règles ACL).


Mise en œuvre

Topologie

Élément hors-prod prod
VPC vpc-app-horsprod vpc-app-prod
Private Network dédié au VPN vpn-s2s — 172.16.240.0/24 vpn-s2s — 172.16.242.0/24
Private Network annoncé au site distant app-k8s-internal — 172.16.0.0/22 app-k8s-internal — 172.16.16.0/22
VPN Gateways (type VGW-S) horsprod-par1 (fr-par-1), horsprod-par2 (fr-par-2) prod-par1 (fr-par-1), prod-par2 (fr-par-2)
Initiation vpn_gateway vpn_gateway
Propagation de routes activée activée
Politique par défaut de l'ACL VPC drop drop

Dans chaque environnement, le VPN est raccordé à un Private Network dédié (vpn-s2s) plutôt qu'au PN du cluster : la gateway y prend son IP privée, et la propagation de routes se charge de rendre les préfixes ADEME visibles depuis app-k8s-internal.

Sites distants

Les deux environnements se raccordent aux mêmes FortiGate ADEME, avec les mêmes ASN et les mêmes préfixes acceptés ; seul le préfixe annoncé change (le PN du cluster de l'environnement).

Site vpn_name IP publique ASN Préfixes acceptés (prefix_filter_in) Préfixes annoncés (prefix_filter_out) HA
ADEME Angers horsprod-angers 217.108.11.55 65049 10.1.0.0/16, 192.168.21.0/24, 10.255.3.0/24 172.16.0.0/22 oui
ADEME Paris horsprod-paris 194.206.70.149 65092 10.2.0.0/16, 10.255.3.0/24 172.16.0.0/22 oui
ADEME Angers prod-angers 217.108.11.55 65049 10.1.0.0/16, 192.168.21.0/24, 10.255.3.0/24 172.16.16.0/22 oui
ADEME Paris prod-paris 194.206.70.149 65092 10.2.0.0/16, 10.255.3.0/24 172.16.16.0/22 oui

10.255.3.0/24 (services DRI : VIP LDAP, bases DPE…) est joignable par les deux sites.

Chaque site a donc, par environnement, deux tunnels (<vpn_name>-par1 et <vpn_name>-par2) et deux sessions BGP — soit quatre tunnels par FortiGate. Les liens BGP /30 sont uniques sur l'ensemble des connexions :

Connexion Lien BGP Scaleway ↔ client
horsprod-angers-par1 169.254.0.1/30 ↔ 169.254.0.2/30
horsprod-angers-par2 169.254.1.1/30 ↔ 169.254.1.2/30
horsprod-paris-par1 169.254.2.1/30 ↔ 169.254.2.2/30
horsprod-paris-par2 169.254.2.5/30 ↔ 169.254.2.6/30
prod-angers-par1 169.254.3.1/30 ↔ 169.254.3.2/30
prod-angers-par2 169.254.4.1/30 ↔ 169.254.4.2/30
prod-paris-par1 169.254.5.1/30 ↔ 169.254.5.2/30
prod-paris-par2 169.254.5.5/30 ↔ 169.254.5.6/30

Les tunnels des deux environnements sont montés : les huit connexions sont établies (tunnel et sessions BGP up).

Suites cryptographiques

Identiques pour tous les tunnels, phase 1 (IKEv2) comme phase 2 (ESP) :

Paramètre Valeur
Chiffrement aes256gcm
Intégrité sha512
Groupe DH curve25519

Intégrité et AES-GCM

AES-GCM est un mode AEAD : l'intégrité est intrinsèque et le champ integrity devrait être null. Le provider Scaleway refuse cette valeur à ce jour, d'où le sha512 explicite dans la configuration (commentaire #should be null but bugged... dans le tfvars). À réévaluer lors des montées de version du provider.

Schéma

Les deux VPC applicatifs (hors-prod et prod) et leurs gateways (une par zone), les tunnels vers les FortiGate ADEME d'Angers et de Paris, et les sessions BGP associées.

Source : VPN-SCW-ADEME.drawio, modifiable via l'icône crayon de la barre d'outils.


Gestion Terraform

Chaîne de configuration

Tout est piloté par la configuration centralisée (scw-init-config/001.config_infrastructure/terraform.tfvars), publiée dans le state config/infrastructure.tfstate puis consommée par les runs de l'environnement.

Ordre Run Rôle
1 scw-init-config/001.config_infrastructure Déclare s2s_vpn_definition, le PN vpn_s2s dans vpc_definition.private_network.<env> et les règles vpc_firewall.<env>. À ré-appliquer après toute modification du tfvars.
2 scw-application/<env>/terraform/001.vpc_private_network Crée le VPC et ses Private Networks, dont vpn-s2s. Expose pn_vpn_s2s_id et vpc_id.
3 scw-application/<env>/terraform/003.s2s_vpn Instancie le module scw-tf-modules/scaleway_s2s_vpn : gateways, customer gateways, routing policies, connexions.
4 scw-application/<env>/terraform/004.vpc_firewall Crée l'ACL VPC à partir de vpc_firewall.<env>.
cd infrastructure/main-infra/scw-init-config/001.config_infrastructure && terraform apply
cd ../../scw-application/<env>/terraform/003.s2s_vpn && terraform apply   # <env> : horsprod ou prod
cd ../004.vpc_firewall && terraform apply

Les runs 003 et 004 s'authentifient auprès de Scaleway avec les credentials lus dans Vault (ademe/cluster-<env>/scaleway_credentials) : un VAULT_TOKEN valide est nécessaire.

Le module scaleway_s2s_vpn

Le module prend un préfixe de nom (le nom de l'environnement), un type de gateway, le PN cible et une liste de tunnels. Il en déduit :

  • une gateway <env>-par1, toujours ;
  • une gateway <env>-par2, uniquement si au moins un tunnel demande high_availability = true ;
  • par tunnel : une customer gateway et une routing policy nommées <vpn_name>, une connexion <vpn_name>-par1, et une connexion <vpn_name>-par2 si HA.

Les ressources sont indexées par vpn_name (for_each) : ajouter ou retirer un site ne recrée pas les autres.

Ne pas renommer un vpn_name

Le vpn_name est la clé des ressources dans le state. Le changer revient à détruire et recréer la customer gateway, la routing policy et les connexions du site — donc une nouvelle PSK et une coupure. Pour renommer sans coupure, passer par des blocs moved.

Référence d'un tunnel

Chaque entrée de s2s_vpn_definition.<env>.tunnels :

Champ Description
vpn_name Identifiant du site, sert de clé et de préfixe de nom. Convention : <env>-<site>.
high_availability true pour un second tunnel sur fr-par-2.
customer_public_ip IP publique de l'équipement distant.
customer_asn ASN du site distant (privé : 64512–65534).
routingpolicies.prefix_filter_in Préfixes que le site distant peut annoncer (ses réseaux).
routingpolicies.prefix_filter_out Préfixes annoncés au site distant (nos réseaux).
initiation_policy vpn_gateway ou customer_gateway.
enable_route_propagation Injection des routes BGP dans la table du VPC. Laisser à true.
bgp_config_ipv4 private_ip (Scaleway) et peer_private_ip (client), un /30 en 169.254.0.0/16, unique par connexion.
high_availability_bgp_config_ipv4 Idem pour la connexion fr-par-2. Ignoré si high_availability = false (mais doit être renseigné, le type l'exige).
esp_ciphers / ikev2_ciphers encryption, integrity, dh_group. À aligner avec l'équipement distant.

Ajouter un site distant

  1. Dans 001.config_infrastructure/terraform.tfvars, ajouter une entrée à s2s_vpn_definition.<env>.tunnels en choisissant des /30 BGP non encore utilisés (voir le tableau des liens BGP ci-dessus).
  2. Si le site expose de nouveaux réseaux, les ajouter à prefix_filter_in et prévoir les règles vpc_firewall correspondantes.
  3. Appliquer 001.config_infrastructure, puis 003.s2s_vpn, puis 004.vpc_firewall.
  4. Transmettre à l'administrateur du site : l'IP publique de chaque gateway, l'ASN Scaleway (12876), les IP BGP, les suites cryptographiques et la PSK (voir ci-dessous).

Récupérer les informations à transmettre au site distant

IP publique des gateways — elle n'est pas directement dans l'output : vpn_gateway_config_par1.public_config contient l'ID de l'IP IPAM, à résoudre via l'API :

cd scw-application/<env>/terraform/003.s2s_vpn
terraform output -json vpn_gateway_config_par1 | jq '.public_config'   # idem avec vpn_gateway_config_par2
curl -s -H "X-Auth-Token: $SCW_SECRET_KEY" \
  "https://api.scaleway.com/ipam/v1/regions/fr-par/ips/<ipam_ipv4_id>" | jq '.address'

PSK — générée par Scaleway et stockée dans Secret Manager. L'ID du secret est dans l'output des connexions :

terraform output -json vpn_connections_par1 | jq '."prod-angers" | {secret_id, secret_version}'   # idem pour chaque site, par1 et par2
scw secret version access-by-path secret-id=<secret_id> revision=<secret_version>

Elle est aussi consultable dans la console Scaleway, sur la page de la connexion.

État des tunnels — status, tunnel_status et bgp_status_ipv4 sont exposés sur chaque connexion :

terraform apply -refresh-only   # les outputs reflètent le state : le rafraîchir pour avoir l'état courant
terraform output -json vpn_connections_par1 | jq 'map_values({status, tunnel_status, bgp_status_ipv4})'
terraform output -json vpn_connections_par2 | jq 'map_values({status, tunnel_status, bgp_status_ipv4})'

Modifier les règles ACL (firewall VPC)

Où sont les règles

Dans scw-init-config/001.config_infrastructure/terraform.tfvars, variable vpc_firewall, une liste par environnement :

vpc_firewall = {
  horsprod = [
    { source = "172.16.0.0/22", dest = "10.1.12.108/32", proto = "TCP", port = "3306", comment = "SRV-ANG-BDD108" },
    { source = "172.16.0.0/22", dest = "10.255.3.1/32",  proto = "TCP", port = "636",  comment = "VIP Load balancing CDMs" },
    # ...
  ]
  prod = [
    { source = "172.16.16.0/22", dest = "10.255.3.1/32", proto = "TCP", port = "636", comment = "VIP Load balancing CDMs" },
    # ...
  ]
}
Champ Valeurs
source / dest CIDR
proto ANY, TCP, UDP, ICMP
port Port de destination : ANY, un port (443) ou une plage (8000-8080)
comment Optionnel. Devient la description de la règle dans la console Scaleway — typiquement le nom du serveur ou du service ADEME visé. Si absent, la description est générée : <source> -> <dest> (<proto>/<port>).

Pour un flux sortant, la source est le Private Network du cluster de l'environnement (172.16.0.0/22 en hors-prod, 172.16.16.0/22 en prod) ; la destination, une machine ou un service précis du réseau ADEME, de préférence en /32 et sur le seul port utile.

Ce que génère le run 004.vpc_firewall

Le run traduit chaque règle en règle scaleway_vpc_acl (action accept) et génère automatiquement la règle retour : les ACL VPC étant stateless, la réponse dest → source doit être autorisée elle aussi. Pour une règle A → B sur le port P, on obtient :

Sens Source Destination Port source Port destination
aller A B any P
retour (return: …) B A P any

La règle retour reprend la description de la règle aller, préfixée de return:. Il n'y a donc pas à écrire les règles retour dans le tfvars. Une seule ACL IPv4 est créée par VPC, avec toutes les règles ; si la liste est vide, aucune ACL n'est créée.

Politique par défaut : drop

La variable default_policy de 004.vpc_firewall fixe le sort du trafic ne correspondant à aucune règle. Elle est à drop dans les deux environnements : le filtrage est effectif, et tout nouveau flux entre le cluster et le réseau ADEME doit être déclaré dans vpc_firewall.<env> avant d'être utilisé.

L'ACL s'applique à tout le VPC : lors d'une modification, vérifier que le trafic intra-VPC nécessaire reste autorisé.

Procédure

  1. Éditer vpc_firewall.<env> dans 001.config_infrastructure/terraform.tfvars.
  2. Vérifier que les réseaux distants visés sont bien dans prefix_filter_in du tunnel concerné — une règle ACL vers un réseau non routé est inutile.
  3. Appliquer :
cd infrastructure/main-infra/scw-init-config/001.config_infrastructure && terraform apply
cd ../../scw-application/<env>/terraform/004.vpc_firewall && terraform plan   # relire les règles aller + retour
terraform apply

L'output vpc_acl.rules du run liste les règles effectivement poussées, retour compris.