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

⏱ 75-100 minDificultad: mediaApartados: 1.32.1

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 el lab-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:

  1. SSH desde IAP a ambas VMs. IAP TCP forwarding conecta desde el rango 35.235.240.0/20.
  2. Web → app en el puerto 8080, por cuenta de servicio.
  3. 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-web llega a vm-app:8080, pero no al revés (no hay regla que lo permita).
  • Con NAT, vm-app sale a Internet con la IP de la pasarela.
  • Sin NAT, vm-app sigue usando Cloud Storage gracias a Private Google Access; vm-web no.
  • En Logging → Logs Explorer, filtra resource.type="gce_subnetwork" y logName:"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.


Volver al módulo