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
40 changes: 30 additions & 10 deletions .github/hack/cleanup-odh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# - ODH operator namespace (odh-operator)
# - OpenDataHub application namespace (opendatahub)
# - MaaS subscription namespace (models-as-a-service)
# - Keycloak identity provider (if deployed)
# - ODH CRDs (optional)
#
# Usage: ./cleanup-odh.sh [--include-crds]
Expand Down Expand Up @@ -123,33 +124,52 @@ for policy_ns in kuadrant-system rh-connectivity-link; do
"authorinos.operator.authorino.kuadrant.io" "kuadrants.kuadrant.io" "limitadors.limitador.kuadrant.io"
done

# 11. Delete llm namespace and model resources
echo "11. Deleting LLM models and namespace..."
# 11. Delete Keycloak identity provider (if installed)
echo "11. Deleting Keycloak namespace (if installed)..."
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd ../.. && pwd)"
if [[ -f "${SCRIPT_DIR}/scripts/cleanup-keycloak.sh" ]]; then
# Pass --delete-crds if --include-crds was specified for this script
if $INCLUDE_CRDS; then
"${SCRIPT_DIR}/scripts/cleanup-keycloak.sh" --force --delete-crds 2>/dev/null || true
else
"${SCRIPT_DIR}/scripts/cleanup-keycloak.sh" --force 2>/dev/null || true
fi
else
# Fallback if cleanup script not found - direct cleanup
force_delete_namespace "keycloak-system" "keycloaks.k8s.keycloak.org"
if $INCLUDE_CRDS; then
kubectl delete crd keycloaks.k8s.keycloak.org --ignore-not-found 2>/dev/null || true
kubectl delete crd keycloakrealmimports.k8s.keycloak.org --ignore-not-found 2>/dev/null || true
fi
fi

# 12. Delete llm namespace and model resources
echo "12. Deleting LLM models and namespace..."
force_delete_namespace "llm" "llminferenceservice" "inferenceservice" "maasmodelrefs.maas.opendatahub.io"

# 12. Delete gateway resources in openshift-ingress
echo "12. Deleting gateway resources..."
# 13. Delete gateway resources in openshift-ingress
echo "13. Deleting gateway resources..."
kubectl delete gateway maas-default-gateway -n openshift-ingress --ignore-not-found 2>/dev/null || true
kubectl delete envoyfilter -n openshift-ingress -l kuadrant.io/managed=true --ignore-not-found 2>/dev/null || true
kubectl delete envoyfilter kuadrant-auth-tls-fix -n openshift-ingress --ignore-not-found 2>/dev/null || true
kubectl delete authpolicy -n openshift-ingress --all --ignore-not-found 2>/dev/null || true
kubectl delete ratelimitpolicy -n openshift-ingress --all --ignore-not-found 2>/dev/null || true
kubectl delete tokenratelimitpolicy -n openshift-ingress --all --ignore-not-found 2>/dev/null || true

# 13. Delete MaaS RBAC (ClusterRoles, ClusterRoleBindings - can conflict with other managers)
echo "13. Deleting MaaS RBAC..."
# 14. Delete MaaS RBAC (ClusterRoles, ClusterRoleBindings - can conflict with other managers)
echo "14. Deleting MaaS RBAC..."
kubectl delete clusterrolebinding maas-api maas-controller-rolebinding --ignore-not-found 2>/dev/null || true
kubectl delete clusterrole maas-api maas-controller-role --ignore-not-found 2>/dev/null || true

# 14. Optionally delete CRDs
# 15. Optionally delete CRDs
if $INCLUDE_CRDS; then
echo "14. Deleting ODH CRDs..."
echo "15. Deleting ODH CRDs..."
kubectl delete crd datascienceclusters.datasciencecluster.opendatahub.io --ignore-not-found 2>/dev/null || true
kubectl delete crd dscinitializations.dscinitialization.opendatahub.io --ignore-not-found 2>/dev/null || true
kubectl delete crd datasciencepipelinesapplications.datasciencepipelinesapplications.opendatahub.io --ignore-not-found 2>/dev/null || true
# Add more CRDs as needed
else
echo "14. Skipping CRD deletion (use --include-crds to remove CRDs)"
echo "15. Skipping CRD deletion (use --include-crds to remove CRDs)"
fi

echo ""
Expand All @@ -158,4 +178,4 @@ echo ""
echo "Verify cleanup with:"
echo " kubectl get subscription -A | grep -i odh"
echo " kubectl get csv -A | grep -i odh"
echo " kubectl get ns | grep -E 'odh|opendatahub|models-as-a-service|kuadrant|rh-connectivity-link|llm'"
echo " kubectl get ns | grep -E 'odh|opendatahub|models-as-a-service|kuadrant|rh-connectivity-link|keycloak-system|llm'"
18 changes: 9 additions & 9 deletions docs/content/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ graph TB

**Flow summary:**

1. User sends `POST /v1/api-keys` with Bearer `{oc-token}`.
1. User sends `POST /v1/api-keys` with Bearer `{identity-token}`.
2. Gateway routes the request to AuthPolicy (Authorino).
3. AuthPolicy validates the OpenShift token via TokenReview.
3. AuthPolicy validates the presented identity token via the configured auth method (`kubernetesTokenReview` for OpenShift, or OIDC JWT validation when enabled).
4. Gateway forwards the authenticated request and user context to the Key Minting Service.

```mermaid
Expand All @@ -88,18 +88,18 @@ graph TB
KMS[MaaS API]
end

U -->|"1. POST /v1/api-keys<br/>Bearer {oc-token}"| G
U -->|"1. POST /v1/api-keys<br/>Bearer {identity-token}"| G
G -->|"2. Route /maas-api"| AP
AP -->|"3. TokenReview<br/>validate OpenShift token"| G
AP -->|"3. Validate identity token<br/>TokenReview or OIDC JWT"| G
G -->|"4. Forward + user context"| KMS

style KMS fill:#1976d2,stroke:#333,stroke-width:2px,color:#fff
style G fill:#7b1fa2,stroke:#333,stroke-width:2px,color:#fff
style AP fill:#e65100,stroke:#333,stroke-width:2px,color:#fff
```

!!! Tip "Future Plans"
Today, validation uses the **OpenShift token flow** (TokenReview). Future plans include optional integration with other OIDC providers (e.g., external IdPs, Keycloak).
!!! Tip "OIDC Support"
The `maas-api` route can be configured to validate external OIDC tokens (for example Keycloak-issued JWTs) in addition to the existing OpenShift TokenReview flow. Model routes still use the current API-key policy, so the interim OIDC flow is: authenticate with OIDC at `maas-api`, mint an `sk-oai-*` key, then use that key for model discovery and inference.


### Key Minting Service (Default Implementation)
Expand Down Expand Up @@ -288,7 +288,7 @@ graph LR

### 1. API Key Creation Flow (MaaS API)

Users create API keys by authenticating with their OpenShift token. The MaaS API generates a key, stores only the hash in PostgreSQL, and returns the plaintext once:
Users create API keys by authenticating with an accepted identity token (OpenShift today, or OIDC when configured on the `maas-api` route). The MaaS API generates a key, stores only the hash in PostgreSQL, and returns the plaintext once:

```mermaid
sequenceDiagram
Expand All @@ -298,9 +298,9 @@ sequenceDiagram
participant MaaS as MaaS API
participant DB as PostgreSQL

User->>Gateway: POST /maas-api/v1/api-keys<br/>Authorization: Bearer {openshift-token}
User->>Gateway: POST /maas-api/v1/api-keys<br/>Authorization: Bearer {identity-token}
Gateway->>Authorino: Enforce MaaS API AuthPolicy
Authorino->>Authorino: TokenReview (validate OpenShift token)
Authorino->>Authorino: Validate token (TokenReview or OIDC JWT)
Authorino->>Gateway: Authenticated
Gateway->>MaaS: Forward request with user context

Expand Down
26 changes: 18 additions & 8 deletions docs/content/configuration-and-management/token-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ The validation endpoint (`/internal/v1/api-keys/validate`) is called by Authorin

## Model Discovery

The `/v1/models` endpoint allows you to discover which models you're authorized to access. This endpoint works with any valid authentication token — you can use your OpenShift token or an API key.
The `/v1/models` endpoint allows you to discover which models you're authorized to access. The API forwards the same `Authorization` header you send to each model route, so the result depends on what those model routes accept.

### How It Works

Expand All @@ -138,12 +138,22 @@ flowchart LR

This means you can:

1. **Authenticate with OpenShift or OIDC** — use your existing identity token for `GET /v1/models` (optional `X-MaaS-Subscription` when you have multiple subscriptions).
2. **Use an API key** — use your `sk-oai-*` key in the Authorization header for listing and for inference.
3. **Call `/v1/models` immediately** — see only the models you can access, without creating an API key first (if using an OpenShift token).
1. **Use an API key** — this is the most portable option because the current model-route AuthPolicies already validate `sk-oai-*` keys.
2. **Use an identity token directly** — only when the model routes themselves accept that token type.
3. **Create a key first for the interim OIDC flow** — when OIDC is enabled only on the `maas-api` route, use your OIDC token to call `POST /v1/api-keys`, then call `/v1/models` with the minted API key.

!!! note "Inference vs listing"
Inference (calls to each model’s chat/completions URL) requires an API key in `Authorization: Bearer` only. Do not send `X-MaaS-Subscription` on inference—the subscription is the one bound at API key mint time. `GET /v1/models` accepts either an API key or an OpenShift token; with a user token, `X-MaaS-Subscription` remains supported for filtering.
Inference (calls to each model's chat/completions URL) requires an API key in `Authorization: Bearer` only. Do not send `X-MaaS-Subscription` on inference—the subscription is the one bound at API key mint time. `GET /v1/models` accepts either an API key or an OpenShift token; with a user token, `X-MaaS-Subscription` remains supported for filtering.

### Interim OIDC Flow

When the `maas-api` AuthPolicy is configured for OIDC but model HTTPRoutes still use the existing API-key-only policy, the flow is:

1. Authenticate to your IdP and obtain an OIDC access token.
2. Call `POST /v1/api-keys` with that OIDC token.
3. Use the returned `sk-oai-*` key for `GET /v1/models` and inference requests.

This preserves compatibility with the current model-route policy while allowing non-OpenShift identities to onboard through `maas-api`.

---

Expand Down Expand Up @@ -236,15 +246,15 @@ A: Yes. Your API key is bound to a subscription at creation time. If that subscr

---

**Q: What's the difference between my OpenShift token and an API key?**
**Q: What's the difference between my OpenShift/OIDC token and an API key?**

A: Your **OpenShift token** is your identity token from authentication (e.g., OpenShift or OIDC). An **API key** is a long-lived credential created via `POST /v1/api-keys` and stored as a hash in PostgreSQL. For **GET /v1/models**, the API passes your Authorization header as-is to each model endpoint; you can use either. For inference, use API key.
A: Your **OpenShift/OIDC token** is your identity token from authentication. An **API key** is a long-lived credential created via `POST /v1/api-keys` and stored as a hash in PostgreSQL. The API passes your `Authorization` header as-is to each model endpoint, so use whichever credential type the model route accepts. In the current interim OIDC rollout, use the OIDC token to mint an API key first, then use that key for `/v1/models` and inference.

---

**Q: Do I need an API key to list available models?**

A: No. Call **GET /v1/models** with your OpenShift/OIDC token (or an API key) in the Authorization header. The API uses that same header to probe each model endpoint and returns only models you can access.
A: Not always. If the target model routes accept your OpenShift or OIDC token directly, call **GET /v1/models** with that token. If only the `maas-api` route is OIDC-enabled and the model routes still use API-key auth, mint an API key with `POST /v1/api-keys` first and then call **GET /v1/models** with the returned key.

---

Expand Down
Loading
Loading