Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Schluss mit VPN: Interner GKE-Zugriff über IAP-Gateway

By Nir ForerJan 13, 20256 min read

Diese Seite ist auch in English, Español, Français, Italiano, 日本語 und Português verfügbar.

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.