fix(oidc): Switch LibreChat and MCP OAuth to local Keycloak (keycloak.sttlab.local)
This commit is contained in:
+19
-4
@@ -6,14 +6,29 @@ metadata:
|
|||||||
data:
|
data:
|
||||||
HOST: "0.0.0.0"
|
HOST: "0.0.0.0"
|
||||||
PORT: "3080"
|
PORT: "3080"
|
||||||
DOMAIN_CLIENT: "https://${DOMAIN}"
|
DOMAIN_CLIENT: "https://librechat.sttlab.local"
|
||||||
DOMAIN_SERVER: "https://${DOMAIN}"
|
DOMAIN_SERVER: "https://librechat.sttlab.local"
|
||||||
MEILI_HOST: "http://meilisearch:7700"
|
MEILI_HOST: "http://meilisearch:7700"
|
||||||
NODE_ENV: "production"
|
NODE_ENV: "production"
|
||||||
ALLOW_EMAIL_LOGIN: "true"
|
ALLOW_EMAIL_LOGIN: "true"
|
||||||
ALLOW_REGISTRATION: "true"
|
ALLOW_REGISTRATION: "true"
|
||||||
ALLOW_SOCIAL_LOGIN: "false"
|
ALLOW_SOCIAL_LOGIN: "true"
|
||||||
ALLOW_SOCIAL_REGISTRATION: "false"
|
ALLOW_SOCIAL_REGISTRATION: "true"
|
||||||
|
# Keycloak SSO, additive alongside email/password login (not replacing it
|
||||||
|
# — ALLOW_EMAIL_LOGIN stays true). OPENID_CLIENT_SECRET/OPENID_SESSION_SECRET
|
||||||
|
# are in librechat-secrets (see librechat/deployment.yaml).
|
||||||
|
OPENID_ISSUER: "https://keycloak.sttlab.local/realms/classic-models"
|
||||||
|
OPENID_CLIENT_ID: "librechat"
|
||||||
|
OPENID_CALLBACK_URL: "/oauth/openid/callback"
|
||||||
|
OPENID_SCOPE: "openid profile email librechat-audience"
|
||||||
|
OPENID_BUTTON_LABEL: "classic-models SSO"
|
||||||
|
# RP-initiated logout — without this, switching Keycloak users silently
|
||||||
|
# reuses the existing SSO session instead of re-prompting.
|
||||||
|
OPENID_USE_END_SESSION_ENDPOINT: "true"
|
||||||
SESSION_EXPIRY: "900000"
|
SESSION_EXPIRY: "900000"
|
||||||
REFRESH_TOKEN_EXPIRY: "604800000"
|
REFRESH_TOKEN_EXPIRY: "604800000"
|
||||||
DEBUG_LOGGING: "false"
|
DEBUG_LOGGING: "false"
|
||||||
|
OTEL_TRACING_ENABLED: "true"
|
||||||
|
OTEL_SERVICE_NAME: "librechat"
|
||||||
|
OTEL_EXPORTER_OTLP_ENDPOINT: "http://jaeger.observability.svc.cluster.local:4318"
|
||||||
|
OTEL_TRACES_EXPORTER: "otlp"
|
||||||
|
|||||||
@@ -9,17 +9,22 @@ metadata:
|
|||||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||||
nginx.ingress.kubernetes.io/proxy-buffering: "off"
|
nginx.ingress.kubernetes.io/proxy-buffering: "off"
|
||||||
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
|
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
|
||||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
# configuration-snippet (WebSocket Upgrade/Connection headers) removed —
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
# this cluster's ingress-nginx has snippet annotations disabled
|
||||||
proxy_set_header Connection "upgrade";
|
# (admission webhook rejects them outright). If any LibreChat feature
|
||||||
|
# turns out to need raw WebSocket upgrade specifically (vs. the SSE/
|
||||||
|
# HTTP-1.1 streaming already covered by proxy-buffering/proxy-http-version
|
||||||
|
# above), this needs a different mechanism — e.g. a ConfigMap-based
|
||||||
|
# global nginx snippet if the cluster admin allows one, since per-Ingress
|
||||||
|
# snippets are blocked here.
|
||||||
spec:
|
spec:
|
||||||
ingressClassName: nginx
|
ingressClassName: nginx
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
- librechat.sttlab.pc
|
- librechat.sttlab.local
|
||||||
secretName: sttlab-tls
|
secretName: sttlab-tls
|
||||||
rules:
|
rules:
|
||||||
- host: librechat.sttlab.pc
|
- host: librechat.sttlab.local
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
||||||
|
|||||||
@@ -5,11 +5,44 @@ metadata:
|
|||||||
namespace: librechat
|
namespace: librechat
|
||||||
data:
|
data:
|
||||||
librechat.yaml: |
|
librechat.yaml: |
|
||||||
version: 1.3.9
|
version: 1.3.13
|
||||||
mcpSettings:
|
mcpSettings:
|
||||||
allowedDomains:
|
allowedDomains:
|
||||||
- "http://mcp-jira.mcp.svc.cluster.local:9000"
|
- "https://catalog-dev.sttlab.local"
|
||||||
mcpServers:
|
mcpServers:
|
||||||
jira:
|
catalog-mcp:
|
||||||
type: streamable-http
|
type: streamable-http
|
||||||
url: http://mcp-jira.mcp.svc.cluster.local:9000/mcp
|
url: https://catalog-dev.sttlab.local/catalog-mcp
|
||||||
|
oauth:
|
||||||
|
authorization_url: https://keycloak.sttlab.local/realms/classic-models/protocol/openid-connect/auth
|
||||||
|
token_url: https://keycloak.sttlab.local/realms/classic-models/protocol/openid-connect/token
|
||||||
|
client_id: librechat
|
||||||
|
client_secret: '${LIBRECHAT_OIDC_CLIENT_SECRET}'
|
||||||
|
redirect_uri: https://librechat.sttlab.local/api/mcp/catalog-mcp/oauth/callback
|
||||||
|
scope: 'openid mcp-catalog-audience kong-token-exchange-broker-audience'
|
||||||
|
grant_types_supported: ['authorization_code', 'refresh_token']
|
||||||
|
token_endpoint_auth_methods_supported: ['client_secret_post']
|
||||||
|
endpoints:
|
||||||
|
custom:
|
||||||
|
# kong-v2's platform-llm proxy (not the old kube/llms/openrouter lab)
|
||||||
|
# — key-auth, same shared-key limitation as before (LibreChat's
|
||||||
|
# custom endpoints don't support OAuth/OIDC on this outbound leg).
|
||||||
|
# baseURL is `/llm/v1`, not the full path: LibreChat appends
|
||||||
|
# `/chat/completions` itself.
|
||||||
|
- name: 'OpenRouter'
|
||||||
|
apiKey: '${PLATFORM_LLM_LIBRECHAT_APIKEY}'
|
||||||
|
baseURL: 'https://llm-dev.sttlab.local/llm/v1'
|
||||||
|
headers:
|
||||||
|
apikey: '${PLATFORM_LLM_LIBRECHAT_APIKEY}'
|
||||||
|
models:
|
||||||
|
default:
|
||||||
|
- 'google/gemini-3.7-flash'
|
||||||
|
- 'anthropic/claude-sonnet-5'
|
||||||
|
- 'openai/gpt-5.6-luna'
|
||||||
|
- 'deepseek/deepseek-v4-flash'
|
||||||
|
- 'openai/gpt-oss-120b'
|
||||||
|
- 'mistralai/mistral-nemo'
|
||||||
|
fetch: false
|
||||||
|
titleConvo: true
|
||||||
|
titleModel: 'google/gemini-3.7-flash'
|
||||||
|
modelDisplayLabel: 'OpenRouter'
|
||||||
|
|||||||
@@ -13,6 +13,18 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
app: librechat
|
app: librechat
|
||||||
spec:
|
spec:
|
||||||
|
# *.sttlab.local are lab-only hostnames (self-signed cert), not real
|
||||||
|
# DNS — this pod needs an explicit mapping to each Gateway's stable
|
||||||
|
# in-cluster ClusterIP to resolve them at all.
|
||||||
|
# catalog-dev: kong-v2 catalog-api Gateway (dataplane-ingress-catalog-dev-v4bkm-xqq6v), REST+MCP share this hostname now.
|
||||||
|
# llm-dev: kong-v2's platform-llm Gateway (dataplane-ingress-llm-dev-rlrxf-7fkb7).
|
||||||
|
hostAliases:
|
||||||
|
- ip: "192.168.194.225"
|
||||||
|
hostnames:
|
||||||
|
- "catalog-dev.sttlab.local"
|
||||||
|
- ip: "192.168.194.153"
|
||||||
|
hostnames:
|
||||||
|
- "llm-dev.sttlab.local"
|
||||||
initContainers:
|
initContainers:
|
||||||
- name: wait-for-mongodb
|
- name: wait-for-mongodb
|
||||||
image: busybox:1.36
|
image: busybox:1.36
|
||||||
@@ -81,13 +93,51 @@ spec:
|
|||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: librechat-secrets
|
name: librechat-secrets
|
||||||
key: ANTHROPIC_API_KEY
|
key: ANTHROPIC_API_KEY
|
||||||
|
- name: PLATFORM_LLM_LIBRECHAT_APIKEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: librechat-secrets
|
||||||
|
key: PLATFORM_LLM_LIBRECHAT_APIKEY
|
||||||
|
- name: LIBRECHAT_OIDC_CLIENT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: librechat-secrets
|
||||||
|
key: LIBRECHAT_OIDC_CLIENT_SECRET
|
||||||
|
# Same Keycloak client secret as LIBRECHAT_OIDC_CLIENT_SECRET
|
||||||
|
# above (one Keycloak client, "librechat", used both for the
|
||||||
|
# app's own SSO login and for each MCP server's OAuth flow) —
|
||||||
|
# LibreChat's OIDC-login code path reads this specific env var
|
||||||
|
# name, so it's duplicated rather than referenced once.
|
||||||
|
- name: OPENID_CLIENT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: librechat-secrets
|
||||||
|
key: LIBRECHAT_OIDC_CLIENT_SECRET
|
||||||
|
- name: OPENID_SESSION_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: librechat-secrets
|
||||||
|
key: OPENID_SESSION_SECRET
|
||||||
|
# jira-mcp.sttlab.local / konggw.sttlab.local serve a self-signed
|
||||||
|
# cert (see hostAliases comment above) — Node rejects it by
|
||||||
|
# default; NODE_EXTRA_CA_CERTS trusts just this one CA, unlike
|
||||||
|
# NODE_TLS_REJECT_UNAUTHORIZED=0 which would disable TLS
|
||||||
|
# verification globally.
|
||||||
|
- name: NODE_EXTRA_CA_CERTS
|
||||||
|
value: /etc/sttlab-tls/tls.crt
|
||||||
|
# 512Mi was too tight for LibreChat under real use (MCP clients,
|
||||||
|
# OTel instrumentation) and caused repeated V8 heap-limit crashes.
|
||||||
|
# Cap old-space explicitly, leaving headroom under the new
|
||||||
|
# container limit for non-heap memory (buffers, native modules).
|
||||||
|
- name: NODE_OPTIONS
|
||||||
|
value: "--max-old-space-size=1200"
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 100m
|
cpu: 100m
|
||||||
memory: 256Mi
|
memory: 512Mi
|
||||||
limits:
|
limits:
|
||||||
cpu: 500m
|
cpu: 500m
|
||||||
memory: 512Mi
|
memory: 1536Mi
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /health
|
path: /health
|
||||||
@@ -104,7 +154,16 @@ spec:
|
|||||||
- name: app-config
|
- name: app-config
|
||||||
mountPath: /app/librechat.yaml
|
mountPath: /app/librechat.yaml
|
||||||
subPath: librechat.yaml
|
subPath: librechat.yaml
|
||||||
|
- name: sttlab-tls-cert
|
||||||
|
mountPath: /etc/sttlab-tls
|
||||||
|
readOnly: true
|
||||||
volumes:
|
volumes:
|
||||||
- name: app-config
|
- name: app-config
|
||||||
configMap:
|
configMap:
|
||||||
name: librechat-app-config
|
name: librechat-app-config
|
||||||
|
- name: sttlab-tls-cert
|
||||||
|
secret:
|
||||||
|
secretName: sttlab-tls
|
||||||
|
items:
|
||||||
|
- key: tls.crt
|
||||||
|
path: tls.crt
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ stringData:
|
|||||||
# Optional: fill in your AI provider API keys
|
# Optional: fill in your AI provider API keys
|
||||||
OPENAI_API_KEY: ""
|
OPENAI_API_KEY: ""
|
||||||
ANTHROPIC_API_KEY: ""
|
ANTHROPIC_API_KEY: ""
|
||||||
|
# platform-llm proxy consumer key (kong-v2/platform-llm, consumer "librechat")
|
||||||
|
PLATFORM_LLM_LIBRECHAT_APIKEY: ""
|
||||||
|
LIBRECHAT_OIDC_CLIENT_SECRET: ""
|
||||||
|
OPENID_SESSION_SECRET: ""
|
||||||
|
|||||||
Reference in New Issue
Block a user