Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Vault High Availability auf GKE

By Ami MahloofDec 17, 201916 min read

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

1 fskpg2trcdatechlv lqzw

Secrets nativ in Kubernetes zu verwalten ist nicht sicher: Sie liegen lediglich als Base64-codierter Klartext vor, der in jedem laufenden Pod ausgelesen werden kann.

Disclaimer:

Dieses Tutorial führt Sie Schritt für Schritt durch jede Komponente des Setups. Sie können die Einrichtung selbstverständlich auch mit Terraform erledigen – wer ein derart kritisches Setup jedoch blind aufzieht, läuft in Probleme, die sich ohne fundiertes Verständnis nur schwer lösen lassen.

1 3th420mk8ylbebpjutjckw

Natürlich lässt sich ein Secret in Kubernetes verschlüsseln – allerdings nur im Ruhezustand. Sobald es im Pod gemountet ist, liegt es als Datei oder Umgebungsvariable vor und ist innerhalb des Pods problemlos einsehbar. Bei einem Sicherheitsvorfall sind die Daten kompromittiert, sobald jemand Zugriff auf den Pod hat – oder per kubectl auf den Namespace, in dem der Pod läuft.

HashiCorp Vault bietet einen sicheren Weg, Secrets zu verwalten sowie Zugriffe zu auditieren und zu widerrufen. Vault zu installieren und zu betreiben ist eine Sache – die Secrets dann auch in einem Pod nutzbar zu machen eine andere.

Dieser Beitrag zeigt die Installation von Vault auf GKE mit Terraform und Helm. Wie Sie die Secrets anschließend nutzen, lesen Sie in meinem weiteren Blogbeitrag zum transparenten Konsumieren von Secrets aus Vault in einem Pod.

Dieser Ansatz ist deutlich einfacher zu betreiben, wenn Sie nur Vault benötigen und auf erweiterte Consul-Funktionen wie das Consul-Template mit Vault verzichten können.

In diesem Tutorial zeige ich Ihnen, wie Sie ein hochverfügbares Vault mit Google Cloud Storage (GCS) als Backend und durchgängiger TLS-Verschlüsselung aufsetzen.

Hinweis:

Legen Sie das Vault-Setup pro Umgebung separat an, um Upgrades sauber testen zu können und die Umgebungen voneinander zu trennen.

Ich rate davon ab, Vault als Service nach außen freizugeben. Wenn Sie darauf zugreifen müssen, geht das per:

$ kubectl port-forward vault-0 8200:8200

Die UI erreichen Sie dann wie weiter unten beschrieben unter https://127.0.0.1:8200.

Müssen VMs auf Vault zugreifen, sollten Sie VPC-Peering einsetzen, da Services und Pods native IPs verwenden – das ist hier aber nicht Thema.

Tutorial:

Überblick:

  • TLS-Zertifikate für Vault erstellen
  • Einen GCS-Bucket als Vault-Storage-Backend anlegen
  • KMS-Keyring und Verschlüsselungsschlüssel für Vault Auto-Unseal anlegen
  • Service Accounts für Vault einrichten, damit es auf KMS und das GCS-Storage-Backend zugreifen kann
  • Das offizielle HashiCorp-Vault-Helm-Chart per Helm Tillerless installieren

TLS-Zertifikate für Vault erstellen:

0 y3nb5jrvforb rez

Eine der Empfehlungen für Production Hardening lautet, dass die Kommunikation zwischen Vault und seinen Clients sowohl ein- als auch ausgehend per TLS verschlüsselt sein muss.

Wir erstellen ein Zertifikat für:

  • die Kubernetes-Vault-Service-Adresse
  • 127.0.0.1

Zum Erzeugen der Zertifikate nutzen wir das CloudFlare SSL ToolKit (cfssl und cfssljson).

Die Installation setzt eine funktionierende Go-1.12+-Installation sowie einen korrekt gesetzten GOPATH voraus.

Wichtig: Stellen Sie sicher, dass das GOPATH-bin-Verzeichnis in Ihrem PATH liegt:

export PATH=$GOPATH/bin:$PATH

CloudFlare SSL ToolKit installieren:

go get -u github.com/cloudflare/cfssl/cmd/cfssl
go get -u github.com/cloudflare/cfssl/cmd/cfssljson

Eine Certificate Authority (CA) initialisieren:

$ mkdir vault-ca && cd vault-ca

Legen Sie die CA-Dateien an:

CA-Konfigurationsdatei mit einer Gültigkeit von 5 Jahren

$ cat <<EOF > ca-config.json
{
"signing": {
"default": {
"expiry": "8760h"
},
"profiles": {
"default": {
"usages": ["signing", "key encipherment", "server auth", "client auth"],
"expiry": "8760h"
}
}
}
}
EOF

CA Signing Request:

$ cat <<EOF > ca-csr.json
{
"hosts": [\
"cluster.local"\
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [\
{\
"C": "US",\
"L": "NewYork",\
"O": "Kubernetes",\
"OU": "CA",\
"ST": "NewYork"\
}\
]
}
EOF

VAULT Certificate Signing Request, der von der oben angelegten CA signiert wird: Hinweis: Passen Sie den Namespace für Vault an, falls es nicht der Default-Namespace ist.

$ cat <<EOF > vault-csr.json
{
"CN": "Vault-GKE",
"hosts": [\
"127.0.0.1",\
"vault.default.svc.cluster.local"\
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [\
{\
"C": "US",\
"L": "NewYork",\
"O": "Innovia",\
"OU": "Vault",\
"ST": "NewYork"\
}\
]
}
EOF

Die Zertifikatsangaben unter "names" können Sie nach Belieben anpassen.

Initialisieren Sie die CA mit der eben bearbeiteten Datei:

$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca
2019/11/12 16:35:01 [INFO] generating a new CA key and certificate from CSR
2019/11/12 16:35:01 [INFO] generate received request
2019/11/12 16:35:01 [INFO] received CSR
2019/11/12 16:35:01 [INFO] generating key: rsa-2048
2019/11/12 16:35:01 [INFO] encoded CSR
2019/11/12 16:35:01 [INFO] signed certificate with serial number 425581644650417483788325060652779897454211028144

Erzeugen Sie einen privaten Schlüssel und signieren Sie das TLS-Zertifikat:

$ cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=default \
vault-csr.json | cfssljson -bare vault
2019/11/12 16:36:33 [INFO] generate received request
2019/11/12 16:36:33 [INFO] received CSR
2019/11/12 16:36:33 [INFO] generating key: rsa-2048
2019/11/12 16:36:34 [INFO] encoded CSR
2019/11/12 16:36:34 [INFO] signed certificate with serial number 311973563616303179057952194819087555625015840298

Im aktuellen Arbeitsverzeichnis sollten nun folgende Dateien liegen:

ca-key.pem
ca.pem
vault-key.pem
vault.pem

Bewahren Sie die CA-Dateien sicher auf – Sie brauchen sie, um das Zertifikat nach Ablauf neu zu signieren (CA: 5 Jahre, Vault: 1 Jahr).

Legen Sie ein Secret für das Vault-TLS-Zertifikat und CA.pem an:

kubectl create secret generic vault-tls \
--from-file=ca.pem \
--from-file=vault.pem \
--from-file=vault-key.pem

Setzen Sie das GCP-Projekt für den weiteren Verlauf:

$ export GCP_PROJECT=<your_project_id>

Aktivieren Sie die für dieses Tutorial benötigten GCP-APIs:

$ gcloud services enable \
cloudapis.googleapis.com \
cloudkms.googleapis.com \
container.googleapis.com \
containerregistry.googleapis.com \
iam.googleapis.com \
--project ${GCP_PROJECT}
Operation "operations/acf.8e126724-bbde-4c0d-b516-5dca5b8443ee" finished successfully.

Vault Storage Backend

0 kgo4ymq8e12mvigcGoogle Cloud Storage

Im HA-Modus haben Vault-Server zwei zusätzliche Zustände: standby und active. Innerhalb eines Vault-Clusters ist nur eine einzige Instanz active und verarbeitet sämtliche Anfragen (Lese- und Schreibvorgänge); alle standby-Knoten leiten Anfragen an den active-Knoten weiter.

Legen Sie den Bucket auf GCS mit gsutil an. Bucket-Namen müssen in der gesamten Google Cloud global eindeutig sein – wählen Sie also einen einzigartigen Namen.

$ export GCS_BUCKET_NAME=mycompany-vault-data
$ gsutil mb gs://$GCS_BUCKET_NAME
$ gsutil versioning set on gs://$GCS_BUCKET_NAME

Auch wenn die Daten bei der Übertragung und im Ruhezustand verschlüsselt sind: Setzen Sie die Bucket-Berechtigungen restriktiv, um die Angriffsfläche klein zu halten. Sinnvoll ist ein Service Account, dessen IAM-Rechte Vaults Interaktion mit Google Cloud auf die Objekte im Storage-Bucket beschränken.

Vault Auto-Unseal

0 29ts5sy8qvx7cern

Nach einem Neustart befindet sich Vault zunächst im versiegelten und verschlüsselten Zustand. Vor der Nutzung muss es entsiegelt werden. Mit der Auto-Unseal-Funktion lassen sich Master Keys und Root-Token automatisch aus CloudKMS auslesen.

KMS-Keyring und Crypto Key anlegen:

In diesem Abschnitt legen wir das KMS-Keyring und den Schlüssel zum Ver- und Entschlüsseln der Vault-Master-Keys und des Root-Tokens an:

Das KMS-Keyring vault-helm-unseal-kr anlegen:

$ gcloud kms keyrings create vault-helm-unseal-kr \
--location global \
--project ${GCP_PROJECT}

Den Verschlüsselungsschlüssel anlegen:

$ gcloud kms keys create vault-helm-unseal-key \
--location global \
--keyring vault-helm-unseal-kr \
--purpose encryption \
--project ${GCP_PROJECT}

GCP Service Accounts und IAM-Berechtigungen für Vault einrichten

Variablen setzen:

$ export VAULT_SA_NAME=vault-server;
export VAULT_SA=$VAULT_SA_NAME@$GCP_PROJECT.iam.gserviceaccount.com

Den Vault-Server-Service-Account anlegen:

$ gcloud iam service-accounts create $VAULT_SA_NAME \
--display-name "Vault server service account" \
--project ${GCP_PROJECT}

Den Service-Account-Key des Vault-Servers (Credentials-JSON-Datei) erzeugen:

$ gcloud iam service-accounts keys create \
--iam-account $VAULT_SA /tmp/vault_gcs_key.json
created key [be22cfe6e30f3a3fcfc6ebaa23ca3ba905dd60ab] of type [json] as [/tmp/vault_gcs_key.json] for [[email protected]]
Das Secret anlegen, in dem der Google-Service-Account von Vault hinterlegt wird
$ kubectl create secret generic vault-gcs \
--from-file=/tmp/vault_gcs_key.json
secret/vault-gcs created

Zugriff auf den GCS-Bucket des Vault-Storage gewähren:

$ gsutil iam ch \
serviceAccount:${VAULT_SA}:objectAdmin \
gs://${GCS_BUCKET_NAME}

Zugriff auf den Vault-KMS-Schlüssel gewähren:

$ gcloud kms keys add-iam-policy-binding \
vault-helm-unseal-key \
--location global \
--keyring vault-helm-unseal-kr \
--member serviceAccount:${VAULT_SA} \
--role roles/cloudkms.cryptoKeyEncrypterDecrypter \
--project ${GCP_PROJECT}
Updated IAM policy for key [vault-helm-unseal-key].
bindings:
- members:
- serviceAccount:[email protected]
role: roles/cloudkms.cryptoKeyEncrypterDecrypter
etag: BwWZ6sIYovk=
version: 1
Hinweis:
Falls Sie den Service Account zwischenzeitlich gelöscht und neu angelegt haben, müssen Sie die IAM-Policy auf dem Schlüssel löschen. Andernfalls springen Sie weiter zu "Das offizielle HashiCorp-Vault-Chart abrufen".
$ gcloud kms keys get-iam-policy vault-helm-unseal-key --location global --keyring vault-helm-unseal-kr > kms-policy.yaml
Bearbeiten Sie die Policy-Datei, entfernen Sie die Members unter binding und speichern Sie die Datei
bindings:
etag: BwWXQz4HjuI=
version: 1

Wenden Sie die Policy erneut an:

$ gcloud kms keys set-iam-policy vault-helm-unseal-key --location global --keyring vault-helm-unseal-kr kms-policy.yaml

Das offizielle Vault-Chart von HashiCorp abrufen:

1 phvmhmqvoj6awvkgmrnhbg

Hinweis:

Ab Version 0.3.0 gibt es eine Kubernetes-Vault-Integration, die Secrets automatisch in den Pod injiziert, indem sie diese als Datei in einem Volume bereitstellt. Ich empfehle dringend meinen Vault Secrets Webhook, da dieser Secrets sicherer in den Pod einschleust und gleichzeitig deren Nutzung automatisiert.

export CHART_VERSION=0.3.0

Chart abrufen und entpacken:

$ wget https://github.com/hashicorp/vault-helm/archive/v$CHART_VERSION.tar.gz && tar zxf v$CHART_VERSION.tar.gz && rm v$CHART_VERSION.tar.gz

Die values.yaml für das Chart konfigurieren:

Das folgende Gist enthält Platzhalter für die Variablensubstitution:

global:
tlsDisable: false
server:
extraEnvironmentVars:
GOOGLE_APPLICATION_CREDENTIALS: /vault/userconfig/vault-gcs/vault_gcs_key.json
extraVolumes:
- type: secret
name: vault-gcs
path: "/vault/userconfig"
- type: secret
name: vault-tls
path: "/etc/tls"
authDelegator:
enabled: true
ha:
enabled: true
config: |
ui = true
listener "tcp" {
tls_disable = 0
tls_cert_file = "/etc/tls/vault-tls/vault.pem"
tls_key_file = "/etc/tls/vault-tls/vault-key.pem"
tls_client_ca_file = "/etc/tls/vault-tls/ca.pem"
tls_min_version = "tls12"
address = "[::]:8200"
cluster_address = "[::]:8201"
}
storage "gcs" {
bucket = "GCS_BUCKET_NAME"
ha_enabled = "true"
}
seal "gcpckms" {
project = "GCP_PROJECT"
region = "global"
key_ring = "vault-helm-unseal-kr"
crypto_key = "vault-helm-unseal-key"
}

Mit folgendem Befehl erzeugen Sie eine neue Values-Datei namens vault-gke-values.yaml:

$ curl -s https://gist.githubusercontent.com/innovia/53c05bf69312706fc93ffe3bb685b223/raw/adc169605984da8ba82082191c8f631579b1b199/vault-gke-values.yaml | sed "s/GCP_PROJECT/$GCP_PROJECT/g" | sed "s/GCS_BUCKET_NAME/$GCS_BUCKET_NAME/g" > vault-helm-$CHART_VERSION/vault-gke-values.yaml

Kontrollieren Sie die erzeugte Datei, um sicherzugehen, dass das richtige Projekt und der richtige GCS-Bucket eingetragen sind.

$ cat vault-helm-$CHART_VERSION/vault-gke-values.yaml | grep -E 'bucket|project'
bucket = "<COMPANY>-vault-data"
project = "ami-playground"

Mit Helm 2.x:

Ist im Cluster kein Tiller installiert, lässt sich die Tiller-Einrichtung überspringen, indem Sie ein Tillerless-Plugin für Helm installieren. Das startet einen lokalen Tiller auf Ihrem Rechner und verweist Helm darauf. Andernfalls springen Sie weiter zum Abschnitt Vault-Chart installieren.

Das Tillerless-Helm-Plugin installieren:

Falls Helm noch nicht installiert ist:

$ brew install helm@2

Nur den Client initialisieren, damit der Tiller-Server nicht installiert wird:

helm init --client-only
helm-tillerless-Plugin installieren
helm plugin install https://github.com/rimusz/helm-tiller
Tiller über Helm starten
$ helm tiller start
Installed Helm version v2.16.1
Copied found /usr/local/bin/tiller to helm-tiller/bin
Helm and Tiller are the same version!
Starting Tiller...
Tiller namespace: kube-system

Mit Helm 3

$ brew install helm

Vault-Chart installieren:

Hinweis: Bei Helm 3 werden die Ressourcen in der Ausgabe nicht aufgeführt.

$ helm upgrade --install vault -f vault-helm-$CHART_VERSION/vault-gke-values.yaml vault-helm-$CHART_VERSION
release "vault" does not exist. Installing it now.
NAME: vault
LAST DEPLOYED: Wed Nov 13 15:41:55 2019
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME AGE
vault-config 0s
==> v1/Service
NAME AGE
vault 0s
==> v1/ServiceAccount
NAME AGE
vault 0s
==> v1/StatefulSet
NAME AGE
vault 0s
==> v1beta1/ClusterRoleBinding
NAME AGE
vault-server-binding 0s
==> v1beta1/PodDisruptionBudget
NAME AGE
vault 0s
NOTES:
Thank you for installing HashiCorp Vault!
Now that you have deployed Vault, you should look over the docs on using
Vault with Kubernetes available here:
https://www.vaultproject.io/docs/
Your release is named vault. To learn more about the release, try:
$ helm status vault
$ helm get vault
Vault sollte jetzt laufen und sich im uninitialisierten Zustand befinden.

Folgende Warnungen sind OK, solange Vault noch nicht initialisiert ist:

=> Vault server started! Log data will stream in below:
2019-12-17T19:07:37.937Z [INFO] proxy environment: http_proxy= https_proxy= no_proxy=
2019-12-17T19:07:38.909Z [INFO] core: stored unseal keys supported, attempting fetch
2019-12-17T19:07:39.037Z [WARN] failed to unseal core: error="stored unseal keys are supported, but none were found"
2019-12-17T19:07:44.038Z [INFO] core: stored unseal keys supported, attempting fetch
2019-12-17T19:07:44.080Z [INFO] core: autoseal: seal configuration missing, but cannot check old path as core is sealed: seal_type=recovery
2019-12-17T19:07:44.174Z [WARN] failed to unseal core: error="stored unseal keys are supported, but none were found"
---
kubectl describe pod vault-0
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
...
Warning Unhealthy 3s (x9 over 27s) kubelet, minikube Readiness probe failed: Key Value

Vault mit KMS Auto-Unseal initialisieren

Öffnen Sie ein Port-Forward zu Vault:

$ kubectl port-forward vault-0 8200:8200 > /dev/null & export PID=$!; echo "vault port-forward pid: $PID"

Verbinden Sie sich mit Vault über das CA.pem-Zertifikat:

$ export VAULT_ADDR=https://127.0.0.1:8200;
export VAULT_CACERT=$PWD/ca.pem
Mein Vault-ca.pem liegt z. B. unter:
VAULT_CACERT: /Users/ami/vault-gke-medium/ca.pem

Installieren Sie den Vault-Client (achten Sie darauf, dass Client- und Server-Version übereinstimmen):

$ brew install vault

Status prüfen:

$ vault status
Key Value
--- -----
Recovery Seal Type gcpckms
Initialized false
Sealed true
Total Recovery Shares 0
Threshold 0
Unseal Progress 0/0
Unseal Nonce n/a
Version n/a
HA Enabled true

Jetzt Vault initialisieren:

vault operator init
Recovery Key 1: 33nCanHWgYMR/VPj6bNQdHXJiayL6WeB8Ourx4kHYNaX
Recovery Key 2: IMf7RjptFxtGQUbEWUWehanCBiSY7VhElkM7rRVxczGc
Recovery Key 3: zGuzk/PhNet9OHL4cW2H7d3XypDxfwWXkmajclLPklK4
Recovery Key 4: nCFS0dt0cNGB2LWk0F+3Vmz9TbVNpeIsXbIXDbRarlnT
Recovery Key 5: 9GxXr/6T8OJWJrWqyHQxayR0BAK+WTdbT870AzKEFl2V
Initial Root Token: s.1ukhSgycySjZUJRD0bZjSEit
Success! Vault is initialized
Recovery key initialized with 5 key shares and a key threshold of 3. Please
securely distribute the key shares printed above.

Bewahren Sie diese Schlüssel sicher auf.

Der selbst signierten Certificate Authority vertrauen:

Da wir die ca.pem selbst erstellt haben, wird ihr nicht vertraut – sie ist nicht Teil des CA-Bundles, das mit Ihrem Rechner ausgeliefert wird.

Mit den folgenden Schritten lässt sich das je nach Betriebssystem nachholen.

macOS:

Mit "immer vertrauen" für die CA öffnen Sie die Vault-UI im Browser ohne Fehlermeldungen:

$ sudo security add-trusted-cert -d -k /Library/Keychains/System.keychain $VAULT_CACERT

Windows 10:

Folgen Sie dieser Anleitung, um das Zertifikat zu den vertrauenswürdigen Herausgebern hinzuzufügen:

https://docs.microsoft.com/en-us/visualstudio/deployment/how-to-add-a-trusted-publisher-to-a-client-computer-for-clickonce-applications?view=vs-2019

Kubernetes-Backend-Authentifizierung mit Vault einrichten

Da Vault läuft und hochverfügbar ist, können wir es jetzt mit Kubernetes verbinden.

Wir nutzen einen Service Account für das initiale Login von Vault gegenüber Kubernetes.

Das Token dieses Service Accounts wird per Vault-CLI in Vault konfiguriert.

Dieser Service Account verfügt über die spezielle Berechtigung "system:auth-delegator", mit der Vault den Service Account des Pods zur Authentifizierung an Kubernetes weiterreichen kann. Nach erfolgreicher Authentifizierung gibt Vault ein Vault-Login-Token an den Client zurück, der damit auf Vault zugreift und die benötigten Secrets abruft.

Der Client meldet sich mit dem Login-Token bei Vault an und holt das Secret.

Vault prüft anhand der Zuordnung von Vault-Rolle, Service Account, Namespace und Policy, ob der Zugriff erlaubt oder verweigert wird.

Legen wir den Service Account vault-reviewer dafür an:

https://gist.github.com/innovia/5435f2336e4dd0045dbb5842880b3334#file-vault-reviewer-yaml

Link zum Original-Gist

Hinweis: Wenn Sie Vault in einem anderen Namespace eingerichtet haben, passen Sie diese Datei entsprechend an.

kubectl apply -f vault-reviewer.yaml

Aktivieren Sie das Kubernetes-Auth-Backend:

$ vault login
$ vault auth enable kubernetes
Success! Enabled kubernetes auth method at: kubernetes/

Vault mit dem vault-reviewer-Token und der CA konfigurieren:

Hinweis: Wenn Sie Vault in einem anderen Namespace eingerichtet haben, hängen Sie das Flag -n an jeden kubectl-Befehl an.

$ VAULT_SA_TOKEN_NAME=$(kubectl get sa vault-reviewer -o jsonpath="{.secrets[*]['name']}")
$ SA_JWT_TOKEN=$(kubectl get secret "$VAULT_SA_TOKEN_NAME" -o jsonpath="{.data.token}" | base64 --decode; echo)
$ SA_CA_CRT=$(kubectl get secret "$VAULT_SA_TOKEN_NAME" -o jsonpath="{.data['ca\.crt']}" | base64 --decode; echo)
$ vault write auth/kubernetes/config token_reviewer_jwt="$SA_JWT_TOKEN" kubernetes_host=https://kubernetes.default kubernetes_ca_cert="$SA_CA_CRT"
Success! Data written to: auth/kubernetes/config

Grundvoraussetzungen, damit ein Pod auf ein Secret zugreifen kann:

  • Der Pod muss einen Service Account besitzen
  • Das Vault-CA.pem-Secret muss im Namespace existieren, in dem der Pod läuft
  • Es muss eine Policy mit mindestens Lesezugriff auf das Secret existieren
path "secret/foo" {
capabilities = ["read"]
}
  • In Vault muss eine Vault-Rolle angelegt werden:
vault write auth/kubernetes/role/<role_name> \
bound_service_account_names=<service_account_name> \
bound_service_account_namespaces=<service_account_namespace> \
policies=<policy_name>

Damit ist die Einrichtung von HashiCorp Vault auf GKE abgeschlossen. Ich empfehle dringend, zusätzlich Vault Secrets Webhooks einzurichten, um Secrets aus Vault nahtlos und nur über ein paar Annotations zu konsumieren.

Vault-UI mit Identity-Aware Proxy (IAP) über einen Load Balancer einrichten

1 dxfdyie8ymjqvpgeb6gfaw

Der Identity-Aware Proxy authentifiziert Benutzer, ohne dass ein VPN oder ein SSH-Bastion-Host nötig ist.

Mehr Infos zu IAP

Wenn Sie für den Service einen Load Balancer mit Identity-Aware Proxy einrichten möchten, folgen Sie den Schritten unten. Andernfalls erreichen Sie die Vault-UI über kubectl port-forward vault-0 8200.

Der folgende Prozess bindet keinen Google-Nutzer an Vault – er dient ausschließlich als Multi-Faktor-Authentifizierung. Es gäbe zwar JWT für die Vault-Authentifizierung, doch das erlaubt jedem Benutzer aus Ihrer Domain, eine Rolle zu wählen, was weniger sicher ist …

Hinweis:

Das selbst signierte Zertifikat für den Vault-Service selbst bleibt weiterhin nötig. Das Load-Balancer-Zertifikat wird benötigt, um IAP und HTTPS zu aktivieren.

Voraussetzungen:

  • Für den Load Balancer muss ein Zertifikat per Google Managed Certificate erstellt oder als Kubernetes-Secret hinterlegt werden.

https://cloud.google.com/load-balancing/docs/ssl-certificates

  • Die Domain muss über die Google Webmaster Tools verifiziert sein
  • Es müssen eine globale statische IP sowie ein DNS-Eintrag angelegt werden

(Wenn Sie den externalDNS-Service einsetzen, entfällt das.)

$ gcloud compute addresses create vault-ui --global

Ein Zertifikat lässt sich mit folgendem YAML erstellen:

https://gist.github.com/innovia/71c219692b003e97bc72feeeb5bc8442

$ kubectl apply -f managed-cert.yaml

Nach dem Erstellen dauert es 15–20 Minuten, bis sich der Status von Provisioning auf Active ändert.

Status des Zertifikats prüfen:

$ kubectl describe ManagedCertificate vault-ui-certificate
Name: vault-ui-certificate
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"networking.gke.io/v1beta1","kind":"ManagedCertificate","metadata":{"annotations":{},"name":"vault-ui-certificate","namespac...
API Version: networking.gke.io/v1beta1
Kind: ManagedCertificate
Metadata:
Creation Timestamp: 2020-01-13T23:10:28Z
Generation: 3
Resource Version: 7120865
Self Link: /apis/networking.gke.io/v1beta1/namespaces/default/managedcertificates/vault-ui-certificate
UID: e35e7a1b-3659-11ea-ae90-42010aa80174
Spec:
Domains:
vault.ami-playground.doit-intl.com
Status:
Certificate Name: mcrt-9462e1f4-6dd6-4cf2-8769-9693ba29789e
Certificate Status: Active
Domain Status:
Domain: vault.ami-playground.doit-intl.com
Status: Active
Expire Time: 2020-04-12T15:12:29.000-07:00
Events: <none>

IAP für GKE konfigurieren:

Alternativ zu den hier zusammengefassten Schritten können Sie der vollständigen Anleitung folgen:

Konfigurieren Sie IAP für Ihre Domain über den OAuth-Consent-Screen und erzeugen Sie die Client-Credentials.

Kopieren Sie nach dem Erstellen die Client-ID und tragen Sie sie im Feld Authorized redirect URIs in folgendem Format ein:

https://iap.googleapis.com/v1/oauth/clientIds/<CLIENT_ID>:handleRedirect

Legen Sie das Secret an, das von der Backend-Config verwendet wird:

kubectl create secret generic my-secret --from-literal=client_id=client_id_key \
--from-literal=client_secret=client_secret_key

Erstellen Sie eine Backend-Config für IAP:

https://gist.github.com/innovia/4485a253f15cd824d0e6d2a19230a603

Aktivieren Sie den Abschnitt am Ende der vault-gke.yaml-Dateien und stellen Sie sicher, dass die Werte für die globale statische IP sowie der DNS-Eintrag für den Host aktualisiert sind.

Hinweis:

Sie müssen die Vault-Installation löschen und mit Helm neu erstellen, da der GKE-Ingress Probleme beim Aktualisieren bestehender Ingresses hat.

Zusammenfassung der Values-YAML-Datei:

  • Wir aktivieren den Vault-UI-Service auf Port 443 und exponieren ihn über einen NodePort
  • Wir koppeln den Vault-Service per Backend-Config an IAP
  • Wir aktivieren den Ingress mit einer statischen globalen IP und dem darauf gemappten DNS-Host
  • Wir deaktivieren HTTP auf dem Load Balancer
  • Wir konfigurieren die Kommunikation zwischen Load Balancer und Vault-Pods so, dass ausschließlich HTTPS verwendet wird
  • Wir richten das Managed Certificate für den Load Balancer ein, damit dieser als HTTPS-Listener arbeitet

Wenn Sie nach dem Deployment auf der IAP-Seite nachsehen, werden folgende Fehler und Warnungen angezeigt (bei Shared-VPC-Networking erscheinen unter Umständen beide Backend-Services mit ERROR. Der eigentliche Test ist der Aufruf der Vault-UI-URL im Browser):

1 yu5axdnsw13bo 4vi sqeg

Der erste Fehler betrifft das Default-Backend (das die 404er ausliefert). Er weist lediglich darauf hin, dass IAP für 404-Seiten nicht aktiv ist – genau so ist es gewollt.

Der andere ist nur eine Warnung. Klicken Sie darauf, sehen Sie etwa Folgendes:

1 vulcp7of1dwxqb5m7p2zfq

Das bedeutet lediglich, dass GCP einige Firewall-Regeln erkannt hat, die IAP umgehen – etwa interne Netzwerke und die Verbindung des Load Balancers zum Vault-Backend.

Wählen Sie auf der IAP-Seite default/vault aus und fügen Sie im Info-Panel auf der linken Seite die Members hinzu, die per Load Balancer Zugriff auf die vault-ui benötigen.

Vergeben Sie an das Member die Berechtigung "IAP-secured Web App User", damit dieser Benutzer auf die Vault-UI zugreifen kann.