Cerramos el módulo 2 con un diagnóstico incómodo: todo lo que AlpinaShop ha construido está expuesto. La VM alpinashop-web-1 tiene el puerto 80 abierto a internet y el 22 accesible desde cualquier dirección, la instancia alpinashop-pedidos es alcanzable por IP pública, el Service de GKE publicó una IP sin HTTPS y no existe ninguna frontera entre lo que debe ser público y lo que jamás debería serlo. Esta lección construye esa frontera.
La red es la capa sobre la que se apoya todo lo demás. Antes de poner un balanceador, un CDN o un WAF hay que decidir dónde vive cada máquina, qué rangos de direcciones usa, quién puede hablar con quién y por dónde sale el tráfico a internet. En Google Cloud esa decisión se materializa en una VPC (Virtual Private Cloud): una red privada definida por software, aislada del resto de clientes, dentro de la cual conviven tus instancias, tus clústeres, tus bases de datos y tus balanceadores.
La red VPC de Google Cloud tiene una propiedad que sorprende a quien viene de AWS o de un centro de datos tradicional: es global. Una única red puede abarcar todas las regiones del planeta, y dos máquinas en Bélgica y en Tokio dentro de la misma VPC se hablan por IP privada sin túneles ni pasarelas. Las subredes, en cambio, son regionales. Entender esa asimetría es el 80 % de entender la red de Google.
En esta lección diseñarás e implementarás la red alpinashop-vpc con sus subredes, planificarás el direccionamiento sin solapes, escribirás el conjunto real de reglas de firewall de AlpinaShop, darás salida a internet a máquinas sin IP pública con Cloud NAT, alcanzarás las APIs de Google sin pasar por internet con Private Google Access y conectarás por fin alpinashop-pedidos por IP privada.
Aviso. Esta lección tiene implicaciones de seguridad directas. El diseño que se propone es razonable para una pyme como AlpinaShop, pero cualquier arquitectura de red que vaya a producción debe revisarla un profesional de seguridad, y si tratas datos personales o medios de pago, también un responsable de cumplimiento normativo.
Contenido
- Qué es una VPC y en qué se diferencia de una red tradicional
- Red por defecto, modo automático y modo personalizado
- Planificación del direccionamiento: CIDR sin solapes
- Creación de
alpinashop-vpcy sus subredes - Rangos secundarios para GKE
- Direcciones IP internas y externas, efímeras y estáticas
- Reglas de firewall VPC: el modelo completo
- El conjunto real de reglas de AlpinaShop
- Rutas y la puerta de enlace por defecto
- Cloud NAT: salida a internet sin IP pública
- Private Google Access: llegar a las APIs sin salir a internet
- Acceso privado a servicios: Cloud SQL por IP privada
- VPC Flow Logs: diagnóstico y auditoría
- Qué queda fuera de esta lección
- Qué es una VPC y en qué se diferencia de una red tradicional
En un centro de datos clásico, la red es física: switches, VLAN, cableado, rangos que alguien apuntó en una hoja de cálculo hace ocho años. En Google Cloud, la red es una abstracción de software montada sobre la red global de Google. Las diferencias prácticas son estas:
| Concepto | Red tradicional | VPC de Google Cloud |
|---|---|---|
| Alcance | Un centro de datos, una sede | Global: todas las regiones a la vez |
| Segmentación | VLAN + routers físicos | Subredes regionales dentro de la misma red |
| Conexión entre sedes | Enlaces dedicados, VPN | Automática dentro de la VPC, por la red interna de Google |
| Firewall | Aparato en un punto de la red | Distribuido: se aplica en cada interfaz de cada VM |
| Cambiar un rango | Ventana de mantenimiento | Se añade una subred nueva; ampliar rango es una orden |
| Coste de la red interna | Amortización del hardware | Tráfico entre zonas y regiones, medido por GB |
Tres consecuencias que conviene grabar desde el principio:
- La red es un recurso global del proyecto. No pertenece a una región. Cuando creas
alpinashop-vpcenalpinashop-prod, existe en todas partes. - Las subredes son regionales, no zonales. Una subred en
europe-west1cubreeurope-west1-b,-cy-d. Una VM en cualquiera de esas zonas puede tomar una IP de esa subred. Esto es lo que permite que el MIG regionalalpinashop-web-migreparta instancias entre tres zonas usando una sola subred. - El firewall no está "en un sitio". No hay un cortafuegos perimetral por el que pase el tráfico: cada regla se aplica en el hipervisor, en la propia interfaz de red de cada VM. Por eso el tráfico entre dos VM de la misma subred también está sujeto a reglas, y por eso no puedes "saltarte" el firewall poniendo las máquinas juntas.
- Red por defecto, modo automático y modo personalizado
Cuando creaste el proyecto alpinashop-prod, Google creó automáticamente una red llamada default. Es cómoda para probar y mala idea para producción. Veamos por qué.
Existen dos modos de red:
Modo automático (--subnet-mode=auto) |
Modo personalizado (--subnet-mode=custom) |
|
|---|---|---|
| Subredes | Una por región, creadas solas, con rangos fijos 10.128.0.0/9 |
Ninguna: las creas tú, con el rango que decidas |
| Nuevas regiones | Aparece una subred automáticamente | No cambia nada |
| Control del CIDR | Ninguno | Total |
| Riesgo de solape con la oficina o con otra VPC | Alto | Controlado |
| Reglas de firewall iniciales | Permisivas (incluye SSH y RDP desde 0.0.0.0/0) |
Ninguna, salvo las implícitas |
La red default es de modo automático y además trae reglas de firewall preconfiguradas que abren el puerto 22 (SSH) y el 3389 (RDP) a todo internet. Eso es exactamente el problema que AlpinaShop arrastra desde el módulo 2.
Toda organización seria usa modo personalizado por tres motivos:
- Control del direccionamiento. Si mañana AlpinaShop conecta su oficina por VPN (tema de 07-03), los rangos no pueden chocar. Con modo automático, Google decide por ti en todas las regiones presentes y futuras.
- Superficie de ataque mínima. No hay subredes en regiones que no usas ni reglas permisivas que alguien olvidó revisar.
- Reproducibilidad. La red se describe en código (Terraform, en 06-07) y se recrea idéntica en
alpinashop-dev.
La primera tarea real, entonces, es crear una red personalizada y dejar de usar default:
# Fijamos el proyecto de trabajo (configuración nombrada del módulo 1)
gcloud config set project alpinashop-prod
# Vemos qué hay ahora mismo
gcloud compute networks list
gcloud compute networks subnets list --filter="network:default"Más adelante, cuando todo esté migrado, la red default se elimina. No se elimina el primer día: hay recursos vivos dentro. El orden correcto es crear la red nueva → mover cargas → borrar la vieja.
- Planificación del direccionamiento: CIDR sin solapes
Antes de escribir un solo comando, se planifica en papel. Un plan de direccionamiento malo se paga durante años, porque el rango primario de una subred se puede ampliar, pero no reducir ni mover, y porque dos redes con rangos solapados no se pueden conectar nunca.
Recordatorio mínimo de CIDR:
| Notación | Máscara | Direcciones totales | Direcciones usables en GCP |
|---|---|---|---|
/24 |
255.255.255.0 | 256 | 252 |
/22 |
255.255.252.0 | 1 024 | 1 020 |
/20 |
255.255.240.0 | 4 096 | 4 092 |
/16 |
255.255.0.0 | 65 536 | 65 532 |
Google reserva cuatro direcciones en cada subred: la de red, la puerta de enlace (siempre la segunda, x.x.x.1), y dos reservadas para uso futuro (las dos últimas). Por eso un /24 da 252 IP utilizables, no 254.
El plan de AlpinaShop reserva el bloque privado 10.10.0.0/16 para producción y deja espacio deliberado entre subredes:
| Uso | Rango | Región | Comentario |
|---|---|---|---|
sn-web-euw1 (primario) |
10.10.0.0/24 |
europe-west1 |
Instancias del MIG del catálogo |
sn-datos-euw1 (primario) |
10.10.1.0/24 |
europe-west1 |
Backends internos y trabajos por lotes |
sn-web-euw1 secundario gke-pods |
10.20.0.0/16 |
— | Pods de alpinashop-cluster |
sn-web-euw1 secundario gke-servicios |
10.21.0.0/20 |
— | Services de alpinashop-cluster |
| Acceso privado a servicios (Cloud SQL) | 10.30.0.0/20 |
— | Rango cedido a Google para servicios gestionados |
| Reservado para futuras subredes | 10.10.2.0/24 … 10.10.255.0/24 |
— | No asignar sin actualizar este documento |
Reservado para alpinashop-dev |
10.60.0.0/16 |
— | Otra VPC, otro proyecto |
| Oficina de AlpinaShop (VPN futura) | 192.168.10.0/24 |
— | No usar nunca dentro de la VPC |
Reglas de oro al planificar:
- Nunca uses
10.0.0.0/24ni192.168.1.0/24. Son los rangos que usa medio mundo, incluida la red doméstica de tus compañeros; el día que haya VPN, chocarán. - Deja huecos. Es más fácil pedir perdón que reorganizar direcciones.
- Documenta el plan y versiónalo. El documento de direccionamiento vive en el repositorio, no en la memoria de Marta.
- Los rangos de pods de Kubernetes son grandes. Un
/16para pods no es exagerado: GKE reserva un bloque por nodo.
- Creación de
alpinashop-vpc y sus subredes
alpinashop-vpc y sus subredesCon el plan cerrado, la implementación son tres órdenes:
# 1) La red, en modo personalizado y con enrutamiento global
gcloud compute networks create alpinashop-vpc \
--project=alpinashop-prod \
--subnet-mode=custom \
--bgp-routing-mode=global \
--description="Red principal de produccion de AlpinaShop"Dos parámetros merecen explicación:
--subnet-mode=custom: no crea ninguna subred automáticamente. Es lo que queremos.--bgp-routing-mode=global: cuando en el futuro haya un Cloud Router (VPN o Interconnect, tema de 07-03), las rutas aprendidas se propagarán a todas las regiones, no solo a la del router. Es el valor correcto para una red que puede crecer; cambiarlo después obliga a reconfigurar el router.
# 2) Subred de la capa web
gcloud compute networks subnets create sn-web-euw1 \
--project=alpinashop-prod \
--network=alpinashop-vpc \
--region=europe-west1 \
--range=10.10.0.0/24 \
--enable-private-ip-google-access \
--enable-flow-logs \
--logging-aggregation-interval=interval-5-sec \
--logging-flow-sampling=0.5 \
--logging-metadata=include-all
# 3) Subred de datos y backends internos
gcloud compute networks subnets create sn-datos-euw1 \
--project=alpinashop-prod \
--network=alpinashop-vpc \
--region=europe-west1 \
--range=10.10.1.0/24 \
--enable-private-ip-google-access \
--enable-flow-logs \
--logging-flow-sampling=0.5Qué hace cada opción, en detalle:
--range: el rango primario. Es el que se reparte entre las interfaces de las VM.--enable-private-ip-google-access: permite que una VM sin IP pública llegue a las APIs de Google (Cloud Storage, BigQuery, Secret Manager…). Lo desarrollamos en el apartado 11. Actívalo siempre; no tiene coste ni contraindicación.--enable-flow-logsy sus parámetros: activa el registro de flujos.--logging-flow-sampling=0.5muestrea la mitad de los flujos, lo que reduce el coste de logs a la mitad manteniendo utilidad de diagnóstico. Lo vemos en el apartado 13.
Comprobación:
gcloud compute networks subnets describe sn-web-euw1 \
--region=europe-west1 \
--format="table(name, ipCidrRange, gatewayAddress, privateIpGoogleAccess)"Verás que gatewayAddress es 10.10.0.1: la puerta de enlace siempre es la segunda dirección del rango, y no es una máquina que puedas tocar, sino una función distribuida de la red.
- Rangos secundarios para GKE
El clúster alpinashop-cluster que creamos en 02-05 necesita direcciones para pods y para services, y esas direcciones no salen del rango primario de la subred, sino de rangos secundarios (lo que Google llama alias IP ranges). Este es el modelo de red nativo de VPC de GKE, y es el que se usa siempre en Autopilot.
gcloud compute networks subnets update sn-web-euw1 \
--region=europe-west1 \
--add-secondary-ranges=gke-pods=10.20.0.0/16,gke-servicios=10.21.0.0/20Por qué rangos tan grandes:
| Elemento | Rango | Capacidad aproximada |
|---|---|---|
| Nodos | 10.10.0.0/24 (primario) |
252 nodos |
| Pods | 10.20.0.0/16 |
65 536 direcciones; GKE asigna un /24 por nodo por defecto |
| Services (ClusterIP) | 10.21.0.0/20 |
4 096 servicios |
La consecuencia de mayor calado: con red nativa de VPC, un pod tiene una IP real de la VPC. No hay NAT interno entre pods y VM. Eso significa que una regla de firewall puede referirse directamente al rango de pods, y que una VM de sn-datos-euw1 puede recibir tráfico de un pod y ver su IP de origen auténtica. Es más simple de razonar y más fácil de auditar.
Al crear un clúster nuevo se indican los rangos así (referencia para cuando recrees el clúster en la red nueva):
gcloud container clusters create-auto alpinashop-cluster \
--region=europe-west1 \
--network=alpinashop-vpc \
--subnetwork=sn-web-euw1 \
--cluster-secondary-range-name=gke-pods \
--services-secondary-range-name=gke-servicios
- Direcciones IP internas y externas, efímeras y estáticas
Cada VM tiene siempre una IP interna (de la subred) y, opcionalmente, una IP externa. Y cada una puede ser efímera (se asigna al arrancar y se pierde al parar la máquina) o estática (reservada, sobrevive a la instancia).
| Tipo | Alcance | Persistencia | Coste orientativo | Uso típico en AlpinaShop |
|---|---|---|---|---|
| Interna efímera | Subred | Mientras viva la VM | Gratis | Instancias del MIG |
| Interna estática | Subred | Reservada | Gratis | Backends internos con nombre fijo |
| Externa efímera | Internet | Mientras viva la VM | Se factura mientras está en uso | Nada en producción |
| Externa estática regional | Internet | Reservada | Pequeño coste; más cara si está sin usar | Balanceador regional, Cloud NAT |
| Externa estática global | Internet (anycast) | Reservada | Pequeño coste | IP del balanceador HTTPS global (03-02) |
Reservamos ya la IP global que usará el balanceador de la próxima lección y las IP que necesitará Cloud NAT:
# IP global anycast para el balanceador HTTPS externo (la usaremos en 03-02 y 03-07)
gcloud compute addresses create alpinashop-lb-ip \
--project=alpinashop-prod \
--global \
--ip-version=IPV4
gcloud compute addresses describe alpinashop-lb-ip --global --format="value(address)"
# IP regional estática para la salida de Cloud NAT
gcloud compute addresses create alpinashop-nat-ip \
--project=alpinashop-prod \
--region=europe-west1Reservar la IP de salida de NAT tiene una ventaja concreta: la pasarela de pago de AlpinaShop exige una lista de IP autorizadas. Si la IP de salida fuese efímera, cambiaría al recrear el NAT y el cobro dejaría de funcionar un martes por la tarde sin motivo aparente.
Una advertencia de facturación: una IP externa estática reservada y no asociada a nada se factura igualmente, y a una tarifa mayor que si estuviera en uso. Es una de las fugas de coste más habituales. Revísalas periódicamente:
gcloud compute addresses list --filter="status=RESERVED" \
--format="table(name, region, address, status)"
- Reglas de firewall VPC: el modelo completo
El firewall de VPC es stateful (si permites la conexión de entrada, la respuesta sale sin necesidad de regla) y distribuido (se aplica en cada interfaz). Una regla tiene estos elementos:
| Elemento | Valores | Notas |
|---|---|---|
| Dirección | INGRESS (entrada) o EGRESS (salida) |
Por defecto INGRESS |
| Acción | allow o deny |
No hay "log only"; para eso está --enable-logging |
| Prioridad | 0 – 65535, menor gana | Por defecto 1000 |
| Origen (ingress) | Rangos CIDR, etiquetas de red, cuentas de servicio | Se elige uno de los tres tipos |
| Destino (egress) | Rangos CIDR | |
| Destinatarios | Toda la red, etiquetas de red, o cuentas de servicio | El "a quién se aplica" |
| Protocolos y puertos | tcp:80,443, udp:53, icmp, all |
Además existen cuatro reglas implícitas que no ves en la lista y no puedes borrar:
| Regla implícita | Dirección | Acción | Prioridad |
|---|---|---|---|
| Permitir toda la salida | EGRESS | allow | 65535 |
| Denegar toda la entrada | INGRESS | deny | 65535 |
Permitir DHCP, DNS y metadatos (169.254.169.254) |
EGRESS | allow | Siempre |
| Bloquear el puerto 25 saliente | EGRESS | deny | Siempre (antispam) |
La consecuencia práctica es la que define la disciplina de trabajo: por defecto no entra nada y sale todo. Todo lo que quieras permitir de entrada hay que escribirlo; todo lo que quieras impedir de salida, también.
Etiquetas de red frente a cuentas de servicio
Es la decisión de diseño más importante del firewall. Se puede seleccionar a qué máquinas se aplica una regla de dos maneras:
| Criterio | Etiquetas de red (--target-tags) |
Cuentas de servicio (--target-service-accounts) |
|---|---|---|
| Quién puede ponerlas | Cualquiera con compute.instances.setTags |
Solo quien puede actuar como esa cuenta de servicio |
| Cambio en caliente | Sí, al vuelo | Requiere parar la instancia |
| Riesgo | Un desarrollador puede etiquetar su VM como web y heredar sus permisos de red |
Controlado por IAM |
| Legibilidad | Muy alta | Alta |
| Recomendación | Aceptable para entornos pequeños | Preferible en producción |
AlpinaShop usa un modelo mixto y explícito: etiquetas para las reglas de entrada desde internet (donde la legibilidad importa y las máquinas son del MIG) y cuentas de servicio para el acceso a datos. Y documenta esa decisión, porque mezclar los dos criterios sin criterio es una fuente clásica de agujeros.
- El conjunto real de reglas de AlpinaShop
Este es el conjunto completo. Léelo entero antes de ejecutarlo: cada regla responde a una necesidad concreta.
# ---------------------------------------------------------------
# 1) Tráfico HTTP/HTTPS SOLO desde los rangos del balanceador de Google
# 130.211.0.0/22 y 35.191.0.0/16 son los rangos desde los que
# Google Cloud origina las peticiones del balanceador y las
# comprobaciones de estado. NO son "internet".
# ---------------------------------------------------------------
gcloud compute firewall-rules create fw-allow-lb-health-web \
--network=alpinashop-vpc \
--direction=INGRESS \
--action=allow \
--priority=1000 \
--source-ranges=130.211.0.0/22,35.191.0.0/16 \
--target-tags=web-catalogo \
--rules=tcp:80,tcp:8080 \
--description="HTTP y health checks desde el balanceador global"
# ---------------------------------------------------------------
# 2) SSH SOLO desde el rango del Identity-Aware Proxy (IAP).
# 35.235.240.0/20 es el rango desde el que IAP abre el túnel TCP.
# Con esto NO hace falta ninguna IP publica en las VM.
# ---------------------------------------------------------------
gcloud compute firewall-rules create fw-allow-ssh-iap \
--network=alpinashop-vpc \
--direction=INGRESS \
--action=allow \
--priority=1000 \
--source-ranges=35.235.240.0/20 \
--rules=tcp:22 \
--description="SSH unicamente a traves de IAP (ver 03-04)"
# ---------------------------------------------------------------
# 3) La capa web puede hablar con la capa de datos por PostgreSQL.
# Selector por cuenta de servicio: solo las maquinas que corren
# como sa-catalogo-web pueden abrir el 5432.
# ---------------------------------------------------------------
gcloud compute firewall-rules create fw-allow-web-to-datos \
--network=alpinashop-vpc \
--direction=INGRESS \
--action=allow \
--priority=1000 \
--source-service-accounts=sa-catalogo-web@alpinashop-prod.iam.gserviceaccount.com \
--target-tags=capa-datos \
--rules=tcp:5432 \
--description="Acceso PostgreSQL desde la aplicacion del catalogo"
# ---------------------------------------------------------------
# 4) Denegar explicitamente el resto de entrada, con registro.
# Prioridad 65000: por debajo de todas las reglas anteriores,
# por encima de la regla implicita 65535. Sirve para VER en los
# logs qué se está intentando y no se permite.
# ---------------------------------------------------------------
gcloud compute firewall-rules create fw-deny-all-ingress \
--network=alpinashop-vpc \
--direction=INGRESS \
--action=deny \
--priority=65000 \
--source-ranges=0.0.0.0/0 \
--rules=all \
--enable-logging \
--description="Denegacion explicita con log de todo lo no permitido"Diagrama del resultado:
flowchart TB
Internet([Internet])
LB[Balanceador HTTPS global<br/>130.211.0.0/22 · 35.191.0.0/16]
IAP[Identity-Aware Proxy<br/>35.235.240.0/20]
subgraph VPC["alpinashop-vpc · global"]
subgraph SNW["sn-web-euw1 · 10.10.0.0/24 · europe-west1"]
MIG["alpinashop-web-mig<br/>tag: web-catalogo<br/>SA: sa-catalogo-web"]
GKE["alpinashop-cluster<br/>pods 10.20.0.0/16"]
end
subgraph SND["sn-datos-euw1 · 10.10.1.0/24"]
BATCH["Lotes / backends internos<br/>tag: capa-datos"]
end
NAT[Cloud NAT]
end
PSA["Acceso privado a servicios<br/>10.30.0.0/20"]
SQL[(alpinashop-pedidos<br/>IP privada)]
Internet -->|443| LB
LB -->|80/8080 tag web-catalogo| MIG
IAP -->|22| MIG
MIG -->|5432 SA sa-catalogo-web| SQL
MIG --> NAT --> Internet
SQL --- PSA
PSA --- VPC
Por qué 0.0.0.0/0 en el puerto 22 es un error
Es el patrón más extendido y el más peligroso. Abrir tcp:22 a 0.0.0.0/0 significa literalmente que cualquier máquina del planeta puede intentar autenticarse en tu servidor. Las consecuencias no son teóricas:
- Un servidor con el 22 abierto recibe miles de intentos de autenticación al día desde el primer minuto. Los escáneres automáticos barren los rangos de todos los proveedores de nube continuamente.
- Basta una credencial débil, una clave reutilizada o un fallo en
sshdpara que se convierta en una intrusión. - El ruido en los logs esconde los ataques reales.
- Cierra la puerta a la trazabilidad: no sabes quién entró, solo qué clave se usó.
La alternativa correcta es IAP TCP forwarding, la regla número 2 del bloque anterior. Con ella:
- Las VM no necesitan IP pública.
- El acceso pasa por Google, que exige autenticación con la identidad corporativa (Marta, Dani) y aplica IAM: quien no tenga el rol
roles/iap.tunnelResourceAccessorno llega ni al saludo de SSH. - Cada sesión queda registrada con la identidad real de la persona.
# Conexión SSH sin IP pública, a través de IAP
gcloud compute ssh alpinashop-web-1 \
--zone=europe-west1-b \
--tunnel-through-iapLos detalles de IAP y sus permisos los desarrollamos en 03-04 (IAM); aquí basta con saber que la regla de firewall del 22 apunta a 35.235.240.0/20 y nunca a internet.
Comprobar reglas antes de que duelan
# Ver todas las reglas ordenadas por prioridad
gcloud compute firewall-rules list \
--filter="network:alpinashop-vpc" \
--sort-by=priority \
--format="table(name, direction, priority, sourceRanges.list(), targetTags.list(), allowed[].map().firewall_rule().list())"
# Simular: ¿puede esta VM recibir tráfico en el 22 desde internet?
gcloud compute networks get-effective-firewalls alpinashop-web-1 \
--zone=europe-west1-b 2>/dev/null || \
gcloud compute instances network-interfaces get-effective-firewalls alpinashop-web-1 \
--zone=europe-west1-b
- Rutas y la puerta de enlace por defecto
Las rutas deciden por dónde sale un paquete. Cada VPC nace con dos tipos de ruta automáticas:
| Ruta | Destino | Siguiente salto | Se puede borrar |
|---|---|---|---|
| Ruta de subred | El CIDR de cada subred | La propia VPC | No (se crea y borra con la subred) |
| Ruta por defecto | 0.0.0.0/0 |
Puerta de enlace de internet | Sí |
Que la ruta por defecto se pueda borrar es una herramienta de seguridad muy potente: si eliminas la ruta 0.0.0.0/0 de una VPC, ninguna máquina de esa red puede salir a internet, ni siquiera con IP pública. Es el patrón de las redes aisladas para tratamiento de datos sensibles. AlpinaShop no llega ahí en producción, pero es útil para el proyecto de análisis de Lucía si algún día trata datos personales.
gcloud compute routes list --filter="network:alpinashop-vpc" \
--format="table(name, destRange, nextHopGateway, priority)"Las rutas personalizadas (por ejemplo, enviar cierto tráfico a un appliance de red) y las rutas aprendidas por BGP desde una VPN pertenecen a 07-03.
- Cloud NAT: salida a internet sin IP pública
Ya hemos dicho que las VM no deben tener IP pública. Pero necesitan salir a internet para actualizar paquetes del sistema, instalar dependencias de Python o llamar a la API de la pasarela de pago. La solución es Cloud NAT: un servicio gestionado, sin instancias que mantener, que traduce las direcciones privadas a una o varias IP públicas de salida.
Puntos clave:
- Cloud NAT es solo de salida. Nadie puede iniciar una conexión desde internet hacia una VM a través de él. Es exactamente lo que queremos.
- No es una máquina: no hay cuello de botella que dimensionar ni parches que aplicar.
- Se configura por región y se asocia a un Cloud Router, que en este caso no hace BGP: es solo el punto de anclaje del servicio.
# 1) Cloud Router (obligatorio para NAT, aunque aquí no anuncie rutas)
gcloud compute routers create alpinashop-router-euw1 \
--network=alpinashop-vpc \
--region=europe-west1
# 2) La pasarela NAT, con la IP estática reservada en el apartado 6
gcloud compute routers nats create alpinashop-nat-euw1 \
--router=alpinashop-router-euw1 \
--region=europe-west1 \
--nat-custom-subnet-ip-ranges=sn-web-euw1,sn-datos-euw1 \
--nat-external-ip-pool=alpinashop-nat-ip \
--enable-logging \
--log-filter=ERRORS_ONLY \
--min-ports-per-vm=128Detalle de las opciones:
--nat-custom-subnet-ip-ranges: solo estas dos subredes salen por NAT. La alternativa (--nat-all-subnet-ip-ranges) es cómoda pero menos explícita.--nat-external-ip-pool=alpinashop-nat-ip: usa nuestra IP estática. Esa es la dirección que AlpinaShop comunica a la pasarela de pago para su lista de autorizados. La alternativa--auto-allocate-nat-external-ipsdeja que Google elija, y las IP pueden cambiar.--min-ports-per-vm=128: cada VM reserva 128 puertos de origen. Es el parámetro que causa el fallo más típico de Cloud NAT: si una máquina abre muchas conexiones salientes simultáneas (por ejemplo, un trabajo que descarga miles de ficheros) y se queda sin puertos, las conexiones nuevas fallan con un error confuso. El síntoma se ve en las métricas comonat_allocation_failed.--log-filter=ERRORS_ONLY: registra solo los fallos.ALLgenera un volumen de logs considerable y coste asociado.
Verificación desde una VM sin IP pública:
gcloud compute ssh alpinashop-web-1 --zone=europe-west1-b --tunnel-through-iap
# Dentro de la VM: la IP de salida debe ser la de alpinashop-nat-ip
curl -s https://ifconfig.me
- Private Google Access: llegar a las APIs sin salir a internet
Cuando una VM sin IP pública llama a storage.googleapis.com para leer del bucket alpinashop-catalogo, ¿por dónde va ese tráfico? Hay dos respuestas posibles, y la buena no es la evidente.
- Sin Private Google Access: la petición sale por Cloud NAT, va a internet, resuelve una IP pública de Google y vuelve. Funciona, pero sale de la red de Google, consume capacidad de NAT y se factura como tráfico de salida.
- Con Private Google Access activado en la subred: la petición se enruta internamente hacia los frontales de las APIs de Google. No pasa por internet, no consume puertos de NAT y no se factura como egress a internet.
Ya lo activamos al crear las subredes con --enable-private-ip-google-access. Si necesitas activarlo después:
gcloud compute networks subnets update sn-web-euw1 \
--region=europe-west1 \
--enable-private-ip-google-access
gcloud compute networks subnets describe sn-datos-euw1 \
--region=europe-west1 \
--format="value(privateIpGoogleAccess)"Es una opción que debe estar activada siempre, en todas las subredes, en todos los entornos. No tiene coste, no tiene contraindicación y afecta a casi todo lo que hará AlpinaShop: leer el bucket, escribir en BigQuery (módulo 4), leer un secreto de Secret Manager (03-06) o descargar una imagen de Artifact Registry.
Existe una variante, Private Service Connect, que permite alcanzar APIs de Google o servicios de terceros por una IP privada tuya, con control de DNS. Es una pieza de arquitecturas más grandes y se estudia en 07-03.
- Acceso privado a servicios: Cloud SQL por IP privada
En 02-03 creamos alpinashop-pedidos con IP pública y nos conectamos con el Auth Proxy. Funcionaba, pero la instancia era alcanzable desde internet (aunque protegida por redes autorizadas y credenciales). Ahora la movemos a IP privada, que es la configuración correcta.
El mecanismo se llama acceso privado a servicios y consiste en esto: cedes un rango de tu VPC a Google, Google crea allí los recursos gestionados (la instancia de Cloud SQL) y establece un emparejamiento entre tu red y la suya. Desde tu VPC, la base de datos aparece con una IP normal de ese rango.
flowchart LR
subgraph VPC["alpinashop-vpc"]
VM["alpinashop-web-1<br/>10.10.0.5"]
RES["Rango reservado<br/>10.30.0.0/20"]
end
subgraph GOOG["Red de servicios gestionados de Google"]
SQL[(alpinashop-pedidos<br/>10.30.0.3)]
end
VM -->|5432 por IP privada| RES
RES -.emparejamiento de redes.-> GOOG
GOOG --> SQL
Implementación, paso a paso:
# 1) Habilitar la API de networking de servicios
gcloud services enable servicenetworking.googleapis.com --project=alpinashop-prod
# 2) Reservar el rango que cedemos a Google (el del plan: 10.30.0.0/20)
gcloud compute addresses create alpinashop-psa-range \
--global \
--purpose=VPC_PEERING \
--addresses=10.30.0.0 \
--prefix-length=20 \
--network=alpinashop-vpc \
--description="Rango para servicios gestionados (Cloud SQL)"
# 3) Establecer la conexión privada
gcloud services vpc-peerings connect \
--service=servicenetworking.googleapis.com \
--ranges=alpinashop-psa-range \
--network=alpinashop-vpc \
--project=alpinashop-prod
# 4) Comprobar
gcloud services vpc-peerings list \
--network=alpinashop-vpc \
--project=alpinashop-prodCon la conexión establecida, se asigna la IP privada a la instancia y se retira la pública:
# Asignar IP privada dentro de alpinashop-vpc
gcloud sql instances patch alpinashop-pedidos \
--project=alpinashop-prod \
--network=projects/alpinashop-prod/global/networks/alpinashop-vpc \
--no-assign-ip
# Ver la IP privada resultante
gcloud sql instances describe alpinashop-pedidos \
--format="value(ipAddresses[].ipAddress, ipAddresses[].type)"Detalles que evitan sustos:
--no-assign-ipretira la IP pública. Hazlo solo cuando hayas verificado que la conexión privada funciona desde una VM; si no, te quedas sin acceso hasta revertirlo.- El rango cedido no se puede reducir después sin recrear la conexión. Un
/20da margen para instancias futuras (la réplicaalpinashop-pedidos-replica-informestambién consume direcciones de ahí). - La cadena de conexión de la aplicación Flask cambia de la IP pública a la privada; el Auth Proxy sigue siendo válido y sigue siendo la opción recomendada porque añade cifrado y autenticación IAM, pero ya no es la única forma de llegar.
- El cambio de red de una instancia de Cloud SQL implica reinicio. Planifícalo fuera de horario comercial.
- VPC Flow Logs: diagnóstico y auditoría
Los registros de flujo graban una muestra de las conexiones que atraviesan las interfaces de las VM: origen, destino, puertos, bytes, paquetes, y si fueron permitidas o denegadas. Sirven para tres cosas:
- Diagnosticar por qué algo no conecta (¿llegó el paquete? ¿lo denegó una regla?).
- Auditar: ver a qué destinos externos habla realmente tu infraestructura.
- Analizar coste de tráfico entre zonas y regiones.
Ya los activamos al crear las subredes. Una consulta típica en Cloud Logging (el servicio se estudia a fondo en 06-06; aquí lo usamos como herramienta):
# Conexiones denegadas hacia la capa web en la última hora
gcloud logging read '
resource.type="gce_subnetwork"
AND log_id("compute.googleapis.com/firewall")
AND jsonPayload.disposition="DENIED"
' --project=alpinashop-prod --limit=20 --freshness=1h \
--format="table(
timestamp,
jsonPayload.connection.src_ip,
jsonPayload.connection.dest_ip,
jsonPayload.connection.dest_port,
jsonPayload.rule_details.reference
)"# Flujos con más bytes salientes: ¿quién está generando tráfico?
gcloud logging read '
resource.type="gce_subnetwork"
AND log_id("compute.googleapis.com/vpc_flows")
' --project=alpinashop-prod --limit=10 --freshness=30m \
--format="table(
jsonPayload.connection.src_ip,
jsonPayload.connection.dest_ip,
jsonPayload.bytes_sent
)"Consideraciones de coste y privacidad:
- Los flow logs se facturan por volumen de logs ingeridos. En una red con tráfico intenso, el muestreo al 100 % puede ser caro.
0.5es un punto de partida razonable;0.1para tráfico muy alto. - Contienen direcciones IP de clientes, que en la Unión Europea son dato personal. Fija una política de retención acorde con el RGPD y documenta el tratamiento. Esta es una de esas decisiones que debe validar el responsable de cumplimiento antes de producción.
Además de los flow logs, para diagnóstico puntual está Network Intelligence Center y su herramienta de pruebas de conectividad, que simula un paquete y te dice exactamente qué regla lo permitió o denegó, sin generar tráfico real:
gcloud network-management connectivity-tests create prueba-web-a-sql \
--source-instance=projects/alpinashop-prod/zones/europe-west1-b/instances/alpinashop-web-1 \
--destination-cloud-sql-instance=projects/alpinashop-prod/instances/alpinashop-pedidos \
--destination-port=5432 \
--protocol=TCP
gcloud network-management connectivity-tests describe prueba-web-a-sql \
--format="value(reachabilityDetails.result)"
- Qué queda fuera de esta lección
Para que no te quede la sensación de hueco, esto es lo que existe y no hemos tratado aquí:
| Tema | Dónde se ve |
|---|---|
| VPC compartida entre proyectos | 07-03 |
| Emparejamiento (peering) entre VPC propias | 07-03 |
| Cloud VPN, Interconnect, conectividad híbrida | 07-03 |
| Private Service Connect a fondo | 07-03 |
| Balanceo de carga | 03-02 (la siguiente) |
| Cloud Armor y protección DDoS | 03-05 |
| Permisos de IAP y de red | 03-04 |
| Cloud Logging a fondo | 06-06 |
| Organization policies de red (p. ej. prohibir IP públicas) | 07-07 |
Errores Comunes y Consejos
- Usar la red
defaulten producción. Trae subredes en todas las regiones y reglas que abren SSH y RDP a internet. Crea una red personalizada desde el principio y borra ladefaultcuando ya no queden recursos dentro. - Abrir
tcp:22a0.0.0.0/0. El error clásico. Usa IAP con la regla desde35.235.240.0/20y quita las IP públicas de las VM. - Olvidar los rangos del balanceador. Si no permites
130.211.0.0/22y35.191.0.0/16, las comprobaciones de estado fallan, el backend se marca no saludable y el balanceador devuelve 502 sin explicación clara. Es, con diferencia, el fallo número uno de la próxima lección. - Elegir rangos "cómodos" como
10.0.0.0/16o192.168.1.0/24. Chocarán el día de la VPN. Planifica y documenta el direccionamiento antes de crear nada. - Creer que las subredes son zonales. Son regionales. No necesitas una subred por zona; una subred regional sirve a un MIG regional en tres zonas.
- Confundir prioridad alta con número alto. En el firewall de VPC gana el número menor. Una regla
allowcon prioridad 100 vence a unadenycon prioridad 1000. - Dejar IP externas estáticas reservadas sin usar. Se facturan, y a tarifa superior. Audita
gcloud compute addresses list --filter="status=RESERVED"cada mes. - No activar Private Google Access. Sin él, todo el tráfico a las APIs de Google sale por NAT a internet: más coste, más latencia y agotamiento de puertos de NAT.
- Retirar la IP pública de Cloud SQL antes de comprobar la privada. Verifica primero la conectividad privada desde una VM y después aplica
--no-assign-ip. - Dimensionar mal
min-ports-per-vmen Cloud NAT. Un trabajo por lotes con muchas conexiones salientes agota los puertos y provoca fallos intermitentes difíciles de atribuir. Vigila la métrica de fallos de asignación. - Confiar solo en el firewall. El firewall de VPC controla el nivel de red. La autenticación, la autorización y la validación de entrada siguen siendo responsabilidad de la aplicación.
Ejercicios
Ejercicio 1 — Plan de direccionamiento para alpinashop-dev
Marta necesita replicar la red en el proyecto alpinashop-dev. El requisito es que los rangos no solapen con producción (10.10.0.0/16, secundarios 10.20.0.0/16 y 10.21.0.0/20, acceso privado a servicios 10.30.0.0/20) ni con la oficina (192.168.10.0/24), porque en el futuro se conectarán ambos entornos.
Diseña el plan (red, dos subredes en europe-west1, rangos secundarios de GKE y rango para servicios gestionados) y escribe los comandos gcloud que lo crean, con Private Google Access activado.
Ejercicio 2 — Reglas de firewall para el servicio interno de informes
Lucía necesita una VM de informes llamada alpinashop-informes-1 en sn-datos-euw1, con la etiqueta informes, que cumpla:
- Solo Lucía y Marta pueden entrar por SSH, y únicamente a través de IAP.
- La VM debe poder consultar la réplica
alpinashop-pedidos-replica-informespor el puerto 5432. - La VM no debe recibir tráfico desde internet ni desde la capa web.
- La VM debe poder descargar paquetes de PyPI (salida a internet) sin tener IP pública.
Escribe las reglas de firewall necesarias y explica qué componente resuelve el punto 4.
Ejercicio 3 — Diagnóstico de un 502
Tras crear el balanceador (adelanto de la próxima lección), el catálogo devuelve 502 y en la consola los backends del MIG aparecen como UNHEALTHY. La aplicación responde correctamente si haces curl http://10.10.0.5:8080/salud desde otra VM de la misma subred.
Enumera las tres causas más probables por orden de probabilidad y el comando que usarías para confirmar o descartar cada una.
Soluciones
Solución 1
Plan propuesto (bloque 10.60.0.0/16 reservado en el documento de direccionamiento para desarrollo):
| Uso | Rango |
|---|---|
sn-web-euw1-dev (primario) |
10.60.0.0/24 |
sn-datos-euw1-dev (primario) |
10.60.1.0/24 |
Secundario gke-pods-dev |
10.70.0.0/16 |
Secundario gke-servicios-dev |
10.71.0.0/20 |
| Acceso privado a servicios | 10.80.0.0/20 |
Ninguno solapa con 10.10/16, 10.20/16, 10.21/20, 10.30/20 ni con 192.168.10.0/24.
gcloud config set project alpinashop-dev
gcloud compute networks create alpinashop-vpc-dev \
--subnet-mode=custom \
--bgp-routing-mode=global
gcloud compute networks subnets create sn-web-euw1-dev \
--network=alpinashop-vpc-dev \
--region=europe-west1 \
--range=10.60.0.0/24 \
--secondary-range=gke-pods-dev=10.70.0.0/16,gke-servicios-dev=10.71.0.0/20 \
--enable-private-ip-google-access
gcloud compute networks subnets create sn-datos-euw1-dev \
--network=alpinashop-vpc-dev \
--region=europe-west1 \
--range=10.60.1.0/24 \
--enable-private-ip-google-access
gcloud compute addresses create alpinashop-psa-range-dev \
--global --purpose=VPC_PEERING \
--addresses=10.80.0.0 --prefix-length=20 \
--network=alpinashop-vpc-devNota: los rangos secundarios se pueden dar en la creación con --secondary-range o añadirse después con --add-secondary-ranges.
Solución 2
# (1) SSH solo por IAP, aplicado a la VM de informes
gcloud compute firewall-rules create fw-allow-ssh-iap-informes \
--network=alpinashop-vpc \
--direction=INGRESS --action=allow --priority=1000 \
--source-ranges=35.235.240.0/20 \
--target-tags=informes \
--rules=tcp:22
# (2) La replica de Cloud SQL vive en el rango de acceso privado a
# servicios; el trafico saliente esta permitido por la regla
# implicita de EGRESS, asi que no hace falta regla de entrada.
# Si se quisiera restringir la salida de forma explicita:
gcloud compute firewall-rules create fw-egress-informes-sql \
--network=alpinashop-vpc \
--direction=EGRESS --action=allow --priority=900 \
--destination-ranges=10.30.0.0/20 \
--target-tags=informes \
--rules=tcp:5432
# (3) No se crea ninguna regla de entrada desde internet ni desde la
# capa web. La regla implicita "denegar toda la entrada" y la
# explicita fw-deny-all-ingress (prioridad 65000) ya lo cubren.
# Verificacion de que ninguna regla existente aplica a la etiqueta:
gcloud compute firewall-rules list \
--filter="network:alpinashop-vpc AND targetTags:informes" \
--format="table(name, direction, priority, sourceRanges.list())"Punto 4: lo resuelve Cloud NAT. La pasarela alpinashop-nat-euw1 ya cubre sn-datos-euw1, de modo que la VM, sin IP pública, sale a internet para instalar desde PyPI. Además, para descargar del bucket alpinashop-catalogo o escribir en BigQuery no usará NAT, sino Private Google Access, que está activo en la subred.
En cuanto a "solo Lucía y Marta": el firewall no distingue personas. Esa parte se resuelve con IAM dando roles/iap.tunnelResourceAccessor y roles/compute.osLogin únicamente a los grupos correspondientes; se ve en 03-04.
Solución 3
Por orden de probabilidad:
- Falta la regla de firewall para los rangos de comprobación de estado. Es la causa más frecuente con diferencia. La comprobación se origina en
130.211.0.0/22y35.191.0.0/16, no en internet ni en la subred.
gcloud compute firewall-rules list \
--filter="network:alpinashop-vpc AND sourceRanges:(130.211.0.0/22 OR 35.191.0.0/16)" \
--format="table(name, allowed[].map().firewall_rule().list(), targetTags.list())"- La etiqueta de red no coincide. La regla apunta a
web-catalogopero la plantilla de instancia del MIG no aplica esa etiqueta a las máquinas nuevas.
gcloud compute instances list --filter="name~alpinashop-web-mig" \
--format="table(name, zone, tags.items.list(), status)"- La comprobación apunta a un puerto o ruta equivocados. La aplicación escucha en 8080 y expone
/salud, pero la comprobación está configurada en el 80 o en/.
gcloud compute health-checks describe hc-catalogo \
--format="yaml(type, httpHealthCheck.port, httpHealthCheck.requestPath)"Herramienta transversal: los logs de firewall de fw-deny-all-ingress mostrarán los intentos denegados desde 35.191.0.0/16 si la causa es la primera, y la prueba de conectividad de Network Intelligence Center confirma la regla exacta que bloquea.
Conclusión
AlpinaShop ya tiene una red de verdad. Has entendido la propiedad que define la red de Google —la VPC es global, las subredes son regionales— y por qué eso simplifica arquitecturas que en otros proveedores exigen malabares. Has descartado la red default con argumentos y has creado alpinashop-vpc en modo personalizado, con enrutamiento global y un plan de direccionamiento documentado que reserva 10.10.0.0/16 para producción, deja hueco para crecer y no choca con la oficina ni con alpinashop-dev.
Sobre esa red has creado sn-web-euw1 (10.10.0.0/24) y sn-datos-euw1 (10.10.1.0/24), con rangos secundarios gke-pods (10.20.0.0/16) y gke-servicios (10.21.0.0/20) para que alpinashop-cluster use red nativa de VPC y sus pods tengan direcciones reales. Has reservado la IP global alpinashop-lb-ip para el balanceador que construimos ahora mismo y la IP alpinashop-nat-ip para que la pasarela de pago pueda autorizar una dirección de salida estable.
Has aprendido el modelo de firewall completo —stateful, distribuido, prioridad donde gana el número menor, cuatro reglas implícitas, selección por etiqueta o por cuenta de servicio— y has escrito el conjunto real de AlpinaShop: HTTP y comprobaciones de estado solo desde 130.211.0.0/22 y 35.191.0.0/16, SSH solo desde el rango de IAP 35.235.240.0/20, PostgreSQL solo desde la cuenta de servicio sa-catalogo-web, y una denegación explícita con registro que te enseña qué está llamando a la puerta. Y sabes por qué 0.0.0.0/0 en el 22 no es una comodidad sino un incidente pendiente.
Por último, las máquinas ya no necesitan IP pública: Cloud NAT les da salida controlada con una IP estática conocida, Private Google Access las lleva a las APIs de Google sin pisar internet, el acceso privado a servicios ha puesto alpinashop-pedidos en una IP privada del rango 10.30.0.0/20, y los VPC Flow Logs convierten los problemas de red en consultas de log en lugar de conjeturas.
La red está lista, pero el catálogo todavía se sirve por la IP de una máquina. En la siguiente lección, 03-02, Balanceo de carga en la nube, ponemos delante un balanceador HTTP(S) externo global: una única dirección anycast que atiende desde el punto de presencia más cercano a cada cliente europeo, reparte el tráfico entre las instancias del MIG alpinashop-web-mig, sirve las imágenes directamente desde el bucket alpinashop-catalogo mediante un mapa de URL, y comprueba la salud de los backends por esa ruta /salud que ya hemos autorizado en el firewall. Todas las piezas de red que acabamos de colocar existen precisamente para que ese balanceador funcione a la primera.
Curso de Google Cloud Platform (GCP)
Módulo 1: Introducción a Google Cloud Platform
- ¿Qué es Google Cloud Platform?
- Configuración de tu cuenta de GCP
- Descripción general de la consola de GCP
- Proyectos, jerarquía de recursos y facturación
- Regiones, zonas y modelo de responsabilidad compartida
- Cloud Shell y la CLI de gcloud
Módulo 2: Servicios principales de GCP
- Compute Engine: máquinas virtuales en Google Cloud
- Cloud Storage: almacenamiento de objetos
- Cloud SQL: bases de datos relacionales gestionadas
- App Engine: plataforma como servicio
- Google Kubernetes Engine (GKE)
- Bases de datos NoSQL: Firestore, Bigtable y Spanner
- Cómo elegir el servicio de cómputo adecuado
Módulo 3: Redes y seguridad
- Redes VPC
- Balanceo de carga en la nube
- Cloud CDN
- Gestión de identidad y acceso (IAM)
- Cloud Armor
- Secretos y cifrado: Secret Manager y Cloud KMS
- Cloud DNS, certificados TLS y publicación segura de servicios
Módulo 4: Datos y análisis
- BigQuery: el almacén de datos analítico
- Cloud Dataflow: procesamiento de datos por lotes y en streaming
- Cloud Dataproc: Spark y Hadoop gestionados
- Cloud Pub/Sub: mensajería asíncrona
- Cloud Data Fusion: integración de datos sin código
- Orquestación de pipelines con Cloud Composer y Workflows
- Gobierno del dato y cuadros de mando con Dataplex y Looker Studio
Módulo 5: Aprendizaje automático e IA
- Vertex AI: la plataforma de machine learning de GCP
- AutoML: modelos a medida sin escribir código
- TensorFlow en GCP: entrenamiento y servicio de modelos
- API de lenguaje natural
- API de visión
- IA generativa en Vertex AI: modelos Gemini y embeddings
- MLOps: del modelo al producto con Vertex AI Pipelines
Módulo 6: DevOps y monitoreo
- Cloud Build: integración continua en GCP
- Cloud Source Repositories y gestión del código fuente
- Cloud Functions: funciones sin servidor
- Cloud Monitoring (antes Stackdriver): métricas, paneles y alertas
- Cloud Deployment Manager e infraestructura como código nativa
- Cloud Logging y Cloud Trace: logs, trazas y diagnóstico
- Terraform en GCP: infraestructura como código en la práctica
Módulo 7: Temas avanzados de GCP
- Híbrido y multinube con Anthos
- Computación sin servidor con Cloud Run
- Redes avanzadas: VPC compartida, peering y conectividad híbrida
- Mejores prácticas de seguridad
- Gestión y optimización de costos
- Fiabilidad: SLO, alta disponibilidad y recuperación ante desastres
- Gobierno a escala: organización, políticas y auditoría
