How to verify and secure configuration of OpenShift AI Gateway for Model Serving
Environment
Red Hat OpenShift AI Self-Managed 3.x (3.3, 3.4)
Issue
Gateway API listener configurations using allowedRoutes.namespaces.from: All allow any namespace on the cluster to attach HTTPRoutes to the corresponding hostname managed by the Gateway.
This enables route hijack attacks: a user with standard namespace-level permissions can create an HTTPRoute that intercepts traffic intended for a legitimate model endpoint. Intercepted traffic includes API keys, user prompts, and model responses. The attack requires no elevated privileges and produces no visible errors for legitimate users.
Previous versions of Red Hat OpenShift AI documentation and upstream examples used this insecure default.
Resolution
Step 1: Check the current Gateway configuration
Find all gateway instances with the insecure selector value "All":
oc get gateway -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.listeners[*].allowedRoutes.namespaces.from}{"\n"}{end}' | grep -i "all"
An insecure allowedRoutes configuration looks like this:
allowedRoutes:
namespaces:
from: All
If this command returns results, proceed to Step 2.
If it returns no results, your Gateways are not using the insecure default. You may still want to review Step 4 for additional hardening.
Step 2: Check for signs of exploitation
Before applying the fix, check whether unauthorized routes are currently attached to your Gateway.
2.1 List all HTTPRoutes attached to the Gateway
oc get httproutes -A -o json | jq -r '
.items[] |
.metadata.namespace as $ns |
.metadata.name as $name |
.metadata.creationTimestamp as $created |
select(.spec.parentRefs[]? | .name == "<GATEWAY_NAME>") |
"\($ns)\t\($name)\t\($created)"
'
Replace <GATEWAY_NAME> with your Gateway name (e.g., maas-default-gateway).
2.2 Identify unexpected routes
Review the output. Every namespace and route name should correspond to a legitimate model deployment. Investigate any route that:
- Originates from a namespace you do not recognize or that should not be serving models
- Was created recently or outside of normal deployment workflows
- Uses
Exactpath matching (which takes priority overPathPrefixmatching used by MaaS/llm-d)
To check the path matching type on a specific route:
oc get httproute <ROUTE_NAME> -n <NAMESPACE> -o jsonpath='{range .spec.rules[*].matches[*]}{.path.type}{"\t"}{.path.value}{"\n"}{end}'
Routes using Exact matching on paths that correspond to legitimate model endpoints are a strong indicator of route hijacking.
2.3 If you find a suspicious route
- Capture the route definition for investigation:
oc get httproute <SUSPICIOUS_ROUTE> -n <NAMESPACE> -o yaml > suspicious-route-$(date +%s).yaml
- Delete the route immediately:
oc delete httproute <SUSPICIOUS_ROUTE> -n <NAMESPACE>
- Review audit logs for the namespace to determine when the route was created and by whom:
oc adm node-logs --role=master --path=kube-apiserver | grep httproutes | grep <NAMESPACE>
- Rotate any API keys or tokens that were configured for the affected model endpoints.
- Contact Red Hat Support for further investigation if needed.
Step 3: Implement secure gateway configuration
WARNING: Follow these steps in order. If you patch the Gateway (Step 3.3) before labeling all authorized namespaces (Step 3.2), existing model endpoints will become unreachable until the namespaces are labeled.
3.1 Choose a label for your Gateway
Choose a label key that identifies namespaces authorized to attach routes to this Gateway. For example:
maas-gateway-access: "true"for a MaaS-specific Gatewaygateway-<GATEWAY_NAME>: "true"if you have multiple Gateways
Use the same label consistently for all namespaces and the Gateway selector.
3.2 Label ALL authorized namespaces FIRST
Identify all namespaces that currently have routes attached to the Gateway:
oc get httproutes -A -o json | jq -r '
.items[] |
select(.spec.parentRefs[]? |
.name == "<GATEWAY_NAME>" and
(.namespace // "<GATEWAY_NAMESPACE>" == "<GATEWAY_NAMESPACE>")
) | .metadata.namespace' | sort -u
Label every namespace in the output:
oc label namespace <NAMESPACE> maas-gateway-access="true"
Repeat for each namespace. Do not proceed to Step 3.3 until all authorized namespaces are labeled.
Label the MaaS infrastructure namespace
The MaaS controller creates an internal HTTPRoute (maas-api-route) in an infrastructure namespace that may not appear in the HTTPRoute query above — the route may be created later by the operator. If this namespace is not labeled, the API Keys page in the dashboard will display "Error loading components."
Identify which infrastructure namespace MaaS uses for routes on your version:
# Shows the namespace where maas-api-route is (or will be) created
oc get httproute maas-api-route -A -o jsonpath='{.items[0].metadata.namespace}' 2>/dev/null || echo "Route not yet created — see version table below"
| Red Hat OpenShift AI version | MaaS infrastructure namespace |
|---|---|
| 3.4 | redhat-ods-applications |
| 3.5 and later | redhat-ai-gateway-infra |
Label the namespace that matches your version:
# 3.5 and later
oc label namespace redhat-ai-gateway-infra maas-gateway-access="true"
# 3.4
oc label namespace redhat-ods-applications maas-gateway-access="true"
If you are upgrading from 3.4 to 3.5, label redhat-ai-gateway-infra in addition to any previously labeled namespaces.
3.3 Patch the Gateway
Update the Gateway to use label-based namespace selection:
oc patch gateway <GATEWAY_NAME> -n <GATEWAY_NAMESPACE> --type='merge' -p '{
"spec": {
"listeners": [{
"name": "https",
"allowedRoutes": {
"namespaces": {
"from": "Selector",
"selector": {
"matchLabels": {
"maas-gateway-access": "true"
}
}
}
}
}]
}
}'
Note: If your Gateway has multiple listeners, you must include all listeners in the patch command with the updated allowedRoutes configuration. Omitting a listener from the patch will remove it. Check your current listeners first:
oc get gateway <GATEWAY_NAME> -n <GATEWAY_NAMESPACE> -o jsonpath='{range .spec.listeners[*]}{.name}{"\n"}{end}'
3.4 Verify the configuration
Confirm the Gateway now uses namespace selectors:
oc get gateway <GATEWAY_NAME> -n <GATEWAY_NAMESPACE> -o yaml
The configuration should show:
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
maas-gateway-access: "true"
3.5 Verify model endpoints still work
Test that existing model endpoints are responding:
curl -s -o /dev/null -w "%{http_code}" https://<GATEWAY_HOST>/<MODEL_PATH>/v1/models
A 200 response confirms the endpoint is reachable. If you get 404 or no response, check that the model's namespace is labeled (Step 3.2).
An alternative allowedRoutes configuration using explicit namespace names instead of labels:
allowedRoutes:
namespaces:
from: Selector
selector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
- namespace-a
- namespace-b
Note: When using
matchExpressions, you must include the MaaS infrastructure namespace in the values list. Useredhat-ods-applicationsfor Red Hat OpenShift AI 3.4 orredhat-ai-gateway-infrafor 3.5 and later. Without it, the internalmaas-api-routecannot attach to the Gateway.
Step 4: Additional hardening (recommended)
The Gateway namespace selector is the primary fix. The following measures provide defense in depth.
4.1 Restrict HTTPRoute creation via RBAC
By default, users with edit or admin roles in a namespace can create HTTPRoutes. This is granted via RBAC aggregation in the cluster role system:openshift:gateway-api:aggregate-to-admin. To restrict HTTPRoute creation, remove aggregation from this role. Alternatively, delete the cluster role and consult your cluster RBAC policies to create a custom role that excludes httproutes from the edit ClusterRole if appropriate for your environment. This prevents unauthorized users from creating HTTPRoutes even in namespaces that are authorized for Gateway access.
4.2 Deploy a ValidatingAdmissionPolicy
A ValidatingAdmissionPolicy (VAP) can enforce that HTTPRoutes are only created by authorized controllers or in authorized namespaces, providing an additional layer of protection independent of the Gateway configuration.
Example VAP that restricts HTTPRoute creation to namespaces with the maas-gateway-access label:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: restrict-httproute-creation
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["gateway.networking.k8s.io"]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["httproutes"]
validations:
- expression: "object.metadata.namespace in namespaceObject.metadata.labels && namespaceObject.metadata.labels['maas-gateway-access'] == 'true'"
message: "HTTPRoutes can only be created in namespaces labeled with maas-gateway-access=true"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: restrict-httproute-creation-binding
spec:
policyName: restrict-httproute-creation
validationActions:
- Deny
matchResources:
namespaceSelector: {}
Note: Test this policy in Warn mode (validationActions: [Warn]) before switching to Deny to avoid disrupting legitimate deployments.
4.3 Audit labeled namespaces periodically
Schedule regular reviews of which namespaces have the gateway access label:
oc get namespaces -l maas-gateway-access=true -o custom-columns=NAME:.metadata.name,CREATED:.metadata.creationTimestamp
Remove the label from any namespace that no longer requires Gateway access:
oc label namespace <NAMESPACE> maas-gateway-access-
Ongoing operations
When creating a new namespace that needs to serve models via MaaS, label it before deploying the model:
oc label namespace <NEW_NAMESPACE> maas-gateway-access="true"
This step must be part of your namespace provisioning process. Without the label, HTTPRoutes in the new namespace will not attach to the Gateway.
Upgrading to 3.5: MaaS routes move from redhat-ods-applications to redhat-ai-gateway-infra. If your gateway uses label-based namespace selection (matchLabels), label the new namespace before or immediately after upgrading. If your gateway uses matchExpressions with explicit namespace names, add redhat-ai-gateway-infra to the values list.
Root Cause
The default Gateway configuration documented for RHOAI 3.3 and 3.4 used allowedRoutes.namespaces.from: All, which permits any namespace on the cluster to create HTTPRoutes that bind to hostnames managed by the Gateway.
Kubernetes Gateway API resolves route conflicts using specificity rules: an Exact path match takes priority over a PathPrefix match. MaaS and llm-d create HTTPRoutes with PathPrefix matching. An attacker can create a route with Exact matching on the same path, causing the Gateway to route traffic to the attacker's service instead of the legitimate model endpoint.
The intercepted traffic includes:
- API keys and Bearer tokens in request headers
- User prompts in request bodies
- Model responses
- Any metadata in request headers
Using namespace selectors with labeled trusted namespaces implements the principle of least privilege, ensuring only authorized namespaces can attach routes to the Gateway.
This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.