Weg vom VPN, hin zu einer modernen Zero-Trust-Lösung für interne HTTPS-Anwendungen
Verwalten Sie immer noch VPN-Verbindungen, damit Ihre Remote-Mitarbeitenden auf interne GKE-Anwendungen (Google Kubernetes Engine) zugreifen können? Das geht deutlich eleganter. In diesem Beitrag zeigen wir, wie Sie den klassischen VPN-Zugriff durch den Identity-Aware Proxy (IAP) von Google Cloud in Kombination mit einem GKE External Gateway ablösen. Dieser Ansatz senkt nicht nur den Betriebsaufwand, sondern bringt Ihnen über IAM-Authentifizierung und -Autorisierung auch eine deutlich feingranularere Zugriffssteuerung.
Warum sich der Wechsel lohnt
Der klassische VPN-Zugriff auf interne Anwendungen bringt einige Hürden mit sich:
- Hoher Betriebsaufwand für die Verwaltung der VPN-Tunnel
- Zusätzliche Kosten für die VPN-Infrastruktur
- Komplexes Netzwerk-Routing
- Nur grobe Zugriffssteuerung möglich
Mit einem IAP-fähigen External Gateway dagegen können Sie:
- mehrere Anwendungen hinter einem einzigen HTTPS-Load-Balancer bündeln
- Zugriffe über verschiedene GKE-Namespaces hinweg effizient steuern
- das robuste Authentifizierungssystem von Google Cloud nutzen
- Infrastrukturkosten und Komplexität senken
GKE Ingress vs. Gateway: IAP im Vergleich
Für IAP mit GKE gibt es zwei Wege: GKE Ingress oder GKE Gateway. Wer die Unterschiede kennt, trifft die richtige Wahl für die eigene Umgebung.
Grenzen von GKE Ingress
- Namespace-Beschränkung: GKE Ingress kann nur Services aus demselben Namespace referenzieren, in dem die Ingress-Ressource bereitgestellt ist:

GKE Ingress im Namespace Fu kann keinen Service im Namespace Bar referenzieren
- Load-Balancer-Ressourcen: Verteilen sich Anwendungen auf mehrere Namespaces, sind unter Umständen mehrere Load Balancer nötig:

Bei Services in unterschiedlichen Namespaces braucht es zwei separate HTTPS-Load-Balancer – plus domainbasiertes Routing, was die Sache zusätzlich verkompliziert
Vorteile von GKE Gateway
- Cross-Namespace-Support: Eine einzige Gateway-Ressource leitet Traffic an Services in beliebigen Namespaces
- Schlankere Architektur: Ein Load Balancer bedient mehrere Anwendungen – unabhängig vom Namespace
- Flexibleres Routing: Komplexe Routing-Muster über Namespaces hinweg sind deutlich einfacher umzusetzen

Ein GKE Gateway in einem Namespace kann mit nur einem HTTPS-Load-Balancer Traffic an Services in anderen Namespaces senden
Schritt-für-Schritt-Anleitung
Voraussetzungen
- GKE Standard v1.24+ oder GKE Autopilot v1.26+
- VPC-native Cluster-Konfiguration
- HttpLoadBalancing-Add-on aktiviert
Schritt 1: GKE-Cluster anlegen
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
Schritt 2: SSL-Zertifikat konfigurieren
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
Schritt 3: Externe IP einrichten und einen DNS-A-Record darauf zeigen lassen
Damit das ManagedCertificate bereitgestellt werden kann, muss ein DNS-A-Record auf den HTTPS-Load-Balancer verweisen und das Zertifikat am HTTPS-Load-Balancer hinterlegt sein (Letzteres erledigen wir im nächsten Schritt).
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
Schritt 4: Gateway-Ressource bereitstellen
Wichtig: Der gatewayClassName steht für einen Global External HTTPS Load Balancer, und das listeners-Array erlaubt das Anhängen von HTTPRoutes aus jedem beliebigen Namespace. Die reservierte IP setzen wir über addresses[0].type als NamedAddresses – mit dem Namen, den wir bei der Reservierung der statischen IP vergeben haben (in diesem Beispiel "gateway"):
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
# Gateway-IP-Zuweisung prüfen
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
Schritt 5: ManagedCertificate-Status prüfen
# Zertifikatsstatus prüfen
while true; do
STATUS=$(kubectl get managedcertificate gateway-nir-dns-tests-google-com -o=jsonpath="{.status.certificateStatus}")
if [ "$STATUS" = "Active" ]; then
break
fi
echo "Certificate is in $STATUS status, will check again if it became active in 3 seconds"
sleep 3
done
Schritt 6: Beispielanwendungen bereitstellen
kubectl create namespace first-namespace
kubectl create namespace second-namespace
# App A im first-namespace bereitstellen
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
# Service für App A anlegen
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
# App B im second-namespace bereitstellen
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
# Service für App B anlegen
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
Schritt 7: IAP aktivieren und ein k8s-Secret für die IAP-Credentials anlegen
echo -n CLIENT_SECRET_REDACTED > iap-secret.txt
kubectl create secret generic iap --from-file=key=iap-secret.txt
Schritt 8: GCPBackendPolicy zur Erzwingung von IAP anlegen
# IAP für App A aktivieren
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
# IAP für App B aktivieren
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
Schritt 9: HTTPRoute anlegen
# Route für App A anlegen
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
# Route für App B anlegen
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
Schritt 10: Die IAP-geschützten Apps aufrufen
Rufen Sie die IAP-geschützte URL auf. Sie landen auf der Google-Anmeldeseite. Zugriff erhält jeder Nutzer, der zur selben Google-Cloud-Organisation gehört wie die Anwendung und dem die IAM-Rolle IAP-Secured Web App User zugewiesen ist:

[email protected] ist Nutzer in der GCP-Organisation doit.com und besitzt die nötigen IAM-Berechtigungen für die Anwendung. [email protected] gehört nicht zu dieser Organisation und hat daher keinen Zugriff.
Versucht ein Nutzer außerhalb der Organisation oder ohne entsprechende Berechtigung, sich anzumelden und die Anwendung zu öffnen, erscheint eine Fehlerseite mit verweigerten Berechtigungen:

Fehlerseite mit verweigerten Berechtigungen für nicht autorisierte Principals
Der Schritt vom VPN zum IAP-fähigen External Gateway ist nur eine von vielen Möglichkeiten, die Sicherheit und Effizienz Ihrer Google-Cloud-Infrastruktur zu steigern. Sie verbessern damit nicht nur Ihre Security-Posture, sondern senken zugleich Betriebsaufwand und Kosten.
Dieser Leitfaden behandelt zwar gezielt die Modernisierung des GKE-Zugriffs, doch ähnliche Potenziale schlummern in praktisch jedem Bereich Ihrer Cloud-Infrastruktur. Ob Kostenoptimierung, Infrastrukturautomatisierung, Security-Hardening oder Cloud-Architektur – DoiT International bringt fundierte Expertise in zahlreichen Cloud-Disziplinen mit. Wenn Sie wissen möchten, wie DoiT auch andere Bereiche Ihrer Cloud-Infrastruktur modernisieren kann, oder mehr über unsere Cloud-Engineering-Lösungen erfahren möchten, schauen Sie auf doit.com/expertise/ vorbei.