Chapter 8. Deploying OGX for multi-tenancy
As an OpenShift cluster administrator, you can use OGX to deploy a single server or cluster that manages multiple tenant deployments.
8.1. Overview of multi-tenancy on OGX Copy linkLink copied to clipboard!
Multi-tenancy allows teams to share infrastructure while isolating data and access. Without multi-tenancy controls, any authenticated user can view, modify or delete any other user’s resources or applications.
8.1.1. Single-server vs Multi-server environments Copy linkLink copied to clipboard!
Single-server: A single OGX server serves all tenants. Tenant administrators manage the configurations on the shared server and provision resources to tenant users. Isolation is enforced at the application layer with JSON Web Token (JWT) validation and Attribute-Based Access Control (ABAC).
When to use single-server multi-tenancy
Single-server multi-tenancy is recommended when:
- Teams share a cluster and trust the platform, but require data separation.
- Environments where teams can share a single database, a single set of models, and a unified pod.
- Fast deployment is necessary without the overhead of provisioning new infrastructure.
Multi-server: The OGX Operator deploys multiple OGXServer custom resources (CRs) within tenant admin namespaces. Isolation is enforced at the infrastructure level with Role-Based Access Control (RBAC), NetworkPolicies, and ResourceQuotas.
When to use multi-server multi-tenancy
Multi-server multi-tenancy is recommended when:
- Tenant admins need full infrastructure isolation including, separate pods, databases, and storage.
- Environments with strict compliance where process-level separation is mandated.
- Tenant admins need different OGX configurations including different providers, models, and policies.
-
Tenant admins want to manage their own
OGXServerCR and the operator reconciles into a dedicated deployment, service, and storage.
8.1.2. Roles for multi-tenancy environments Copy linkLink copied to clipboard!
In OpenShift AI, there are various roles that manage or interact with the resources the single or multi server provides. A tenant consists of one or more namespaces that own OGXServer custom resources (CRs). By default, resources are isolated within the tenants designated namespace.
-
Platform Admin: Operates at the cluster or multi-server level, the platform admin installs the operator, provisions tenant namespaces, configures Role-Based Access Control (RBAC),
ResourceQuotasCRs, and manages CRDs. -
Tenant Admin: Operates at the namespace or single-server level, the tenant admin creates and manages
OGXServerCRs, configures OGX providers, secrets, and networking configurations. - Tenant User: Operates at the API level, the tenant user makes requests to an API endpoint without OpenShift cluster access.
8.1.3. Operator-enforced isolation Copy linkLink copied to clipboard!
The OGX Operator enforces security and administrative boundaries. By default, the operator enforces the following isolation boundaries:
-
Namespace-scoped resources: All resources created by the operator, including
Deployment,Service,ServiceAccount,RoleBinding,NetworkPolicy, orPersistentVolumeClaim, are created in the CR namespace. -
ConfigMap/ Secret references: All
ConfigMapand secret references in the custom resource specifications are restricted to the CR’s namespace. Network Isolation: The
NetworkPolicyspecification is created for each distribution with the following defaults:Expand Table 8.1. Network Isolation Rules Direction Default Rule Configurable Ingress
Allow from same namespace
Yes, with the
allowedFromparameterIngress
Allow from operator namespace
No, required for operator health checks
Ingress
Deny all other
Yes, with the
allowedFromparameterEgress
Unrestricted or no policy
Yes, via egress rules
Egress
Auto-include DNS (port 53)
No, always injected when egress rules are present
-
Server pod permissions: The server
ServiceAccounthas no Kubernetes API permissions. Secrets are injected as environment variables viasecretKeyRef, not read at runtime by the server.
8.2. Creating a single-server multi-tenant environment Copy linkLink copied to clipboard!
A single-server multi-tenant environment allows you to run a single OGX server with multiple users connecting to a single namespace.
As a tenant admin, you need to configure security boundaries, mapping identities, and networking configurations.
Supported authentication providers
- OAuth2 JWKS: Validates JWT with a JWKS endpoint, best used for Kubernetes OIDC, Keycloak and standard OIDC providers.
- OAuth2 Introspection: Validates tokens via RFC 7662, best used for legacy OAuth servers.
-
Kubernetes: Validates via the K8s
SelfSubjectReviewAPI, best used for native in-cluster service accounts. - GitHub: Validates GitHub PATs with the GitHub API, best used for open-source or deployments in GitHub environments.
- Upstream Header: Reads identity from gateway headers, best used for authorino, istio or API gateway setups.
- Custom: Forwards the token to a user-provided HTTP endpoint, best used for specific proprietary integrations.
Authorization Layers
OGX enforces security through two distinct authorization mechanisms:
Access Policy (ABAC) - This policy controls which specific resources a tenant user can create, read, update, and delete. This policy can be modified to allow team-based sharing.
Optional: Route Policy (RBAC) - OGX checks if the tenant users role is allowed to use the requested URL. For example, some tenant users can be restricted to inference endpoints while admins maintain full access
Route policy capability is available in OpenShift AI but is not included in the default distribution configuration. Utilization requires deploying custom config.yaml configuration.
Each OGX resource has different Isolation levels.
| Resource Name | Isolation Level | Sharing / Access Model |
|---|---|---|
| Responses | Isolated (ABAC) | Fully private, each tenant sees only their own data. |
| Files | Isolated (ABAC) | Fully private, each tenant sees only their own data. |
| Vector Stores | Isolated (ABAC) | Fully private, each tenant sees only their own data. |
| Batches | Isolated (ABAC) | Fully private, each tenant sees only their own data. |
| Interactions | Isolated (ABAC) | Fully private, each tenant sees only their own data. |
| Inference Store | Isolated (ABAC) | Fully private, each tenant sees only their own data. |
| Models | Shared (Configurable) | Infrastructure resource, available to all tenants by default. |
| Tool Groups | Shared (Configurable) | Available to all tenants by default, but can be restricted per-team via access policy. |
The following procedures describe how to set up an OGX server with custom authentication and routing
Prerequisites
-
You have installed the OpenShift CLI (
oc) - You have installed the OGX Operator on your OpenShift AI cluster.
Procedure
-
Deploy an
OGXServerCR with your custom configuration. For more information, see "Deploying an OGX Server". Configure claims mapping in the
config.yamlfileserver: auth: provider_config: claims_mapping: realm_access.roles: roles groups: teams tenant_id: namespacesYou now need to enable authentication on your server. You can enable Kubernetes OIDC, which is recommended for in-cluster workloads, or Keycloak for basic users and external clients.
Enabling authorization with Kubernetes OIDC
Get the clusters OIDC endpoints with the following command:
$ AUTH_ISSUER=$(oc get --raw /.well-known/openid-configuration | jq .issuer -r) $ AUTH_JWKS_URI=$(oc get --raw /.well-known/openid-configuration | jq .jwks_uri -r)Set the following additional environment variables:
AUTH_AUDIENCE=ogx AUTH_VERIFY_TLS=trueThe default distribution configuration activates when you set the necessary environment variables.
Default auth configuration YAML
auth: provider_config: type: ${env.AUTH_ISSUER:+oauth2_token} # activates only when AUTH_ISSUER is set audience: ${env.AUTH_AUDIENCE:=ogx} issuer: ${env.AUTH_ISSUER:=} jwks: uri: ${env.AUTH_JWKS_URI:=} verify_tls: ${env.AUTH_VERIFY_TLS:=true}
Enabling authorization with Keycloak
Set your Keycloak details in the
OGXServercustom resourceapiVersion: ogx.io/v1beta1 kind: OGXServer metadata: name: ogx-shared-server namespace: ogx-system spec: env: - name: AUTH_ISSUER value: "https://keycloak.example.com/realms/my-org" - name: AUTH_JWKS_URI value: "https://keycloak.example.com/realms/my-org/protocol/openid-connect/certs" - name: AUTH_AUDIENCE value: "ogx-api"
You then need to configure the access policy of your server. OpenShift AI ships a default access policy that provides owner-based isolation.
Default access policy YAML
auth: access_policy: # System resources are readable by all - permit: actions: [read] when: resource is unowned description: "All users can read system resources" # Any authenticated user can create resources - permit: actions: [create] description: "Authenticated users can create resources" # Only the owner can read, update, or delete resources - permit: actions: [read, update, delete] when: user is owner description: "Owners can manage their own resources"For team-based sharing where users can see the resources of users on the same team, use the following example
access_policyconfig:auth: access_policy: - permit: actions: [read] when: resource is unowned description: "All users can read system resources" - permit: actions: [create] description: "Authenticated users can create resources" - permit: actions: [read, update, delete] when: user is owner description: "Owners can manage their own resources" - permit: actions: [read] when: user in owners teams description: "Team members can read each others resources"For permissions where an admin can access everything, use the following example
access_policyconfig:auth: access_policy: # Admin bypass: full access to all resources - permit: actions: [create, read, update, delete] when: user with admin in roles description: "Admins have full access" # Standard user policies - permit: actions: [read] when: resource is unowned - permit: actions: [create] - permit: actions: [read, update, delete] when: user is owner
Create the project namespaces, for example:
$ oc new-project <project-a> $ oc new-project <project-b>Set the service accounts for each role, for example
$ oc create serviceaccount ogx-developer -n <project-a> $ oc create serviceaccount ogx-agent -n <project-b>The tenant admin is responsible for provisioning auth tokens for the tenant users. You can generate the tokens using Kubernetes OIDC, which is recommended for cluster workloads, or Keycloak for basic users or external clients.
Accessing a Kubernetes OIDC token
You can create a token based on team roles and project names:
$ TOKEN=$(oc create token ogx-developer -n <project-a> --audience ogx --duration=3600s)
Accessing a Keycloak token
Configure your
config.yamlfile to trust KeyCloakauth.provider_config.issuer: https://keycloak.example.com/realms/ai-platform auth.provider_config.jwks.uri: https://keycloak.example.com/realms/ai-platform/protocol/openid-connect/certsAccess and set the token environment variable:
TOKEN=$(curl -s -X POST \ "https://keycloak.example.com/realms/ai-platform/protocol/openid-connect/token" \ -d "grant_type=password&client_id=ogx&username=alice&password=***" \ | jq -r .access_token)
- Your tenant users can now access the resources on the namespace. For more information, see "Using APIs as a tenant user".
8.3. Creating a multi-server multi-tenant environment Copy linkLink copied to clipboard!
In OpenShift AI, the platform administrator can set up a multi-server multi-tenant environment, enabling tenant admins to manage namespaces for their respective tenant users.
Platform admin responsibilities
- Namespace Provisioning: Create and label tenant namespaces before CR creation.
-
RBAC: Create
RolesandRoleBindingsper tenant namespace - ResourceQuota/ LimitRange: Set per-namespace compute and object quotas.
- Monitoring: Monitor tenant resource usage and set alerts for quota pressure.
- (Optional) Network enforcement: Enable network isolation requirements.
Tenant admin responsibilities
-
Distribution configuration: Create and manage
OGXServerCRs in their namespaces. -
Resource limits: Configure
resources.requests,resources.limits, andmaxReplicason CRs. TheResourceQuotaCR provisioned by the platform admin enforces that these configurations stay within their namespace quota. - Secrets: Create and manage Secrets including, API keys and provider credentials, in their namespace.
-
Network ingress: Configure
spec.network.policy.ingressto allow access from specific users. -
Network egress: Configure
spec.network.policy.egressto restrict outbound traffic from server pods.
The following procedure displays the necessary CR configurations that the platform admin needs to enable for the tenant admin.
Prerequisites
- You have cluster administrator permissions.
-
You have installed the OpenShift CLI (
oc) - You have installed the OGX Operator on your OpenShift AI cluster.
Procedure
Create a
ClusterRolecustom resource for configuring the platform admin permissions.Example platform admin
ClusterRoleCRapiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: ogx-platform-admin rules: - apiGroups: ["ogx.io"] resources: ["ogxservers"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: [""] resources: ["namespaces"] verbs: ["get", "list", "watch", "create"] - apiGroups: ["rbac.authorization.k8s.io"] resources: ["roles", "rolebindings"] verbs: ["get", "list", "watch", "create", "update", "patch"]Grant the tenant admin permissions to manage CR in their namespace with the following example
Roleconfigurations:apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: ogx-tenant-admin namespace: tenant-a rules: - apiGroups: ["ogx.io"] resources: ["ogxservers"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: [""] resources: ["secrets", "configmaps"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: ogx-tenant-admin namespace: tenant-a subjects: - kind: Group name: tenant-a-admins apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: ogx-tenant-admin apiGroup: rbac.authorization.k8s.ioEnable
ResourceQuotaCR using the following configurations as an example:apiVersion: v1 kind: ResourceQuota metadata: name: ogx-tenant-quota namespace: tenant-a spec: hard: requests.cpu: "8" requests.memory: 32Gi limits.cpu: "16" limits.memory: 64Gi pods: "10"Enable preferred
NetworkPolicyCR configurations:Allow specific namespace
spec: network: policy: ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: frontend-ns ports: - port: 8321 protocol: TCPAllow namespace by label
spec: network: policy: ingress: - from: - namespaceSelector: matchLabels: tenant: customer-a ports: - port: 8321 protocol: TCPAllow specific pods in a namespace
spec: network: policy: ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: frontend-ns podSelector: matchLabels: app: api-gateway ports: - port: 8321 protocol: TCPAllow external CIDR
spec: network: policy: ingress: - from: - ipBlock: cidr: 10.0.0.0/8 except: - 10.0.1.0/24 ports: - port: 8321 protocol: TCP
Verification
- Log in to the cluster as a tenant administrator and verify that you can manage resources in your designated namespace.
8.4. Using APIs as a tenant user Copy linkLink copied to clipboard!
You can access and use various APIs configured by a platform or tenant admin.
Procedure
Obtain your credentials from the tenant admin. Your tenant admin will provide you with:
An OGX endpoint URL, for example:
https://ogx.apps.cluster.example.com/v1An API Key or token. You can obtain the keys in various ways:
-
An environment variable: The
OGX_API_KEYis already set by the platform admin when you open the notebook. - Keycloak login: You login to keycloak and the OGX SDK is responsible for refreshing.
- API key: Tenant admin generates and provides you with the key that you use in your configuration.
-
An environment variable: The
You can now access the APIs in the namespaces
Example using the APIs
OGX_URL="https://ogx.apps.cluster.example.com" TOKEN="<your-api-key-from-admin>" # List models curl -s -H "Authorization: Bearer $TOKEN" "$OGX_URL/v1/models" | python3 -m json.tool # Create a response curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ "$OGX_URL/v1/responses" \ -d '{"model":"vllm-inference/llama-3-2-3b","input":"Summarize quantum computing","store":true}' \ | python3 -m json.tool # List your responses curl -s -H "Authorization: Bearer $TOKEN" "$OGX_URL/v1/responses" | python3 -m json.tool # Upload a file (owned by you) curl -s -H "Authorization: Bearer $TOKEN" \ -F "file=@dataset.jsonl" -F "purpose=assistants" \ "$OGX_URL/v1/files" | python3 -m json.tool # Create a vector store (owned by you) curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ "$OGX_URL/v1/vector_stores" \ -d '{"name":"my-knowledge-base"}' | python3 -m json.toolExample using the Python SDK
import os from openai import OpenAI # Token and URL are provided by your platform admin # (typically pre-set as environment variables in your notebook/workspace) client = OpenAI( base_url=os.environ.get("OGX_URL", "https://ogx.apps.cluster.example.com/v1"), api_key=os.environ["OGX_API_KEY"], ) # Create a stored response — automatically owned by your identity response = client.responses.create( model="vllm-inference/llama-3-2-3b", input="Explain transformers", store=True, ) # List responses — only returns yours, other teams' responses are invisible my_responses = client.responses.list()