Lab práctico · Semana 3: Redes en Google Cloud (I): VPC, firewall y balanceo de carga
VPC personalizada con firewall, Cloud NAT y Private Google Access
Qué vas a construir
Una VPC en modo personalizado con dos subredes, dos VMs sin IP externa, reglas de firewall por cuenta de servicio, acceso SSH por IAP, salida a Internet por Cloud NAT y acceso a Cloud Storage por Private Google Access. Es el esqueleto de red «seguro por defecto» que verás en casi cualquier diseño.
flowchart LR
YO["Tú en Cloud Shell"] -->|"SSH por IAP, 35.235.240.0/20"| WEB
subgraph VPC["vpc-lab (modo personalizado)"]
subgraph S1["subred-web 10.10.0.0/24"]
WEB["vm-web, SA web-sa, sin IP externa"]
end
subgraph S2["subred-app 10.10.1.0/24, con Private Google Access"]
APP["vm-app, SA app-sa, sin IP externa"]
end
end
WEB -->|"tcp:8080 permitido por cuenta de servicio"| APP
APP -->|"Cloud NAT"| INET["Internet"]
APP -->|"Private Google Access"| GCS["APIs de Google"]
Antes de empezar
- Haber hecho el
lab-01-cuenta-y-presupuesto(proyecto con facturación y alerta de presupuesto) y ellab-02-gcloud-e-iam. - Abre Cloud Shell y define las variables (sustituye el ID de tu proyecto):
export PROJECT_ID="tu-proyecto-pca"
export REGION="europe-southwest1"
export ZONE="europe-southwest1-a"
gcloud config set project $PROJECT_ID
gcloud config set compute/region $REGION
gcloud config set compute/zone $ZONE
gcloud services enable compute.googleapis.com iap.googleapis.com
Paso 1: crear la VPC en modo personalizado
Creas una red sin subredes automáticas (--subnet-mode=custom), como se recomienda en producción. El modo de enrutamiento dinámico lo dejas en regional (lo cambiarás en el lab 8).
Consola: VPC network → VPC networks → Create VPC network, nombre vpc-lab, Subnet creation mode: Custom.
gcloud compute networks create vpc-lab \
--subnet-mode=custom \
--bgp-routing-mode=regional
Paso 2: crear dos subredes
subred-web sin Private Google Access y subred-app con él activado, para comparar después.
gcloud compute networks subnets create subred-web \
--network=vpc-lab --region=$REGION --range=10.10.0.0/24
gcloud compute networks subnets create subred-app \
--network=vpc-lab --region=$REGION --range=10.10.1.0/24 \
--enable-private-ip-google-access
Fíjate en las rutas que se han creado solas:
gcloud compute routes list --filter="network:vpc-lab" \
--format="table(name,destRange,nextHopGateway,priority)"
Verás una ruta por subred y la ruta por defecto 0.0.0.0/0 hacia default-internet-gateway.
Paso 3: cuentas de servicio para las VMs
Vas a usar cuentas de servicio como destino de las reglas de firewall, que es más seguro que las etiquetas de red (asignarlas exige permisos de IAM).
gcloud iam service-accounts create web-sa --display-name="VM web"
gcloud iam service-accounts create app-sa --display-name="VM app"
export WEB_SA="web-sa@${PROJECT_ID}.iam.gserviceaccount.com"
export APP_SA="app-sa@${PROJECT_ID}.iam.gserviceaccount.com"
Paso 4: reglas de firewall
Recuerda que la VPC tiene implícito un deny de toda la entrada. Abres solo lo necesario:
- SSH desde IAP a ambas VMs. IAP TCP forwarding conecta desde el rango
35.235.240.0/20. - Web → app en el puerto 8080, por cuenta de servicio.
- ICMP interno para hacer pruebas.
gcloud compute firewall-rules create vpc-lab-allow-iap-ssh \
--network=vpc-lab --direction=INGRESS --action=ALLOW \
--rules=tcp:22 --source-ranges=35.235.240.0/20 \
--priority=1000 --enable-logging
gcloud compute firewall-rules create vpc-lab-allow-web-to-app \
--network=vpc-lab --direction=INGRESS --action=ALLOW \
--rules=tcp:8080 \
--source-service-accounts=$WEB_SA \
--target-service-accounts=$APP_SA \
--priority=1000
gcloud compute firewall-rules create vpc-lab-allow-icmp-internal \
--network=vpc-lab --direction=INGRESS --action=ALLOW \
--rules=icmp --source-ranges=10.10.0.0/16 \
--priority=1000
Paso 5: crear las VMs sin IP externa
La opción --no-address hace que la VM no tenga IP pública. La VM de app arranca un servidor HTTP mínimo en el puerto 8080.
gcloud compute instances create vm-web \
--zone=$ZONE --machine-type=e2-micro \
--subnet=subred-web --no-address \
--service-account=$WEB_SA --scopes=cloud-platform \
--image-family=debian-12 --image-project=debian-cloud
gcloud compute instances create vm-app \
--zone=$ZONE --machine-type=e2-micro \
--subnet=subred-app --no-address \
--service-account=$APP_SA --scopes=cloud-platform \
--image-family=debian-12 --image-project=debian-cloud \
--metadata=startup-script='#! /bin/bash
mkdir -p /srv && echo "hola desde vm-app" > /srv/index.html
cd /srv && nohup python3 -m http.server 8080 &'
Paso 6: comprobar el firewall
Entra en vm-web por IAP (sin IP pública):
gcloud compute ssh vm-web --zone=$ZONE --tunnel-through-iap
Dentro de vm-web:
curl -s --max-time 5 http://$(getent hosts vm-app | awk '{print $1}'):8080
Si el nombre no resuelve, usa la IP interna de vm-app (gcloud compute instances list desde Cloud Shell). Debes ver hola desde vm-app: la regla por cuenta de servicio funciona.
Ahora prueba Internet desde vm-web:
curl -s --max-time 5 -o /dev/null -w "%{http_code}\n" https://www.debian.org
Fallará por tiempo de espera: sin IP externa y sin Cloud NAT no hay salida a Internet. Sal con exit.
Paso 7: Cloud NAT para la salida a Internet
Cloud NAT se configura sobre un Cloud Router en la región. Lo aplicas solo a subred-app para comparar.
Consola: Network services → Cloud NAT → Create Cloud NAT gateway, red vpc-lab, región Madrid, crea un Cloud Router nuevo, Source: subred subred-app.
gcloud compute routers create router-lab \
--network=vpc-lab --region=$REGION
gcloud compute routers nats create nat-lab \
--router=router-lab --region=$REGION \
--nat-custom-subnet-ip-ranges=subred-app \
--auto-allocate-nat-external-ips \
--enable-logging
Espera un minuto y entra en vm-app:
gcloud compute ssh vm-app --zone=$ZONE --tunnel-through-iap
Dentro:
curl -s --max-time 5 -o /dev/null -w "%{http_code}\n" https://www.debian.org
curl -s --max-time 5 https://ifconfig.me ; echo
El primero devuelve 200 y el segundo muestra la IP pública de Cloud NAT (no es de la VM). vm-web sigue sin salida porque su subred no está en la pasarela.
Paso 8: Private Google Access
Sigue en vm-app. Su subred tiene Private Google Access, así que llegaría a las APIs de Google incluso sin NAT. Para comprobarlo de verdad, desde Cloud Shell (en otra pestaña) quita temporalmente la NAT:
gcloud compute routers nats delete nat-lab --router=router-lab --region=$REGION --quiet
En vm-app:
curl -s --max-time 5 -o /dev/null -w "%{http_code}\n" https://www.debian.org
gcloud storage ls
Internet vuelve a fallar, pero gcloud storage ls funciona (lista tus buckets o no devuelve nada si no tienes, sin error de conexión): el tráfico a las APIs de Google sale por Private Google Access. Si repites la prueba desde vm-web (su subred no tiene PGA), gcloud storage ls se queda colgado. Sal con exit.
Comprueba que funciona
vm-webllega avm-app:8080, pero no al revés (no hay regla que lo permita).- Con NAT,
vm-appsale a Internet con la IP de la pasarela. - Sin NAT,
vm-appsigue usando Cloud Storage gracias a Private Google Access;vm-webno. - En Logging → Logs Explorer, filtra
resource.type="gce_subnetwork"ylogName:"firewall"para ver los registros de la regla de SSH.
Limpieza
Borra en este orden (las reglas y las subredes dependen de la red):
gcloud compute instances delete vm-web vm-app --zone=$ZONE --quiet
gcloud compute routers nats delete nat-lab --router=router-lab --region=$REGION --quiet 2>/dev/null
gcloud compute routers delete router-lab --region=$REGION --quiet
gcloud compute firewall-rules delete vpc-lab-allow-iap-ssh vpc-lab-allow-web-to-app vpc-lab-allow-icmp-internal --quiet
gcloud compute networks subnets delete subred-web subred-app --region=$REGION --quiet
gcloud compute networks delete vpc-lab --quiet
gcloud iam service-accounts delete $WEB_SA --quiet
gcloud iam service-accounts delete $APP_SA --quiet
Comprueba que no queda nada: gcloud compute instances list, gcloud compute routers list y gcloud compute networks list.
Preguntas para pensar como arquitecto
¿Por qué usar cuentas de servicio en lugar de etiquetas de red como destino del firewall?
Porque cualquiera con permiso para editar la VM puede añadirle una etiqueta y «colarse» en una regla. Asignar una cuenta de servicio a una VM exige iam.serviceAccountUser sobre esa cuenta, así que el equipo de seguridad controla quién puede recibir qué tráfico. Es la base de la separación de funciones en el firewall.
La VM de app necesita llamar a la API de un proveedor que solo acepta IP en lista blanca. ¿Qué cambiarías?
Reservaría una o varias IP externas estáticas regionales y configuraría Cloud NAT con asignación manual (--nat-external-ip-pool) en lugar de automática, para que la IP de salida sea fija y comunicable al proveedor.
¿Qué pasaría si borras la ruta por defecto 0.0.0.0/0?
Se corta la salida a Internet (Cloud NAT deja de funcionar) y también Private Google Access, que usa esa ruta hacia las IP públicas de las APIs. Para acceder a las APIs sin ruta por defecto, crearías rutas específicas a private.googleapis.com o restricted.googleapis.com, o un endpoint de Private Service Connect.
¿Cómo harías que ninguna VM de la organización pudiera tener IP externa?
Con la política de organización compute.vmExternalIpAccess (lista de VMs permitidas vacía), aplicada en la organización o en una carpeta. Las VMs saldrían por Cloud NAT y se administrarían por IAP.