En corto
| |
Borra un pod anotado y comprueba que el injector recibe la llamada:
| |
El síntoma
- El pod lleva
vault.hashicorp.com/agent-inject: "true"y el resto de anotaciones, pero arranca solo con el contenedor de la aplicación: nivault-agent-initnivault-agent. - Crear el pod tarda unos 30 segundos más de lo normal.
kubectl get eventsno dice nada del webhook. En los logs del injector no hay ni una línea deRequest received.- En otros clústeres (zonales públicos, kind, k3s) el mismo chart y las mismas anotaciones funcionan.
Por qué
En un clúster privado de GKE el control plane vive en una VPC de Google y llega a tus nodos a través de la regla de firewall que GKE crea sola (gke-<cluster>-<hash>-master). Esa regla solo permite TCP 443 y 10250 desde el rango del control plane hacia los nodos. Todo lo demás está cerrado.
Un webhook de admisión no pasa por el Service como lo haría un cliente normal: el API server resuelve el Service a sus endpoints y conecta directamente con la IP del pod en el targetPort. El Service del injector (vault-agent-injector-svc) publica el 443, pero el pod escucha en el 8080 (injector.port del chart), así que la conexión real es al 8080 y el firewall la descarta.
Lo que pasa después lo explican dos valores por defecto del chart de Vault:
| Valor | Por defecto | Efecto |
|---|---|---|
injector.webhook.timeoutSeconds | 30 | El API server espera 30 s la respuesta del webhook |
injector.webhook.failurePolicy | Ignore | Si el webhook no contesta, el pod se crea sin mutar, sin error |
De ahí los 30 segundos de retraso y el pod sin sidecar y sin ningún mensaje. Con failurePolicy: Fail habrías visto failed calling webhook "vault.hashicorp.com": … context deadline exceeded en los eventos del ReplicaSet, que al menos apunta al sitio correcto.
No es solo Vault
Cualquier webhook de admisión, conversión de CRDs o API agregada que escuche en un puerto que no sea 443 o 10250 falla igual en GKE privado: el operador de OpenTelemetry (9443), Kyverno (9443), Gatekeeper (8443)… cert-manager escucha en el 10250 precisamente para no necesitar esta regla. La documentación de GKE lo lista como requisito de “admission webhooks, aggregated API servers, webhook conversion”: una regla por puerto, desde masterIpv4CidrBlock hacia los tags de los nodos.
Si el clúster está en Terraform, deja la regla junto al clúster para que no se pierda al recrearlo:
| |
Trampas
- El tag equivocado: los nodos llevan
gke-<cluster>-<hash>-node; cópialo de las reglasgke-<cluster>-*existentes en vez de escribirlo a mano. Con varios node pools, comprueba que todos tengan el tag. - El rango del control plane cambia si recreas el clúster; una regla escrita a mano con el CIDR viejo deja de servir sin avisar.
failurePolicy: Failhace visible el fallo, pero mientras el webhook esté caído no se crea ningún pod en los namespaces que casen con elnamespaceSelector; el chart excluye al propio injector, pero no a Vault si comparten namespace y etiquetas. Úsalo conreplicas: 2.- El injector sí recibe la petición y sigue sin sidecar: entonces ya no es red. Mira los logs de
vault-agent-init:permission deniedes el rol de Kubernetes auth (bound_service_account_names,bound_service_account_namespaces) o la política;connection refusedesvault.hashicorp.com/serviceo la dirección de Vault. - Un solo puerto por regla no escala: si acumulas webhooks, una regla con
--rules=tcp:8080,8443,9443desde el control plane es más fácil de mantener que cinco.
Referencias: vault-k8s #46, vault-k8s #32 y reglas de firewall en clústeres privados en la documentación de GKE.
Ver también
- cert-manager: el reto HTTP-01 devuelve 308 — otro componente que “no hace nada” hasta que miras el sitio correcto.
- GitHub Actions → Google Cloud sin llaves — para desplegar en ese GKE sin llaves JSON.