From 0847e11713ca3e22c583aff9eb64c6ab4422a054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Tailland?= Date: Mon, 31 Aug 2026 07:56:13 +0200 Subject: [PATCH] Gate the n8n trigger endpoints on Keycloak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every webhook on this instance was answering unauthenticated requests, the stock watchlist included — it returned references and stock levels and let anyone clear entries. The A2A facade in front of these workflows enforces a bearer, but the workflows stayed reachable directly, facade bypassed. Keycloak's userinfo is the authenticator: 200 for a valid bearer in the realm, 401 otherwise, which is the auth-request contract exactly. Co-Authored-By: Claude Opus 5 --- deploy/flux-kustomization.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/deploy/flux-kustomization.yaml b/deploy/flux-kustomization.yaml index 38a47d1..7f5c3dd 100644 --- a/deploy/flux-kustomization.yaml +++ b/deploy/flux-kustomization.yaml @@ -27,6 +27,16 @@ spec: TIMEZONE: Europe/Paris INGRESS_CLASS: nginx INGRESS_BODY_SIZE: "50m" + # Every trigger endpoint is gated by this. Keycloak's userinfo answers 200 + # for a valid bearer in the realm and 401 otherwise, which is exactly the + # auth-request contract, so nothing had to be built for it. In-cluster, + # over http: the subrequest never leaves the cluster and needs no CA. + # + # It authenticates, it does not authorise: any token this realm issued + # gets through, including an agent's own service account. The finer grain + # belongs to the workflow, which exchanges the caller's token and is + # refused by the domain if the caller may not read it. + N8N_WEBHOOK_AUTH_URL: http://keycloak.keycloak.svc.cluster.local:8080/realms/classic-models/protocol/openid-connect/userinfo TLS_ISSUER_NAME: sttlab-local-ca TLS_ISSUER_KIND: ClusterIssuer VAULT_SERVER: https://vault.sttlab.eu