VPC privé et Private Service Connect
Le problème
Par défaut, un service qui appelle europe-west1-aiplatform.googleapis.com passe par les adresses publiques de Google. Le trafic reste sur le réseau de Google, mais il est indistinguable d'un appel vers n'importe quelle autre API publique, y compris un bucket d'un autre client vers lequel on exfiltrerait des données. On veut :
- aucune sortie vers internet depuis l'application ;
- un chemin privé, identifiable, vers les seules API Google compatibles avec VPC Service Controls.
Les briques
| Brique | Rôle | Ressource Terraform |
|---|---|---|
| VPC (Virtual Private Cloud) en mode personnalisé | réseau privé, sous-réseaux créés explicitement | google_compute_network, google_compute_subnetwork |
| Private Google Access | les machines sans IP publique peuvent joindre les API Google | private_ip_google_access = true |
| Point d'accès PSC pour les API Google | une IP interne (ici 10.255.0.2) qui « représente » les API Google | google_compute_global_address + google_compute_global_forwarding_rule |
Zone DNS privée googleapis.com | fait résoudre *.googleapis.com vers l'IP PSC, dans ce VPC seulement | google_dns_managed_zone + 2 enregistrements |
| Pare-feu de sortie | refuse tout, sauf HTTPS vers l'IP PSC | 2 × google_compute_firewall |
Private Service Connect (PSC) est le mécanisme qui expose un service (ici les API Google) derrière une adresse IP interne de votre VPC.
sequenceDiagram participant App as "Cloud Run (sortie VPC directe)" participant DNS as "DNS privé googleapis.com" participant FW as "Pare-feu de sortie" participant PSC as "PSC 10.255.0.2 (vpc-sc)" participant V as "Vertex AI" App->>DNS: "europe-west1-aiplatform.googleapis.com ?" DNS-->>App: "CNAME googleapis.com -> 10.255.0.2" App->>FW: "TCP 443 vers 10.255.0.2" FW-->>App: "autorisé (priorité 1000)" App->>PSC: "requête HTTPS" PSC->>V: "API restreinte, contrôlée par VPC-SC" App->>FW: "TCP 443 vers 142.250.x.x (internet)" FW-->>App: "refusé (règle deny 65000)"
Le code
resource "google_compute_global_address" "psc" {
name = "${replace(var.prefix, "-", "")}psc"
purpose = "PRIVATE_SERVICE_CONNECT"
address_type = "INTERNAL"
address = var.psc_ip
network = google_compute_network.vpc.id
}
resource "google_compute_global_forwarding_rule" "psc" {
name = "${replace(var.prefix, "-", "")}psc" # lettres et chiffres uniquement
target = "vpc-sc" # ou "all-apis"
network = google_compute_network.vpc.id
ip_address = google_compute_global_address.psc.id
load_balancing_scheme = ""
}Deux cibles possibles :
target | API joignables | Quand |
|---|---|---|
all-apis | presque toutes les API Google | pas de VPC Service Controls |
vpc-sc | seulement les API compatibles VPC-SC (équivalent de restricted.googleapis.com) | avec un périmètre (leçon suivante) |
resource "google_compute_firewall" "deny_egress" {
name = "${var.prefix}-deny-all-egress"
network = google_compute_network.vpc.id
direction = "EGRESS"
priority = 65000 # plus le nombre est grand, plus la règle est faible
destination_ranges = ["0.0.0.0/0"]
deny { protocol = "all" }
}Une règle deny de priorité 65000 vers 0.0.0.0/0 et une règle allow de priorité 1000 vers 10.255.0.2/32 port 443 coexistent. Que se passe-t-il pour une connexion vers 10.255.0.2:443 ?
Le pare-feu VPC applique la règle correspondante de plus haute priorité, c'est-à-dire de plus petit numéro. À priorité égale, deny gagne ; ici les priorités diffèrent, l'allow à 1000 s'applique.
Et Cloud Run ?
Cloud Run n'est pas « dans » le VPC. La sortie VPC directe (Direct VPC egress) lui attache une interface dans le sous-réseau ; avec egress = "ALL_TRAFFIC", même le trafic vers des IP publiques passe par le VPC, et donc par le pare-feu.
vpc_access {
egress = "ALL_TRAFFIC"
network_interfaces {
network = google_compute_network.vpc.id
subnetwork = google_compute_subnetwork.app.id
}
}Pourquoi faut-il une zone DNS privée en plus du point d'accès PSC ?
Les bibliothèques clientes appellent les noms publics standards. La zone privée, visible seulement depuis ce VPC, les redirige vers l'IP PSC sans changer le code. Le chiffrement est assuré par TLS dans tous les cas.
Exercice
Votre équipe veut utiliser all-apis « pour ne rien casser » alors qu'un périmètre VPC-SC est prévu. Quel est le risque, et comment vérifier depuis Cloud Run que le DNS privé fonctionne ?
Voir la solution
Avec all-apis, le VPC peut joindre des API non couvertes par VPC Service Controls ; un code compromis pourrait exfiltrer par l'une d'elles. Avec vpc-sc, seules les API restreintes sont joignables, et le paramètre vpc_accessible_services = RESTRICTED-SERVICES du périmètre ferme la boucle.
Vérification : déployez temporairement une image de diagnostic, ou ajoutez un point de santé à l'application qui fait :
import socket
print(socket.gethostbyname("europe-west1-aiplatform.googleapis.com")) # attendu : 10.255.0.2Une IP publique signifie que la zone privée n'est pas attachée au bon VPC.
Résultat obtenu
terraform plan réel le 2026-10-15 (extrait de la couche réseau, aucune ressource créée) :
# google_compute_firewall.allow_psc will be created
# google_compute_firewall.deny_egress will be created
# google_compute_global_address.psc will be created
# google_compute_global_forwarding_rule.psc will be created
# google_compute_network.vpc will be created
# google_compute_subnetwork.app will be created
# google_dns_managed_zone.googleapis will be created
# google_dns_record_set.googleapis_a will be created
# google_dns_record_set.googleapis_wildcard will be created
...
Plan: 46 to add, 0 to change, 0 to destroy.Je sais faire
- Créer un VPC personnalisé avec Private Google Access
- Exposer les API Google par un point d'accès PSC et une zone DNS privée
- Bloquer toute sortie sauf vers le point PSC
- Faire sortir Cloud Run par le VPC avec la sortie VPC directe