diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml new file mode 100644 index 0000000..b695060 --- /dev/null +++ b/k8s/ingress.yaml @@ -0,0 +1,70 @@ +# Reaching this agent from outside the cluster. +# +# Until now nothing did: an agent answered only what could call its Service, so +# every conversation with it went through `kubectl run curl`. An orchestrator +# nobody can talk to is not finished. +# +# Peer traffic does **not** come through here — catalogue calls, and is called by, +# other agents on their in-cluster Service names. This is the human door. +# +# ## It is not an open door +# +# A2A carries the end user's token in `Authorization`, and this agent forwards it +# and exchanges it; a forged one fails at the realm. But "fails downstream" is +# not the same as "refused", and an unauthenticated caller could still make the +# agent do work — inference costs money and a request that reaches an LLM is a +# prompt-injection surface. So the same authentication subrequest that guards the +# n8n endpoints guards this one: Keycloak's userinfo answers 200 for a valid +# bearer in the realm and 401 otherwise, which is the auth-request contract +# exactly. +# +# It authenticates, it does not authorise. What the caller may actually reach is +# decided further down, by the exchange and by the roles in their token. +# +# **The agent card is behind it too.** A2A discovery is normally public; here it +# is not, because a client that cannot send an `Authorization` header cannot use +# the agent anyway, and the card names the skills. +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: catalogue-agent-tls + namespace: agents +spec: + secretName: catalogue-agent-tls + dnsNames: + - catalogue-agent.sttlab.local + issuerRef: + name: sttlab-local-ca + kind: ClusterIssuer +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: catalogue-agent + namespace: agents + annotations: + nginx.ingress.kubernetes.io/auth-url: http://keycloak.keycloak.svc.cluster.local:8080/realms/classic-models/protocol/openid-connect/userinfo + # A2A streams a task's progress; buffering it defeats that. + nginx.ingress.kubernetes.io/proxy-buffering: "off" + nginx.ingress.kubernetes.io/proxy-http-version: "1.1" + # An agent turn is a model call and several hops. The nginx default of 60s + # cuts a working answer off mid-flight. + nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + nginx.ingress.kubernetes.io/proxy-send-timeout: "300" +spec: + ingressClassName: nginx + tls: + - hosts: + - catalogue-agent.sttlab.local + secretName: catalogue-agent-tls + rules: + - host: catalogue-agent.sttlab.local + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: catalogue-agent + port: + number: 8080 diff --git a/k8s/kustomization.yaml b/k8s/kustomization.yaml index 2d6888a..fb31aac 100644 --- a/k8s/kustomization.yaml +++ b/k8s/kustomization.yaml @@ -7,3 +7,4 @@ resources: - configmap.yaml - deployment.yaml - service.yaml + - ingress.yaml