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/.