
Gestionar secretos de forma nativa en Kubernetes no es una opción segura. Los secretos en Kubernetes son solo texto plano codificado en base64 que puede consumirse desde un pod en ejecución.
Aviso:
Este tutorial está pensado para que entiendas cada componente de la configuración paso a paso. Nada te impide usar terraform para montarlo, pero meterte a ciegas con algo tan crítico te puede traer problemas que sin ese conocimiento de fondo cuestan mucho más de resolver.

Sí, puedes cifrar un secreto en Kubernetes, pero ese secreto solo se cifra en reposo y, al montarse dentro del Pod, queda como un archivo o variable de entorno al que se accede sin problema desde el propio Pod. Por eso, ante una brecha, los datos se pueden ver comprometidos si alguien tiene acceso al Pod o incluso al namespace donde corre vía kubectl.
Hashicorp Vault es una forma segura de gestionar secretos, además de auditar y revocar el acceso a ellos. Una cosa es instalar y usar vault, y otra muy distinta consumir esos secretos desde un Pod.
Este post explica cómo instalar Vault en GKE con Terraform y Helm. Para consumir esos secretos, te recomiendo leer mi otro post sobre cómo consumir secretos de Vault de forma transparente en un Pod.
Este enfoque es algo más sencillo de manejar si solo te interesa Vault y no necesitas las funciones avanzadas de Consul, como C onsul template con Vault.
En este tutorial te voy a mostrar cómo instalar un vault de alta disponibilidad usando Google Storage GCS como backend de Vault, con TLS de extremo a extremo.
Nota:
Conviene crear la configuración de Vault por entorno, así pruebas mejor las actualizaciones y mantienes los entornos aislados entre sí.
No recomiendo exponer el vault como servicio. Si necesitas acceder, puedes hacerlo con el comando:
$ kubectl port-forward vault-0 8200:8200y entrar a la UI desde https://127.0.0.1:8200, como se detalla más abajo.
Si tienes VMs que necesitan acceder a ese vault, deberías usar VPC peering, ya que los servicios y pods son IPs nativas. Eso no se cubre acá.
Tutorial:
Resumen:
- Crear certificados TLS para vault
- Crear un Bucket de GCS como backend de almacenamiento de Vault
- Crear un keyring de KMS y una clave de cifrado para el auto-unseal de Vault.
- Crear cuentas de servicio para que Vault acceda a KMS y al backend de almacenamiento GCS.
- Instalar el chart oficial de Helm de Hashicorp vault con helm tillerless.
Crear certificados TLS para Vault:

Una de las recomendaciones de hardening para producción es que las comunicaciones entre vault y los clientes vayan cifradas con TLS, tanto en el tráfico entrante como en el saliente.
Vamos a crear un certificado que se usará para:
- La dirección del servicio vault de Kubernetes.
- 127.0.0.1
Vamos a usar el SSL Toolkit de CloudFlare ( cfssl y cfssljson) para generar estos certificados.
La instalación requiere una instalación funcional de Go 1.12+ y un GOPATH bien configurado.
¡Importante!: asegúrate de que el bin de GOPATH esté en tu path:
export PATH=$GOPATH/bin:$PATHInstalando el SSL ToolKit de CloudFlare:
go get -u github.com/cloudflare/cfssl/cmd/cfsslgo get -u github.com/cloudflare/cfssl/cmd/cfssljsonInicializar una Autoridad de Certificación (CA):
$ mkdir vault-ca && cd vault-caCrea los archivos de la CA:
Archivo de configuración de la CA con vencimiento a 5 años
$ cat <<EOF > ca-config.json{ "signing": { "default": { "expiry": "8760h" }, "profiles": { "default": { "usages": ["signing", "key encipherment", "server auth", "client auth"], "expiry": "8760h" } } }}EOFSolicitud de Firma de la CA:
$ cat <<EOF > ca-csr.json{ "hosts": [\ "cluster.local"\ ], "key": { "algo": "rsa", "size": 2048 }, "names": [\ {\ "C": "US",\ "L": "NewYork",\ "O": "Kubernetes",\ "OU": "CA",\ "ST": "NewYork"\ }\ ]}EOFSolicitud de Firma del Certificado de VAULT, que firmará la CA anterior: nota: cambia el namespace de vault si no es el namespace default
$ 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"\ }\ ]}EOFObviamente, puedes ajustar la información del cert en la sección "names" de abajo a tu gusto.
Ejecuta el siguiente comando para inicializar la CA con el archivo que acabas de editar:
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca2019/11/12 16:35:01 [INFO] generating a new CA key and certificate from CSR2019/11/12 16:35:01 [INFO] generate received request2019/11/12 16:35:01 [INFO] received CSR2019/11/12 16:35:01 [INFO] generating key: rsa-20482019/11/12 16:35:01 [INFO] encoded CSR2019/11/12 16:35:01 [INFO] signed certificate with serial number 425581644650417483788325060652779897454211028144Crea una clave privada y firma el certificado TLS:
$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=default \ vault-csr.json | cfssljson -bare vault2019/11/12 16:36:33 [INFO] generate received request2019/11/12 16:36:33 [INFO] received CSR2019/11/12 16:36:33 [INFO] generating key: rsa-20482019/11/12 16:36:34 [INFO] encoded CSR2019/11/12 16:36:34 [INFO] signed certificate with serial number 311973563616303179057952194819087555625015840298En este punto deberías tener los siguientes archivos en el directorio de trabajo actual:
ca-key.pemca.pemvault-key.pemvault.pemguarda los archivos de la CA en un lugar seguro, los vas a necesitar para volver a firmar el cert cuando expire (la CA dura 5 años, Vault dura 1 año)
Crea un secreto para el TLS de Vault y el CA.pem
kubectl create secret generic vault-tls \ --from-file=ca.pem \ --from-file=vault.pem \ --from-file=vault-key.pemDefine el proyecto de GCP para el resto del tutorial:
$ export GCP_PROJECT=<your_project_id>Habilitando las APIs de GCP necesarias para este tutorial:
$ 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.Backend de almacenamiento de Vault
Google Cloud Storage
En modo HA, los servidores de Vault tienen dos estados adicionales: standby y active. Dentro de un cluster de Vault, solo una instancia estará active y atenderá todas las solicitudes (lecturas y escrituras), mientras que los nodos en standby redirigen las solicitudes al nodo active.
Vamos a crear el bucket en gcs con el comando gsutil. Los nombres de bucket deben ser únicos a nivel global en toda Google Cloud, así que elige un nombre único.
$ export GCS_BUCKET_NAME=mycompany-vault-data$ gsutil mb gs://$GCS_BUCKET_NAME$ gsutil versioning set on gs://$GCS_BUCKET_NAMEAunque los datos están cifrados en tránsito y en reposo, asegúrate de configurar los permisos adecuados en el bucket para limitar la exposición. Quizá te interese crear una cuenta de servicio que limite las interacciones de Vault con Google Cloud a los objetos del bucket de almacenamiento mediante permisos IAM.
Auto unseal de Vault

cuando se reinicia Vault, arranca sellado y cifrado. Para usarlo hay que abrirlo (unseal). Existe una nueva función llamada auto unseal que lee las master keys y el root token desde CloudKMS automáticamente.
Crear el Keyring de KMS y la Crypto Key:
En esta sección crearemos el keyring de KMS y la clave para cifrar y descifrar las master keys y el root token de vault:
Crea el keyring de kms vault-helm-unseal-kr:
$ gcloud kms keyrings create vault-helm-unseal-kr \ --location global \ --project ${GCP_PROJECT}Crea la clave de cifrado:
$ gcloud kms keys create vault-helm-unseal-key \ --location global \ --keyring vault-helm-unseal-kr \ --purpose encryption \ --project ${GCP_PROJECT}Crear las cuentas de servicio de GCP y los permisos IAM para vault
Configura las variables:
$ export VAULT_SA_NAME=vault-server; export VAULT_SA=$VAULT_SA_NAME@$GCP_PROJECT.iam.gserviceaccount.comCrea la cuenta de servicio del servidor vault:
$ gcloud iam service-accounts create $VAULT_SA_NAME \ --display-name "Vault server service account" \ --project ${GCP_PROJECT}Crea la clave de la cuenta de servicio del servidor Vault (archivo JSON de credenciales):
$ gcloud iam service-accounts keys create \ --iam-account $VAULT_SA /tmp/vault_gcs_key.jsoncreated key [be22cfe6e30f3a3fcfc6ebaa23ca3ba905dd60ab] of type [json] as [/tmp/vault_gcs_key.json] for [[email protected]] Crea el secreto para almacenar la cuenta de servicio de google de vault$ kubectl create secret generic vault-gcs \ --from-file=/tmp/vault_gcs_key.jsonsecret/vault-gcs createdOtorga acceso al Bucket GCS de almacenamiento de vault:
$ gsutil iam ch \ serviceAccount:${VAULT_SA}:objectAdmin \ gs://${GCS_BUCKET_NAME}Otorga acceso a la kms key de vault:
$ 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.cryptoKeyEncrypterDecrypteretag: BwWZ6sIYovk=version: 1Nota:Si por algún motivo eliminaste la cuenta de servicio y la volviste a crear, tienes que eliminar la política IAM de la clave; si no, salta a la sección Obtener el chart oficial de Hashicorp para vault$ gcloud kms keys get-iam-policy vault-helm-unseal-key --location global --keyring vault-helm-unseal-kr > kms-policy.yaml edita el archivo de política, elimina los members dentro de binding y guarda el archivobindings:etag: BwWXQz4HjuI=version: 1vuelve a aplicar la política:
$ gcloud kms keys set-iam-policy vault-helm-unseal-key --location global --keyring vault-helm-unseal-kr kms-policy.yamlObtener el chart oficial de Hashicorp para Vault:

nota:
desde la versión 0.3.0 hay una integración de vault con Kubernetes que inyecta automáticamente los secretos al Pod renderizándolos como un archivo en un volumen. Te recomiendo usar mi vault secrets webhook, porque es una forma más segura de inyectar un secreto al Pod y además automatiza el consumo de los secretos.
export CHART_VERSION=0.3.0Descarga el chart y descomprímelo:
$ 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.gzConfigurando el values.yaml del chart:
El siguiente gist tiene placeholders para sustituir variables
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" }usa el comando de abajo para crear un nuevo archivo de values llamado 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.yamlRevisa el archivo generado para confirmar que el proyecto y el bucket de GCS son los correctos.
$ cat vault-helm-$CHART_VERSION/vault-gke-values.yaml | grep -E 'bucket|project'bucket = "<COMPANY>-vault-data"project = "ami-playground"usando helm 2.x:
Si no tienes tiller instalado en el cluster, puedes saltarte la configuración de tiller instalando un plugin tillerless para helm, que levanta un tiller local en tu computadora y le indica a helm que lo use. Si no, salta a la sección de instalar el chart de vault más abajo.
Instalar el plugin tillerless de helm:
si aún no tienes helm, instálalo con
$ brew install helm@2Inicializa solo el cliente para que no se instale el servidor tiller
helm init --client-onlyinstala el plugin helm-tillerlesshelm plugin install https://github.com/rimusz/helm-tillerarranca tiller con helm$ helm tiller startInstalled Helm version v2.16.1Copied found /usr/local/bin/tiller to helm-tiller/binHelm and Tiller are the same version!Starting Tiller...Tiller namespace: kube-systemUsando helm 3
$ brew install helmInstalar el chart de Vault:
nota: si usas helm 3, la salida no listará los recursos.
$ helm upgrade --install vault -f vault-helm-$CHART_VERSION/vault-gke-values.yaml vault-helm-$CHART_VERSIONrelease "vault" does not exist. Installing it now.NAME: vaultLAST DEPLOYED: Wed Nov 13 15:41:55 2019NAMESPACE: defaultSTATUS: DEPLOYEDRESOURCES:==> v1/ConfigMapNAME AGEvault-config 0s==> v1/ServiceNAME AGEvault 0s==> v1/ServiceAccountNAME AGEvault 0s==> v1/StatefulSetNAME AGEvault 0s==> v1beta1/ClusterRoleBindingNAME AGEvault-server-binding 0s==> v1beta1/PodDisruptionBudgetNAME AGEvault 0sNOTES:Thank you for installing HashiCorp Vault!Now that you have deployed Vault, you should look over the docs on usingVault 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 vaultVault debería arrancar y quedar en estado no inicializado.Las siguientes advertencias son normales, ya que vault aún no está inicializado:
=> 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 fetch2019-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 fetch2019-12-17T19:07:44.080Z [INFO] core: autoseal: seal configuration missing, but cannot check old path as core is sealed: seal_type=recovery2019-12-17T19:07:44.174Z [WARN] failed to unseal core: error="stored unseal keys are supported, but none were found"---kubectl describe pod vault-0Events: Type Reason Age From Message ---- ------ ---- ---- ------- ... Warning Unhealthy 3s (x9 over 27s) kubelet, minikube Readiness probe failed: Key ValueInicializar vault con auto unseal de KMS
abre un port-forward a Vault con el comando:
$ kubectl port-forward vault-0 8200:8200 > /dev/null & export PID=$!; echo "vault port-forward pid: $PID"Conéctate a Vault usando el cert CA.pem
$ export VAULT_ADDR=https://127.0.0.1:8200; export VAULT_CACERT=$PWD/ca.pemmy vault ca.pem for example is at:VAULT_CACERT: /Users/ami/vault-gke-medium/ca.pemInstala el cliente de vault (asegúrate de que sea de la misma versión que el servidor)
$ brew install vaultVerifica el estado:
$ vault statusKey Value--- -----Recovery Seal Type gcpckmsInitialized falseSealed trueTotal Recovery Shares 0Threshold 0Unseal Progress 0/0Unseal Nonce n/aVersion n/aHA Enabled trueahora inicializa vault:
vault operator initRecovery Key 1: 33nCanHWgYMR/VPj6bNQdHXJiayL6WeB8Ourx4kHYNaXRecovery Key 2: IMf7RjptFxtGQUbEWUWehanCBiSY7VhElkM7rRVxczGcRecovery Key 3: zGuzk/PhNet9OHL4cW2H7d3XypDxfwWXkmajclLPklK4Recovery Key 4: nCFS0dt0cNGB2LWk0F+3Vmz9TbVNpeIsXbIXDbRarlnTRecovery Key 5: 9GxXr/6T8OJWJrWqyHQxayR0BAK+WTdbT870AzKEFl2VInitial Root Token: s.1ukhSgycySjZUJRD0bZjSEitSuccess! Vault is initializedRecovery key initialized with 5 key shares and a key threshold of 3. Pleasesecurely distribute the key shares printed above.Guarda estas claves en un lugar seguro.
Confiar en la autoridad de certificación autofirmada:
Como creamos el ca.pem nosotros mismos, no será de confianza, ya que no forma parte del bundle de CAs que viene con tu computadora.
podemos marcarla como confiable siguiendo las instrucciones de abajo según tu sistema operativo.
Mac OS:
Marcar "always trust" para la CA te permitirá abrir la UI de Vault en el navegador sin errores:
$ sudo security add-trusted-cert -d -k /Library/Keychains/System.keychain $VAULT_CACERTWindows 10:
Sigue las instrucciones de aquí para añadir el cert a los publishers de confianza:
Configurar la autenticación del backend de Kubernetes con Vault
Ahora que Vault está activo y en alta disponibilidad, podemos seguir y conectar Vault con Kubernetes.
Vamos a usar una cuenta de servicio para hacer el login inicial de Vault con Kubernetes.
Este token de cuenta de servicio se configura dentro del vault con la CLI de vault.
Esta cuenta de servicio tiene un permiso especial llamado "system:auth-delegator" que le permite a vault pasar la cuenta de servicio del pod a Kubernetes para autenticarla. Una vez autenticada, vault devuelve un token de login al cliente, que hablará con Vault para obtener los secretos que necesite.
El cliente usará el token de login para iniciar sesión en Vault y obtener el secreto.
Vault verificará un mapeo entre un rol de vault, la cuenta de servicio, el namespace y la política para permitir o denegar el acceso.
creemos la cuenta de servicio para vault-reviewer
https://gist.github.com/innovia/5435f2336e4dd0045dbb5842880b3334#file-vault-reviewer-yaml
Toma en cuenta que, si configuraste Vault en cualquier otro namespace, debes actualizar este archivo en consecuencia.
kubectl apply -f vault-reviewer.yamlhabilita el backend de auth de Kubernetes:
$ vault login$ vault auth enable kubernetesSuccess! Enabled kubernetes auth method at: kubernetes/Configura Vault con el token y la ca de vault-reviewer:
nota: si configuraste vault en cualquier otro namespace, agrega el flag -n después de cada comando kubectl
$ 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/configRequisitos básicos para que un pod acceda a un secreto:
- el Pod debe tener una cuenta de servicio
- el secreto Vault CA.pem debe existir en el namespace donde corre el Pod
- debe existir una política con permiso mínimo de lectura sobre el secreto
path "secret/foo" { capabilities = ["read"]}- se debe crear un rol de Vault en Vault:
vault write auth/kubernetes/role/<role_name> \ bound_service_account_names=<service_account_name> \ bound_service_account_namespaces=<service_account_namespace> \ policies=<policy_name>Con esto concluye la configuración de Hashicorp vault en GKE. Te recomiendo configurar los vault secrets webhooks para consumir secretos de Vault sin fricción, basándote solo en unas pocas anotaciones.
Cómo configurar la UI de Vault con Identity-Aware Proxy (IAP) mediante un load-balancer

Identity-aware proxy es una forma de autenticar a un usuario sin necesidad de configurar una VPN o un Bastion SSH.
Si quieres configurar un load balancer para el servicio con identity-aware proxy, puedes hacerlo siguiendo los pasos de abajo. Si no, puedes acceder a la UI de vault con kubectl port-forward vault-0 8200
El siguiente proceso no vincula a un usuario de google con Vault de ninguna forma; sirve solo como autenticación multi-factor. Existe una forma de usar JWT para autenticarse en vault, pero eso permite que cualquier usuario de tu dominio elija un rol, lo que es menos seguro…
Nota:
Sigues necesitando el certificado autofirmado para el propio servicio de Vault. El certificado del load balancer es necesario para habilitar IAP y https.
Requisitos previos:
- Se debe crear un certificado para el load balancer mediante un Google Managed certificate, o crearlo como un secreto de Kubernetes.
https://cloud.google.com/load-balancing/docs/ssl-certificates
- El dominio debe estar verificado mediante las webmaster tools de Google
- Se debe crear una IP estática global y una entrada de DNS
(si usas el servicio externalDNS, no lo necesitas)
$ gcloud compute addresses create vault-ui --globalPuedes crear un certificado con el siguiente YAML
https://gist.github.com/innovia/71c219692b003e97bc72feeeb5bc8442
$ kubectl apply -f managed-cert.yamlUna vez creado, dale 15-20 minutos para que el estado pase de Provisioning a Active
Verifica el estado del cert:
$ kubectl describe ManagedCertificate vault-ui-certificateName: vault-ui-certificateNamespace: defaultLabels: <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/v1beta1Kind: ManagedCertificateMetadata: 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-42010aa80174Spec: Domains: vault.ami-playground.doit-intl.comStatus: 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:00Events: <none>Configurando IAP para GKE:
Puedes optar por seguir las instrucciones completas en lugar de los pasos resumidos abajo:
Configura el IAP para tu dominio mediante la pantalla de consentimiento de Oauth y crea las credenciales del cliente.
Una vez creado el cliente, copia el client ID y agrégalo al campo authorized redirect URIs en el siguiente formato:
https://iap.googleapis.com/v1/oauth/clientIds/<CLIENT_ID>:handleRedirectCrea el secreto que usará el backend config:
kubectl create secret generic my-secret --from-literal=client_id=client_id_key \ --from-literal=client_secret=client_secret_keyCrea un backend config para el IAP:
https://gist.github.com/innovia/4485a253f15cd824d0e6d2a19230a603
habilita la sección al final del archivo vault-gke.yaml y asegúrate de que los valores de la IP estática global y del DNS para el host estén actualizados
Nota:
tienes que eliminar la instalación de vault y volver a crearla con helm, ya que el ingress de GKE tiene problemas para actualizar ingresses existentes.
resumen del archivo values.yaml:
- habilitamos el servicio de UI de vault en el puerto 443 y lo exponemos por un NodePort
- configuramos el servicio de vault con el IAP mediante un backend config
- habilitamos el ingress con una IP estática global y el DNS como host mapeado a ella
- deshabilitamos HTTP en el load balancer
- configuramos la comunicación entre el load balancer y los pods de vault para que sea solo https
- configuramos el certificado gestionado para el load balancer, de modo que el load balancer sea un listener HTTPS
Una vez desplegado, si revisas la página de IAP verás los siguientes errores y advertencias (puede que veas ambos backend services con un ERROR si usas una red VPC compartida; la verdadera prueba es revisar la URL de la UI de vault en el navegador)

El primer error corresponde al backend por defecto (el que sirve los 404); el error solo indica que IAP no estará activo para ninguna página 404, que es el comportamiento esperado.
El otro es solo una advertencia. Si haces clic en ella, verás algo así:

Lo único que significa es que GCP detectó que algunas reglas de firewall se saltarán el IAP, como las redes internas y la comunicación del load balancer con el backend de vault.
selecciona default/vault en la página de IAP y, desde el panel de información de la izquierda, agrega los miembros que necesiten acceso a la vault-ui mediante el load balancer
agrega al miembro con el permiso "IAP-secured Web App User" para que ese usuario pueda acceder a la ui de vault.