Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Moderniza el acceso a tus apps internas de GKE: de la VPN a un Gateway externo con IAP

By Nir ForerJan 13, 20256 min read

Esta página también está disponible en English, Deutsch, Français, Italiano, 日本語 y Português.

Deja atrás la VPN y adopta una solución Zero Trust moderna para tus aplicaciones HTTPS internas

¿Sigues administrando conexiones VPN para que tu equipo remoto acceda a las aplicaciones internas de GKE (Google Kubernetes Engine)? Hay una mejor forma de hacerlo. En este post te mostramos cómo reemplazar el acceso tradicional por VPN con el Identity-Aware Proxy (IAP) de Google Cloud, usando un GKE External Gateway. Este enfoque moderno no solo reduce la carga operativa, sino que además ofrece un control de acceso más granular gracias a la autenticación y autorización vía IAM.

¿Por qué dar el salto?

El acceso tradicional por VPN a aplicaciones internas trae varios problemas:

  • Alta carga operativa para administrar túneles VPN
  • Costos adicionales por la infraestructura de VPN
  • Configuraciones de enrutamiento de red complejas
  • Control de acceso granular limitado

Al implementar un External Gateway con IAP, puedes:

  • Consolidar varias aplicaciones detrás de un único load balancer HTTPS
  • Administrar el acceso entre distintos namespaces de GKE de forma eficiente
  • Apoyarte en el robusto sistema de autenticación de Google Cloud
  • Reducir costos y complejidad de la infraestructura

GKE Ingress vs. Gateway: comparativa de implementación con IAP

Al implementar IAP en GKE tienes dos caminos principales: usar GKE Ingress o GKE Gateway. Entender en qué se diferencian es clave para elegir la solución adecuada para tu entorno.

Limitaciones de GKE Ingress

  • Restricciones de namespace: GKE Ingress solo puede referenciar Services dentro del mismo namespace en el que se desplegó el recurso Ingress:

Un GKE Ingress en el namespace Fu no puede referenciar un Service del namespace Bar

  • Recursos de load balancer: puede requerir varios load balancers cuando las aplicaciones están en distintos namespaces:

Hay que usar 2 load balancers HTTPS distintos cuando los Services están en namespaces diferentes; además, se debe recurrir al enrutamiento por dominio, lo que añade otra capa de complejidad

Ventajas de GKE Gateway

  • Soporte cross-namespace: enruta tráfico hacia Services en distintos namespaces desde un único recurso Gateway
  • Arquitectura simplificada: un solo load balancer puede atender varias aplicaciones, sin importar su namespace
  • Enrutamiento más flexible: facilita implementar patrones de enrutamiento complejos entre namespaces

Un GKE Gateway en un namespace puede enviar tráfico a Services en otros namespaces con un único load balancer HTTPS

Guía de implementación

Requisitos previos

  • GKE Standard v1.24+ o GKE Autopilot v1.26+
  • Configuración de cluster VPC-native
  • Add-on HttpLoadBalancing habilitado

Paso 1: crea el cluster de GKE

gcloud container clusters create test-gateway \
 --region=us-central1 \
 --release-channel=regular \
 --enable-ip-alias \
 --num-nodes=1 \
 --addons HttpLoadBalancing \
 --gateway-api=standard

gcloud container clusters get-credentials test-gateway --region us-central1

Paso 2: configura el certificado SSL

cat <<EOF | kubectl apply -f -
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
 name: gateway-nir-dns-tests-google-com
 namespace: default
spec:
 domains:
 - gateway.nir-dns-tests-google-com
EOF

Paso 3: configura la IP externa y apunta un registro DNS A hacia ella

Para que se aprovisione el ManagedCertificate, debes tener un registro DNS A apuntando al load balancer HTTPS y el certificado adjuntado al load balancer HTTPS (esto último se hace en el siguiente paso)

gcloud compute addresses create gateway --project=nir-playground --global
RESERVED_IP=$(gcloud compute addresses describe gateway --global | grep address: | awk '{print $2}')

gcloud dns --project=nir-playground record-sets create gateway.nir-dns-tests-google-com. \
  --zone="nir-dns-tests-google-com" --type="A" --ttl="60" --rrdatas=$RESERVED_IP

Paso 4: despliega el recurso Gateway

Fíjate en que gatewayClassName representa un load balancer HTTPS Global External y en que el array de listeners permite adjuntar HTTPRoutes desde cualquier namespace. Nota también que asignamos la IP reservada con addresses[0].type en NamedAddresses y, como valor, el nombre que usamos al reservar la IP estática ("gateway" en este ejemplo):

CERT_NAME=$(kubectl get managedcertificate gateway-nir-dns-tests-google-com -o=jsonpath="{.status.certificateName}")

cat <<EOF | kubectl apply -f -
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1beta1
metadata:
 name: external-http
spec:
 gatewayClassName: gke-l7-global-external-managed
 listeners:
 - name: https
   protocol: HTTPS
   port: 443
   allowedRoutes:
     namespaces:
       from: All
   tls:
     mode: Terminate
     options:
       networking.gke.io/pre-shared-certs: $CERT_NAME
 addresses:
 - type: NamedAddress
   value: gateway
EOF

# Verificar la asignación de IP del Gateway
while true; do
 GATEWAY_IP=$(kubectl get gateway external-http -o=jsonpath="{.status.addresses[0].value}")
 if [ "$GATEWAY_IP" != "" ]; then
   break
 fi
 sleep 3
done

Paso 5: verifica el estado del ManagedCertificate

# Verificar el estado del certificado
while true; do
 STATUS=$(kubectl get managedcertificate gateway-nir-dns-tests-google-com -o=jsonpath="{.status.certificateStatus}")
 if [ "$STATUS" = "Active" ]; then
   break
 fi
 echo "El certificado está en estado $STATUS; se volverá a verificar en 3 segundos para ver si pasó a activo"
 sleep 3
done

Paso 6: despliega las aplicaciones de ejemplo

kubectl create namespace first-namespace
kubectl create namespace second-namespace

# Desplegar App A en first-namespace
cat <<EOF | kubectl apply -n first-namespace -f -
apiVersion: apps/v1
kind: Deployment
metadata:
 name: app-a
spec:
 replicas: 2
 selector:
   matchLabels:
     app: app-a
 template:
   metadata:
     labels:
       app: app-a
   spec:
     containers:
     - name: app-a
       image: hashicorp/http-echo
       args: ["-text=Hello from App A"]
       ports:
       - containerPort: 5678
EOF

# Crear Service para App A
cat <<EOF | kubectl apply -n first-namespace -f -
apiVersion: v1
kind: Service
metadata:
 name: service-a
spec:
 selector:
   app: app-a
 ports:
 - protocol: TCP
   port: 80
   targetPort: 5678
EOF

# Desplegar App B en second-namespace
cat <<EOF | kubectl apply -n second-namespace -f -
apiVersion: apps/v1
kind: Deployment
metadata:
 name: app-b
spec:
 replicas: 2
 selector:
   matchLabels:
     app: app-b
 template:
   metadata:
     labels:
       app: app-b
   spec:
     containers:
     - name: app-b
       image: hashicorp/http-echo
       args: ["-text=Hello from App B"]
       ports:
       - containerPort: 5678
EOF

# Crear Service para App B
cat <<EOF | kubectl apply -n second-namespace -f -
apiVersion: v1
kind: Service
metadata:
 name: service-b
spec:
 selector:
   app: app-b
 ports:
 - protocol: TCP
   port: 80
   targetPort: 5678
EOF

Paso 7: habilita IAP y crea un secret de k8s con las credenciales de IAP

echo -n CLIENT_SECRET_REDACTED > iap-secret.txt
kubectl create secret generic iap --from-file=key=iap-secret.txt

Paso 8: crea una GCPBackendPolicy que aplique IAP

# Habilitar IAP para App A
cat <<EOF | kubectl apply -n first-namespace -f -
apiVersion: networking.gke.io/v1
kind: GCPBackendPolicy
metadata:
 name: backend-policy
spec:
 default:
   iap:
     enabled: true
     oauth2ClientSecret:
       name: iap
     clientID: REPLACE_WITH_YOUR_IAP_OAUTH_CLIENT_ID
 targetRef:
   group: ""
   kind: Service
   name: service-a
EOF

# Habilitar IAP para App B
cat <<EOF | kubectl apply -n second-namespace -f -
apiVersion: networking.gke.io/v1
kind: GCPBackendPolicy
metadata:
 name: backend-policy
spec:
 default:
   iap:
     enabled: true
     oauth2ClientSecret:
       name: iap
     clientID: REPLACE_WITH_YOUR_IAP_OAUTH_CLIENT_ID
 targetRef:
   group: ""
   kind: Service
   name: service-b
EOF

Paso 9: crea el HTTPRoute

# Crear ruta para App A
cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
 name: route-a
 namespace: first-namespace
spec:
 parentRefs:
 - name: external-http
   kind: Gateway
   namespace: default
 rules:
 - matches:
   - path:
       value: /first
   backendRefs:
   - name: service-a
     port: 80
EOF

# Crear ruta para App B
cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
 name: route-b
 namespace: second-namespace
spec:
 parentRefs:
 - name: external-http
   kind: Gateway
   namespace: default
 rules:
 - matches:
   - path:
       value: /second
   backendRefs:
   - name: service-b
     port: 80
EOF

Paso 10: navega a las apps protegidas con IAP

Entra a la URL protegida por IAP. Verás la pantalla de inicio de sesión de Google. Cualquier usuario que pertenezca a la misma organización de Google Cloud donde está desplegada la aplicación y que tenga asignados los permisos IAM IAP-Secured Web App User podrá acceder:

[email protected] es un usuario de la organización de GCP doit.com y tiene asignados los permisos IAM necesarios para acceder a la aplicación. [email protected] no pertenece a esa organización, así que no podrá acceder

Cuando un usuario externo a la organización, o uno sin los permisos asignados, intenta iniciar sesión y entrar a la aplicación, verá una página de permiso denegado:

Página de permiso denegado que se muestra a los principals no autorizados

Modernizar el acceso a GKE pasando de la VPN a un External Gateway con IAP es solo una de las muchas formas de mejorar la seguridad y la eficiencia operativa de tu infraestructura en Google Cloud. Este enfoque no solo refuerza tu postura de seguridad, sino que además reduce la carga operativa y los costos.

Aunque esta guía se centra en modernizar el acceso a GKE, existen oportunidades similares de transformación en toda tu infraestructura cloud: desde la optimización de costos hasta la automatización de infraestructura, y desde el endurecimiento de la seguridad hasta el diseño de arquitectura cloud. DoiT International cuenta con amplia experiencia en múltiples dominios de la nube. Para descubrir cómo DoiT puede ayudarte a modernizar otros aspectos de tu infraestructura cloud, o para conocer nuestras demás soluciones de cloud engineering, visita doit.com/expertise/.