Files
infra-n8n/k8s/ingress/ingress-webhooks.yaml
Stéphane Tailland e9d7504fcd Put every trigger endpoint behind an authentication subrequest
n8n's webhook, form and mcp endpoints carry no authentication of their own, and
for an automation platform those endpoints are the platform: whatever a
workflow reads or changes answers an unauthenticated request. The editor's
login protects the editor and nothing else.

N8N_WEBHOOK_AUTH_URL is required rather than optional — there is no way to
deploy this without naming an authenticator.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 07:56:03 +02:00

74 lines
2.7 KiB
YAML

# Webhook endpoints, authenticated.
#
# n8n serves a workflow's trigger endpoints on the same host as the editor, and
# they carry **no authentication of their own** — a workflow behind one answers
# anybody who can reach the host. That is not a detail: for an automation
# platform, those endpoints are the platform. Anything a workflow can read or
# change is reachable by an unauthenticated request unless something in front
# says otherwise.
#
# So this Ingress puts every trigger path behind an authentication subrequest.
# `${N8N_WEBHOOK_AUTH_URL}` must answer 2xx for an authorised request and
# 401/403 otherwise; an OIDC provider's userinfo endpoint has exactly that
# behaviour and needs nothing built. nginx forwards the caller's own headers to
# it, so a bearer on the original request is what gets checked.
#
# The editor keeps its own path (`/`) on the main Ingress and its own session
# login. Callers inside the cluster reach the Service directly and never pass
# through here — which is how an A2A facade in front of a workflow keeps working
# while the outside door is closed.
#
# Every trigger prefix n8n can serve is listed. One left out is the whole hole
# again, and the list is configurable per deployment
# (N8N_ENDPOINT_WEBHOOK and friends) — if you rename one, rename it here too.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: n8n-webhooks
namespace: ${NAMESPACE}
annotations:
nginx.ingress.kubernetes.io/auth-url: ${N8N_WEBHOOK_AUTH_URL}
nginx.ingress.kubernetes.io/proxy-body-size: ${INGRESS_BODY_SIZE}
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
spec:
ingressClassName: ${INGRESS_CLASS}
tls:
- hosts:
- ${N8N_HOSTNAME}
secretName: n8n-tls
rules:
- host: ${N8N_HOSTNAME}
http:
paths:
- path: /webhook
pathType: Prefix
backend: &n8n
service:
name: n8n
port:
number: 80
- path: /webhook-test
pathType: Prefix
backend: *n8n
- path: /webhook-waiting
pathType: Prefix
backend: *n8n
- path: /form
pathType: Prefix
backend: *n8n
- path: /form-test
pathType: Prefix
backend: *n8n
- path: /form-waiting
pathType: Prefix
backend: *n8n
- path: /mcp
pathType: Prefix
backend: *n8n
- path: /mcp-test
pathType: Prefix
backend: *n8n