Lab práctico · Semana 3: Redes en Google Cloud (I): VPC, firewall y balanceo de carga

Application Load Balancer externo global con MIG y Cloud Armor

⏱ 90-120 minDificultad: mediaApartados: 2.11.3

Qué vas a construir

Un global external Application Load Balancer con una IP anycast que reparte tráfico HTTP entre un Managed Instance Group (MIG) en Madrid, protegido por una política de Cloud Armor con una regla WAF preconfigurada, un bloqueo por IP y rate limiting. Las VMs no tendrán IP pública: solo reciben tráfico del balanceador y de las comprobaciones de estado.

flowchart LR
  U["Usuarios en Internet"] --> FR["Regla de reenvío global: IP anycast, puerto 80"]
  FR --> CA["Cloud Armor: web-policy"]
  CA --> PX["Target HTTP proxy"]
  PX --> UM["URL map"]
  UM --> BS["Backend service con health check"]
  BS --> MIG["MIG en europe-southwest1: 2 VMs e2-micro sin IP externa"]
  HC["Sondeos de salud 35.191.0.0/16"] --> MIG

Antes de empezar

  • Haber hecho el lab-06-vpc-firewall-nat (conoces VPC, firewall y Cloud NAT) y el lab-03-mig-autoescalado.
  • En Cloud Shell:
export PROJECT_ID="tu-proyecto-pca"
export REGION="europe-southwest1"
export ZONE="europe-southwest1-a"
gcloud config set project $PROJECT_ID
gcloud services enable compute.googleapis.com

Paso 1: red, firewall y NAT

Creas una VPC personalizada con una subred. Necesitas una regla que permita las comprobaciones de estado de Google; sin ella, todos los backends aparecen como no saludables. Añades Cloud NAT para que las VMs (sin IP pública) puedan instalar paquetes.

gcloud compute networks create vpc-lb --subnet-mode=custom
gcloud compute networks subnets create subred-lb \
  --network=vpc-lb --region=$REGION --range=10.20.0.0/24

gcloud compute firewall-rules create vpc-lb-allow-health-check \
  --network=vpc-lb --direction=INGRESS --action=ALLOW \
  --rules=tcp:80 \
  --source-ranges=35.191.0.0/16,130.211.0.0/22 \
  --target-tags=allow-health-check

gcloud compute routers create router-lb --network=vpc-lb --region=$REGION
gcloud compute routers nats create nat-lb --router=router-lb --region=$REGION \
  --nat-all-subnet-ip-ranges --auto-allocate-nat-external-ips

La documentación actual lista 35.191.0.0/16 como origen de los sondeos; se incluye también 130.211.0.0/22, que aparece en configuraciones anteriores, para no depender de ello. Con un balanceador proxy como este, el tráfico de usuario también llega a las VMs desde los proxies de Google en esos rangos.

Paso 2: plantilla de instancia y MIG

Cada VM instala Apache y publica una página con su nombre, para ver el reparto.

gcloud compute instance-templates create tpl-web \
  --region=$REGION --machine-type=e2-micro \
  --network=vpc-lb --subnet=subred-lb --no-address \
  --tags=allow-health-check \
  --image-family=debian-12 --image-project=debian-cloud \
  --metadata=startup-script='#! /bin/bash
apt-get update && apt-get install -y apache2
echo "Servido por $(hostname)" > /var/www/html/index.html
systemctl restart apache2'

gcloud compute instance-groups managed create mig-web \
  --template=tpl-web --size=2 --zone=$ZONE

gcloud compute instance-groups set-named-ports mig-web \
  --named-ports=http:80 --zone=$ZONE

El puerto con nombre http:80 le dice al servicio de backend a qué puerto enviar.

Paso 3: IP global y comprobación de estado

Reservas una IP global en nivel Premium (obligatorio para balanceadores globales).

gcloud compute addresses create ip-lb --ip-version=IPV4 \
  --network-tier=PREMIUM --global

gcloud compute health-checks create http hc-web --port=80

Paso 4: servicio de backend, URL map, proxy y regla de reenvío

El esquema EXTERNAL_MANAGED crea el global external Application LB actual (basado en Envoy). EXTERNAL crearía el classic. Activas el registro de peticiones en el servicio de backend para ver después lo que bloquea Cloud Armor.

Consola: Network services → Load balancing → Create load balancer → Application Load Balancer (HTTP/HTTPS) → Public facing → Global → Global external Application Load Balancer.

gcloud compute backend-services create bs-web \
  --load-balancing-scheme=EXTERNAL_MANAGED \
  --protocol=HTTP --port-name=http \
  --health-checks=hc-web --global \
  --enable-logging --logging-sample-rate=1.0

gcloud compute backend-services add-backend bs-web \
  --instance-group=mig-web --instance-group-zone=$ZONE --global

gcloud compute url-maps create um-web --default-service=bs-web

gcloud compute target-http-proxies create proxy-web --url-map=um-web

gcloud compute forwarding-rules create fr-web \
  --load-balancing-scheme=EXTERNAL_MANAGED \
  --network-tier=PREMIUM \
  --address=ip-lb --global \
  --target-http-proxy=proxy-web --ports=80

A partir de este momento corre el cargo de la regla de reenvío.

Paso 5: probar el balanceador

El balanceador tarda unos minutos en propagarse. Comprueba primero la salud de los backends:

gcloud compute backend-services get-health bs-web --global
export LB_IP=$(gcloud compute addresses describe ip-lb --global --format="value(address)")
echo $LB_IP

Cuando ambos estén HEALTHY, lanza varias peticiones:

for i in $(seq 1 10); do curl -s http://$LB_IP/; done

Verás alternarse los nombres de las dos VMs. Si recibes errores 404 o 502 durante los primeros minutos, espera y repite.

Paso 6: política de Cloud Armor

Creas una política con tres reglas y la asocias al servicio de backend:

  1. WAF preconfigurado contra inyección SQL (OWASP CRS), sensibilidad 1.
  2. Bloqueo de una IP: la de tu Cloud Shell, para comprobar que funciona (luego la quitas).
  3. Rate limiting por IP: más de 100 peticiones por minuto recibe un 429.
gcloud compute security-policies create web-policy \
  --description="Politica WAF del lab 07"

gcloud compute security-policies rules create 1000 \
  --security-policy=web-policy \
  --expression="evaluatePreconfiguredWaf('sqli-v422-stable', {'sensitivity': 1})" \
  --action=deny-403

gcloud compute security-policies rules create 3000 \
  --security-policy=web-policy \
  --src-ip-ranges="*" \
  --action=throttle \
  --rate-limit-threshold-count=100 \
  --rate-limit-threshold-interval-sec=60 \
  --conform-action=allow \
  --exceed-action=deny-429 \
  --enforce-on-key=IP

gcloud compute backend-services update bs-web \
  --security-policy=web-policy --global

La regla por defecto de la política (prioridad 2147483647) permite el resto del tráfico.

Comprueba que funciona

Espera un par de minutos a que se propague la política.

  1. Tráfico normal: curl -s http://$LB_IP/ sigue respondiendo.
  2. Inyección SQL: debe devolver 403.
curl -s -o /dev/null -w "%{http_code}\n" "http://$LB_IP/?id=1%27%20OR%20%271%27=%271"
  1. Bloqueo por IP: añade una regla con la IP pública de Cloud Shell y comprueba que recibes 403.
export MI_IP=$(curl -s https://ifconfig.me)
gcloud compute security-policies rules create 500 \
  --security-policy=web-policy \
  --src-ip-ranges="$MI_IP/32" --action=deny-403
sleep 60
curl -s -o /dev/null -w "%{http_code}\n" http://$LB_IP/
gcloud compute security-policies rules delete 500 --security-policy=web-policy --quiet
  1. Rate limiting: lanza 150 peticiones seguidas y cuenta los códigos.
for i in $(seq 1 150); do curl -s -o /dev/null -w "%{http_code}\n" http://$LB_IP/; done | sort | uniq -c

Deberías ver unas 100 respuestas 200 y el resto 429 (el umbral no es exacto al milímetro).

  1. En Network Security → Cloud Armor policies → web-policy ves las reglas y, en Logging, las peticiones bloqueadas (resource.type="http_load_balancer" y jsonPayload.enforcedSecurityPolicy.outcome="DENY").

Limpieza

Borra de fuera hacia dentro. Lo primero, la regla de reenvío, que es lo que más cuesta:

gcloud compute forwarding-rules delete fr-web --global --quiet
gcloud compute target-http-proxies delete proxy-web --quiet
gcloud compute url-maps delete um-web --quiet
gcloud compute backend-services update bs-web --security-policy="" --global
gcloud compute backend-services delete bs-web --global --quiet
gcloud compute security-policies delete web-policy --quiet
gcloud compute health-checks delete hc-web --quiet
gcloud compute addresses delete ip-lb --global --quiet
gcloud compute instance-groups managed delete mig-web --zone=$ZONE --quiet
gcloud compute instance-templates delete tpl-web --quiet
gcloud compute routers nats delete nat-lb --router=router-lb --region=$REGION --quiet
gcloud compute routers delete router-lb --region=$REGION --quiet
gcloud compute firewall-rules delete vpc-lb-allow-health-check --quiet
gcloud compute networks subnets delete subred-lb --region=$REGION --quiet
gcloud compute networks delete vpc-lb --quiet

Verifica que no queda nada facturable:

gcloud compute forwarding-rules list
gcloud compute addresses list
gcloud compute instances list

Las tres listas deben estar vacías (o sin recursos de este lab).

Preguntas para pensar como arquitecto

Los usuarios están en Europa y América. ¿Qué cambiarías para servirles con baja latencia?

Añadiría un segundo MIG en una región de América (p. ej. us-central1) al mismo servicio de backend. El balanceador global, con su IP anycast, recibe a cada usuario en el punto de presencia más cercano y lo envía al backend sano más cercano con capacidad. Si hay contenido estático, activaría Cloud CDN en el servicio de backend.

¿Por qué no puedes bloquear la IP de un atacante con una regla de firewall VPC en este diseño?

Porque el balanceador es un proxy: las conexiones que llegan a las VMs proceden de los proxies de Google, no del cliente. El firewall VPC no ve la IP del atacante. El bloqueo se hace en el borde con Cloud Armor, que sí evalúa la IP de origen del cliente.

La aplicación debe servirse por HTTPS con un certificado gestionado. ¿Qué piezas cambian?

Crearía un certificado gestionado por Google (con Certificate Manager o un ssl-certificates gestionado) para el dominio, un target-https-proxies con ese certificado y una regla de reenvío en el puerto 443. Opcionalmente, un segundo URL map y proxy HTTP en el puerto 80 que redirija a HTTPS. El DNS del dominio debe apuntar a la IP del balanceador para que se emita el certificado.

Una auditoría exige que el tráfico de los usuarios europeos no salga de la UE ni se termine TLS fuera. ¿Sigue valiendo el balanceador global?

El global termina las conexiones en el punto de presencia más cercano al usuario, que podría estar fuera de la región si el usuario viaja, y no garantiza dónde se procesa el tráfico. Para requisitos estrictos de residencia, un regional external Application LB en una región europea asegura que la terminación y el procesamiento ocurren en esa región.

Antes de activar una regla WAF en producción, ¿cómo evitarías bloquear a usuarios legítimos?

Crearía la regla en modo vista previa (--preview), revisaría en los registros qué peticiones habría bloqueado, ajustaría la sensibilidad o excluiría firmas concretas y, cuando los falsos positivos fueran aceptables, quitaría la vista previa.


Volver al módulo