Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions charts/nebari-apps/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ wrong type fails the install.
| mcp.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. |
| mcp.image.repository | string | `"ghcr.io/nebari-dev/apps-pack/apps-mcp"` | apps-mcp image repository. |
| mcp.image.tag | string | `""` | Image tag. Empty defaults to the chart's appVersion. |
| mcp.orgCABundle.configMapName | string | `""` | ConfigMap in the release namespace holding the bundle. Empty disables it and the image's default roots apply. |
| mcp.orgCABundle.key | string | `"ca-bundle.crt"` | Key in the ConfigMap that holds the bundle. |
| mcp.replicaCount | int | `1` | apps-mcp replicas. |
| mcp.resources | object | `{"limits":{"cpu":"250m","memory":"256Mi"},"requests":{"cpu":"250m","memory":"256Mi"}}` | Container resources. Requests equal limits, so the pod is Guaranteed. |
| nameOverride | string | `""` | Override the chart name used in resource names. |
Expand Down
20 changes: 20 additions & 0 deletions charts/nebari-apps/templates/mcp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ spec:
value: http://{{ include "nebari-apps.fullname" . }}-api.{{ .Release.Namespace }}.svc.cluster.local:8080
# The SPA id: bearer passthrough may carry a UI token.
{{- include "nebari-apps.oidcEnv" (dict "root" . "spaVar" "OIDC_SPA_CLIENT_ID") | nindent 12 }}
{{- if .Values.mcp.orgCABundle.configMapName }}
# httpx (the device flow's Keycloak calls) reads SSL_CERT_FILE.
- name: SSL_CERT_FILE
value: /etc/ssl/nebari/ca-bundle.crt
{{- end }}
{{- /* Last, so an extraEnv entry overrides a chart variable of the same name. */}}
{{- with .Values.mcp.extraEnv }}
{{- toYaml . | nindent 12 }}
Expand Down Expand Up @@ -62,10 +67,25 @@ spec:
# The only writable path: Python's tempfile and any library cache.
- name: tmp
mountPath: /tmp
{{- if .Values.mcp.orgCABundle.configMapName }}
- name: org-ca
mountPath: /etc/ssl/nebari
readOnly: true
{{- end }}
volumes:
- name: tmp
emptyDir:
sizeLimit: 64Mi
{{- with .Values.mcp.orgCABundle }}
{{- if .configMapName }}
- name: org-ca
configMap:
name: {{ .configMapName }}
items:
- key: {{ .key }}
path: ca-bundle.crt
{{- end }}
{{- end }}
---
apiVersion: v1
kind: Service
Expand Down
16 changes: 16 additions & 0 deletions charts/nebari-apps/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,22 @@
"additionalProperties": false
},
"description": "Extra container env vars (Kubernetes EnvVar)."
},
"orgCABundle": {
"type": "object",
"properties": {
"configMapName": {
"type": "string",
"description": "ConfigMap in the release namespace holding a PEM CA bundle; empty disables it."
},
"key": {
"type": "string",
"minLength": 1,
"description": "Key in the ConfigMap that holds the bundle."
}
},
"additionalProperties": false,
"description": "CA bundle apps-mcp trusts for outbound HTTPS (SSL_CERT_FILE)."
}
},
"additionalProperties": false
Expand Down
10 changes: 10 additions & 0 deletions charts/nebari-apps/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,16 @@ mcp:
# -- Extra environment variables for apps-mcp, after the chart's own (an
# entry with the same name wins).
extraEnv: []
# A PEM CA bundle apps-mcp trusts for outbound HTTPS, for clusters whose
# Keycloak certificate (`keycloak.url`, which the login device flow calls)
# is signed by a private CA. The bundle replaces the image's trust store,
# so include the public roots too if anything else needs them.
orgCABundle:
# -- ConfigMap in the release namespace holding the bundle. Empty
# disables it and the image's default roots apply.
configMapName: ""
# -- Key in the ConfigMap that holds the bundle.
key: ca-bundle.crt

# =============================================================================
# Keycloak (used when api.auth.enabled)
Expand Down
3 changes: 2 additions & 1 deletion docs/src/content/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,12 @@ in).
| `mcp.image.repository` / `tag` / `pullPolicy` | `ghcr.io/nebari-dev/apps-pack/apps-mcp` / `""` / `IfNotPresent` | MCP image. |
| `mcp.replicaCount` | `1` | **Keep at 1**: device-flow sessions are held in process memory. |
| `mcp.resources` | `250m/256Mi` (requests = limits) | Pod resources. |
| `mcp.orgCABundle.configMapName` / `key` | `""` / `ca-bundle.crt` | ConfigMap (release namespace) with a PEM CA bundle the MCP server trusts for outbound HTTPS. Set it when `keycloak.url` has a certificate from a private CA, or `authenticate` fails with `CERTIFICATE_VERIFY_FAILED`. The bundle replaces the image's trust store, so include the public roots too. |

Environment: `API_URL`, `AUTH_ENABLED`, `OIDC_ISSUER`, `OIDC_ISSUER_INTERNAL`,
`OIDC_DEVICE_CLIENT_ID` (from the UI's OIDC Secret); optional `OIDC_JWKS_URL`,
`OIDC_AUDIENCE`, `HOST`, `PORT`, `LOG_LEVEL`, `MCP_SESSION_TTL_SECONDS` (default 28800:
how long an idle device-flow login is kept).
how long an idle device-flow login is kept), `SSL_CERT_FILE` (set by `mcp.orgCABundle`).

## Logs and probes

Expand Down
5 changes: 5 additions & 0 deletions docs/src/content/docs/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ Enforcement is layered:
On clusters with auth disabled, `authenticate` reports `not_required` and every tool works
anonymously.

The device flow calls Keycloak at its browser-facing URL (`keycloak.url`) from inside the
cluster. If that certificate comes from a private CA, `authenticate` fails with
`CERTIFICATE_VERIFY_FAILED` until the CA is trusted through
[`mcp.orgCABundle`](/configuration/#apps-mcp).

## Tools

| Tool | Purpose |
Expand Down
Loading