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

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

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 OGXServer CR and the operator reconciles into a dedicated deployment, service, and storage.

8.1.2. Roles for multi-tenancy environments

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), ResourceQuotas CRs, and manages CRDs.
  • Tenant Admin: Operates at the namespace or single-server level, the tenant admin creates and manages OGXServer CRs, 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

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, or PersistentVolumeClaim, are created in the CR namespace.
  • ConfigMap/ Secret references: All ConfigMap and secret references in the custom resource specifications are restricted to the CR’s namespace.
  • Network Isolation: The NetworkPolicy specification is created for each distribution with the following defaults:

    Expand
    Table 8.1. Network Isolation Rules
    DirectionDefault RuleConfigurable

    Ingress

    Allow from same namespace

    Yes, with the allowedFrom parameter

    Ingress

    Allow from operator namespace

    No, required for operator health checks

    Ingress

    Deny all other

    Yes, with the allowedFrom parameter

    Egress

    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 ServiceAccount has no Kubernetes API permissions. Secrets are injected as environment variables via secretKeyRef, not read at runtime by the server.

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 SelfSubjectReview API, 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

Important

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.

Expand
Table 8.2. Resource Isolation Matrix
Resource NameIsolation LevelSharing / 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

  1. Deploy an OGXServer CR with your custom configuration. For more information, see "Deploying an OGX Server".
  2. Configure claims mapping in the config.yaml file

    server:
      auth:
        provider_config:
          claims_mapping:
            realm_access.roles: roles
            groups: teams
            tenant_id: namespaces
  3. You 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

      1. 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)
      2. Set the following additional environment variables:

        AUTH_AUDIENCE=ogx
        AUTH_VERIFY_TLS=true
      3. The 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

      1. Set your Keycloak details in the OGXServer custom resource

        apiVersion: 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"
  4. 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"

    1. For team-based sharing where users can see the resources of users on the same team, use the following example access_policy config:

      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"
    2. For permissions where an admin can access everything, use the following example access_policy config:

      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
  5. Create the project namespaces, for example:

    $ oc new-project <project-a>
    $ oc new-project <project-b>
  6. 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>
  7. 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

      1. 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

      1. Configure your config.yaml file to trust KeyCloak

        auth.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/certs
      2. Access 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)
  8. Your tenant users can now access the resources on the namespace. For more information, see "Using APIs as a tenant user".

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 Roles and RoleBindings per 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 OGXServer CRs in their namespaces.
  • Resource limits: Configure resources.requests, resources.limits, and maxReplicas on CRs. The ResourceQuota CR 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.ingress to allow access from specific users.
  • Network egress: Configure spec.network.policy.egress to 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

  1. Create a ClusterRole custom resource for configuring the platform admin permissions.

    Example platform admin ClusterRole CR

    apiVersion: 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"]

  2. Grant the tenant admin permissions to manage CR in their namespace with the following example Role configurations:

    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.io
  3. Enable ResourceQuota CR 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"
  4. Enable preferred NetworkPolicy CR configurations:

    • Allow specific namespace

      spec:
        network:
          policy:
            ingress:
            - from:
              - namespaceSelector:
                  matchLabels:
                    kubernetes.io/metadata.name: frontend-ns
              ports:
              - port: 8321
                protocol: TCP
    • Allow namespace by label

      spec:
        network:
          policy:
            ingress:
            - from:
              - namespaceSelector:
                  matchLabels:
                    tenant: customer-a
              ports:
              - port: 8321
                protocol: TCP
    • Allow 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: TCP
    • Allow 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

You can access and use various APIs configured by a platform or tenant admin.

Procedure

  1. 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/v1
    • An API Key or token. You can obtain the keys in various ways:

      1. An environment variable: The OGX_API_KEY is already set by the platform admin when you open the notebook.
      2. Keycloak login: You login to keycloak and the OGX SDK is responsible for refreshing.
      3. API key: Tenant admin generates and provides you with the key that you use in your configuration.
  2. 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.tool

    Example 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()

Red Hat logoGithubredditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat Documentation

Legal Notice

Theme

© 2026 Red Hat
Back to top