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_ipcôté Scaleway,peer_private_ipcô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 avoirenable_routingetenable_custom_routes_propagationactivé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 demandehigh_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>-par2si 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¶
- Dans
001.config_infrastructure/terraform.tfvars, ajouter une entrée às2s_vpn_definition.<env>.tunnelsen choisissant des/30BGP non encore utilisés (voir le tableau des liens BGP ci-dessus). - Si le site expose de nouveaux réseaux, les ajouter à
prefix_filter_inet prévoir les règlesvpc_firewallcorrespondantes. - Appliquer
001.config_infrastructure, puis003.s2s_vpn, puis004.vpc_firewall. - 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¶
- Éditer
vpc_firewall.<env>dans001.config_infrastructure/terraform.tfvars. - Vérifier que les réseaux distants visés sont bien dans
prefix_filter_indu tunnel concerné — une règle ACL vers un réseau non routé est inutile. - 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.