Gateway Patterns
This page provides curated Gateway API deployment patterns for MaaS on OpenShift. Each pattern includes copy-pasteable YAML manifests, prerequisites, verification steps, and troubleshooting guidance for common failure modes.
Related topics:
- Default MaaS Gateway setup: Install MaaS Components — Create Gateway
- Attaching models to the Gateway: Model Setup Guide
- End-to-end TLS: TLS Configuration
Pattern index
| Pattern | Environment | One-line purpose |
|---|---|---|
| ClusterIP + Route re-encrypt | Dev / Lab / Production | ClusterIP Gateway Service fronted by an OpenShift Route with re-encrypt TLS; no external LoadBalancer required |
Environment matrix
Use this table to decide which pattern fits your deployment:
| Environment | Recommended pattern | Who terminates client TLS? | Internal TLS | Namespace expectations |
|---|---|---|---|---|
| Development / Lab | ClusterIP + Route re-encrypt | OpenShift Router (wildcard or self-signed cert) | service-ca auto-provisioned cert (re-encrypt to Gateway) | openshift-ingress for Gateway and Route; application namespace for HTTPRoute |
| Production | ClusterIP + Route re-encrypt | OpenShift Router (CA-signed certificate) | service-ca auto-provisioned cert (re-encrypt to Gateway) | openshift-ingress for Gateway and Route; application namespace for HTTPRoute |
TLS responsibilities summary:
- Client-facing TLS: Managed by the OpenShift Router. In production, replace the default wildcard certificate with a CA-signed certificate on the IngressController.
- Gateway Service TLS: Auto-provisioned by the OpenShift
service-ca-operatorvia theservice.beta.openshift.io/serving-cert-secret-nameannotation. The Secret name in the ConfigMap annotation must match the Gateway listenercertificateRefs. - Authorino and maas-api TLS: Configured separately. See TLS Configuration.
ClusterIP Gateway with OpenShift Route (re-encrypt)
This pattern deploys a Gateway API Gateway as a ClusterIP service (no external LoadBalancer) and uses an OpenShift Route with re-encrypt TLS termination to expose the Gateway externally.
Traffic flow
graph LR
Client -->|HTTPS| Router["OpenShift Router"]
Router -->|re-encrypt TLS| GWSvc["Gateway Service<br/>ClusterIP:443"]
GWSvc --> Envoy["Istio / Envoy"]
Envoy -->|HTTPRoute| MaaSAPI["maas-api:8443"]
- The client connects over HTTPS to the OpenShift Router.
- The Router terminates the client TLS session and opens a new TLS connection (re-encrypt) to the Gateway Service using the service-ca certificate.
- The Gateway (Istio/Envoy) terminates that connection and routes traffic via
HTTPRoute to backend services such as
maas-api.
Why ClusterIP + Route?
- No LoadBalancer required. Works on bare-metal, restricted cloud accounts, and environments where LoadBalancer provisioning is slow or unavailable.
- OpenShift-native ingress. Leverages the platform Router for TLS termination, hostname routing, and integration with existing wildcard certificates.
- Re-encrypt for defense in depth. Traffic is encrypted between the Router and the Gateway Service, not just at the edge.
Prerequisites
- OpenShift cluster with Gateway API support (see Prerequisites)
- GatewayClass
openshift-defaultaccepted (see Install Gateway API Controller) - Kuadrant or RHCL operator installed (see Install Gateway API Controller)
- Access to the
openshift-ingressnamespace
Manifests
All manifests are in
docs/samples/gateway-patterns/clusterip-route-reencrypt/.
1. GatewayClass
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: openshift-default
spec:
controllerName: "openshift.io/gateway-controller/v1"
If openshift-default already exists on your cluster, skip this step.
2. Gateway options ConfigMap
The ConfigMap configures the Gateway Service as ClusterIP and requests a service-ca TLS certificate:
apiVersion: v1
kind: ConfigMap
metadata:
name: gw-options
namespace: openshift-ingress
data:
service: |
metadata:
annotations:
service.beta.openshift.io/serving-cert-secret-name: "maas-gw-service-tls"
spec:
type: ClusterIP
| Field | Purpose |
|---|---|
serving-cert-secret-name |
The service-ca-operator provisions a TLS certificate into this Secret. Must match the Gateway certificateRefs. |
type: ClusterIP |
No external LoadBalancer; the OpenShift Route handles external ingress. |
3. Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: maas-default-gateway
namespace: openshift-ingress
labels:
kuadrant.io/gateway: "true"
app.kubernetes.io/name: maas-gateway
annotations:
opendatahub.io/managed: "false"
security.opendatahub.io/authorino-tls-bootstrap: "true"
spec:
gatewayClassName: openshift-default
infrastructure:
parametersRef:
group: ""
kind: ConfigMap
name: gw-options
listeners:
- name: https
port: 443
protocol: HTTPS
allowedRoutes:
namespaces:
from: All
tls:
certificateRefs:
- group: ""
kind: Secret
name: maas-gw-service-tls
mode: Terminate
| Label / Annotation | Purpose |
|---|---|
kuadrant.io/gateway: "true" |
Enables Kuadrant/RHCL policy attachment (AuthPolicy, RateLimitPolicy) |
opendatahub.io/managed: "false" |
Lets maas-controller manage AuthPolicies; prevents ODH Model Controller from overwriting them |
security.opendatahub.io/authorino-tls-bootstrap: "true" |
Creates the EnvoyFilter for Gateway → Authorino TLS communication |
infrastructure.parametersRef |
References the gw-options ConfigMap for ClusterIP and service-ca configuration |
4. OpenShift Route
The Route exposes the Gateway Service externally with re-encrypt TLS:
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: maas-gateway-route
namespace: openshift-ingress
spec:
host: maas.<cluster-domain> # Replace with your cluster domain
to:
kind: Service
name: maas-default-gateway-openshift-default # Verify with kubectl get svc -n openshift-ingress
weight: 100
port:
targetPort: 443
tls:
termination: reencrypt
insecureEdgeTerminationPolicy: Redirect
# The router needs the service CA bundle to verify the backend certificate
# during reencrypt handshake. Fetch it with:
# kubectl get configmap signing-cabundle -n openshift-service-ca -o jsonpath='{.data.ca-bundle\.crt}'
destinationCACertificate: |
-----BEGIN CERTIFICATE-----
... (paste service CA bundle here) ...
-----END CERTIFICATE-----
Service name
The Gateway controller generates a Service name from the Gateway name and GatewayClass name. Verify the actual name:
Hostname placeholder
Replace maas.<cluster-domain> with your actual hostname. Retrieve the
cluster domain with:
5. HTTPRoute
Attach workloads to the Gateway. This example routes all traffic to maas-api:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: maas-api
namespace: <application-namespace>
spec:
parentRefs:
- name: maas-default-gateway
namespace: openshift-ingress
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: maas-api
port: 8443
Replace <application-namespace> with your ODH/RHOAI application namespace
(opendatahub or redhat-ods-applications).
Note
The HTTPRoute for maas-api is created automatically by maas-controller by default. You only need to create it manually if you are not using maas-controller.
Apply
# Apply GatewayClass, ConfigMap, and Gateway
kustomize build docs/samples/gateway-patterns/clusterip-route-reencrypt | kubectl apply -f -
# Wait for the Gateway to be programmed
kubectl wait --for=condition=Programmed gateway/maas-default-gateway \
-n openshift-ingress --timeout=60s
# Determine your cluster domain and the Gateway Service name
CLUSTER_DOMAIN=$(kubectl get ingresses.config.openshift.io cluster -o jsonpath='{.spec.domain}')
GW_SVC=$(kubectl get svc -n openshift-ingress \
-l gateway.networking.k8s.io/gateway-name=maas-default-gateway \
-o jsonpath='{.items[0].metadata.name}')
# Fetch the service CA bundle for reencrypt termination
SERVICE_CA=$(kubectl get configmap signing-cabundle -n openshift-service-ca -o jsonpath='{.data.ca-bundle\.crt}')
# Create the Route (update host and service name in openshift-route.yaml, or apply inline)
kubectl apply -f - <<EOF
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: maas-gateway-route
namespace: openshift-ingress
spec:
host: maas.${CLUSTER_DOMAIN}
to:
kind: Service
name: ${GW_SVC}
weight: 100
port:
targetPort: 443
tls:
termination: reencrypt
insecureEdgeTerminationPolicy: Redirect
destinationCACertificate: |
$(echo "$SERVICE_CA" | sed 's/^/ /')
EOF
Verify
# Gateway should show Programmed=True
kubectl get gateway maas-default-gateway -n openshift-ingress
# Service should be ClusterIP
kubectl get svc -n openshift-ingress \
-l gateway.networking.k8s.io/gateway-name=maas-default-gateway
# Route should be Admitted
kubectl get route maas-gateway-route -n openshift-ingress
# TLS certificate Secret should exist
kubectl get secret maas-gw-service-tls -n openshift-ingress
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Gateway stays NotProgrammed |
GatewayClass not accepted, or gw-options ConfigMap missing |
Check kubectl get gatewayclass; verify ConfigMap in openshift-ingress |
Route shows HostAlreadyClaimed |
Another Route uses the same hostname | Change spec.host to a unique FQDN |
503 from the Route |
Gateway Service not ready or certificate not yet provisioned | Wait for service-ca-operator to provision maas-gw-service-tls; check kubectl get secret -n openshift-ingress |
| TLS handshake failure (re-encrypt) | Service CA cert not trusted by the Router | Ensure tls.destinationCACertificate contains the service CA bundle from signing-cabundle ConfigMap in openshift-service-ca namespace |
certificateRefs name mismatch |
Secret name in Gateway does not match ConfigMap annotation | Verify both reference the same Secret name (maas-gw-service-tls) |
MaaS integration
After deploying a gateway pattern, complete the MaaS-specific steps:
-
Attach models to the Gateway. Set
spec.router.gateway.refson your LLMInferenceService to point at the deployed Gateway. See Model Setup Guide. -
Configure end-to-end TLS. Set up Authorino listener TLS and maas-api backend TLS. See TLS Configuration.
-
Complete MaaS installation. If you have not already, follow the full installation flow: database, DataScienceCluster, and validation. See Install MaaS Components.
-
Deploy sample models. Use the bundled model samples to verify the gateway is working end-to-end. See Model Setup.