From d502e5cd4cb58fce925f31879c77e2f0ce57e805 Mon Sep 17 00:00:00 2001 From: Agents Agent Date: Sat, 15 Aug 2026 21:56:01 +0200 Subject: [PATCH] fix(vault): authenticate auth-api and agents-api with Kubernetes auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit auth-api and agents-api took their Vault session token from a file the vault-agent injector wrote once at pod init (agent-pre-populate-only, no renewing sidecar), read it into SPRING_CLOUD_VAULT_TOKEN in the container command, and ran Spring Cloud Vault with the default TOKEN authentication. Vault's kubernetes mount issues a 1 h token, which the Spring Cloud Vault session manager renewed hourly — but only up to the mount's max_lease_ttl of 2764800 s (32 days). Past that cap renewal fails and Spring Vault discards the token and attempts a fresh login; TOKEN authentication has no login mechanism, so the token stayed dead for the remaining life of the pod and every Vault call returned "403 Forbidden: permission denied / invalid token". The visible failure was auth-api's /api/v1/auth/verify, which mints the agents assertion token through Vault transit (TokenService.createAgentsAssertionToken -> VaultTransitJwtEncoder -> transit sign). Forward-auth for agents.jorisjonkers.dev 500s while jorisjonkers.dev keeps working, because only the agents assertion path signs through transit. Both services now use KUBERNETES authentication against their existing kubernetes auth roles, so the session manager can re-login indefinitely from the projected service-account token. The injected token file and the SPRING_CLOUD_VAULT_TOKEN export are removed; the vault-agent still renders the secret env file under its own role. agents-api is configured through the canonical SPRING_CLOUD_VAULT_* property names because its application.yml lives in the agents-api repository. --- .../stateless/agents-api/deployment-enschede-ws.yaml | 9 +++++++-- .../flux/apps/stateless/agents-api/deployment.yaml | 9 +++++++-- .../flux/apps/stateless/auth-api/deployment.yaml | 6 ++++-- services/auth-api/src/main/resources/application.yml | 11 +++++++++++ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/platform/cluster/flux/apps/stateless/agents-api/deployment-enschede-ws.yaml b/platform/cluster/flux/apps/stateless/agents-api/deployment-enschede-ws.yaml index 99c792af..8ec1e1f3 100644 --- a/platform/cluster/flux/apps/stateless/agents-api/deployment-enschede-ws.yaml +++ b/platform/cluster/flux/apps/stateless/agents-api/deployment-enschede-ws.yaml @@ -35,7 +35,6 @@ spec: app.kubernetes.io/name: agents-api-ws annotations: vault.hashicorp.com/agent-inject: 'true' - vault.hashicorp.com/agent-inject-token: 'true' vault.hashicorp.com/agent-pre-populate-only: 'true' vault.hashicorp.com/role: agents-api vault.hashicorp.com/agent-inject-secret-agents-api.env: rabbitmq/creds/app-consumer @@ -77,7 +76,6 @@ spec: - /bin/sh - -ec - | - export SPRING_CLOUD_VAULT_TOKEN="$(cat /vault/secrets/token)" set -a . /vault/secrets/agents-api.env set +a @@ -98,6 +96,13 @@ spec: value: 'true' - name: VAULT_ADDR value: http://vault.data-system.svc.cluster.local:8200 + # Canonical Spring property names: agents-api's application.yml + # lives in the agents-api repo, so bind these directly rather than + # through a placeholder this repo does not own. + - name: SPRING_CLOUD_VAULT_AUTHENTICATION + value: KUBERNETES + - name: SPRING_CLOUD_VAULT_KUBERNETES_ROLE + value: agents-api - name: VAULT_DB_ENABLED value: 'true' - name: DB_HOST diff --git a/platform/cluster/flux/apps/stateless/agents-api/deployment.yaml b/platform/cluster/flux/apps/stateless/agents-api/deployment.yaml index ee03af95..1d8e182d 100644 --- a/platform/cluster/flux/apps/stateless/agents-api/deployment.yaml +++ b/platform/cluster/flux/apps/stateless/agents-api/deployment.yaml @@ -41,7 +41,6 @@ spec: app.kubernetes.io/name: agents-api annotations: vault.hashicorp.com/agent-inject: 'true' - vault.hashicorp.com/agent-inject-token: 'true' vault.hashicorp.com/agent-pre-populate-only: 'true' vault.hashicorp.com/role: agents-api vault.hashicorp.com/agent-inject-secret-agents-api.env: rabbitmq/creds/app-consumer @@ -84,7 +83,6 @@ spec: - /bin/sh - -ec - | - export SPRING_CLOUD_VAULT_TOKEN="$(cat /vault/secrets/token)" set -a . /vault/secrets/agents-api.env set +a @@ -111,6 +109,13 @@ spec: value: 'true' - name: VAULT_ADDR value: http://vault.data-system.svc.cluster.local:8200 + # Canonical Spring property names: agents-api's application.yml + # lives in the agents-api repo, so bind these directly rather than + # through a placeholder this repo does not own. + - name: SPRING_CLOUD_VAULT_AUTHENTICATION + value: KUBERNETES + - name: SPRING_CLOUD_VAULT_KUBERNETES_ROLE + value: agents-api - name: VAULT_DB_ENABLED value: 'true' - name: DB_HOST diff --git a/platform/cluster/flux/apps/stateless/auth-api/deployment.yaml b/platform/cluster/flux/apps/stateless/auth-api/deployment.yaml index 587e25fc..0f1424da 100644 --- a/platform/cluster/flux/apps/stateless/auth-api/deployment.yaml +++ b/platform/cluster/flux/apps/stateless/auth-api/deployment.yaml @@ -56,7 +56,6 @@ spec: app.kubernetes.io/name: auth-api annotations: vault.hashicorp.com/agent-inject: 'true' - vault.hashicorp.com/agent-inject-token: 'true' vault.hashicorp.com/agent-pre-populate-only: 'true' vault.hashicorp.com/role: auth-api vault.hashicorp.com/agent-inject-secret-auth-api.env: secret/data/auth-api @@ -119,7 +118,6 @@ spec: - /bin/sh - -ec - | - export SPRING_CLOUD_VAULT_TOKEN="$(cat /vault/secrets/token)" set -a . /vault/secrets/auth-api.env set +a @@ -158,6 +156,10 @@ spec: value: 'true' - name: VAULT_ADDR value: http://vault.data-system.svc.cluster.local:8200 + - name: VAULT_AUTHENTICATION + value: KUBERNETES + - name: VAULT_KUBERNETES_ROLE + value: auth-api - name: VAULT_DB_ENABLED value: 'true' - name: DB_HOST diff --git a/services/auth-api/src/main/resources/application.yml b/services/auth-api/src/main/resources/application.yml index a5165144..d0530fcd 100644 --- a/services/auth-api/src/main/resources/application.yml +++ b/services/auth-api/src/main/resources/application.yml @@ -15,6 +15,17 @@ spring: enabled: ${VAULT_ENABLED:false} uri: ${VAULT_ADDR:http://localhost:8200} authentication: ${VAULT_AUTHENTICATION:TOKEN} + # KUBERNETES auth lets the session manager re-login once a token can no + # longer be renewed. A static token cannot: Vault's kubernetes mount caps + # renewal at max_lease_ttl (32d), after which every call 403s for the + # remaining life of the pod. + kubernetes: + role: ${VAULT_KUBERNETES_ROLE:auth-api} + kubernetes-path: kubernetes + service-account-token-file: /var/run/secrets/kubernetes.io/serviceaccount/token + session: + lifecycle: + enabled: true config: lifecycle: enabled: true