HTTPS on xKS Gateway — Manual Workaround for RHOAI 3.4 TP (pre-PR #90)
The rhai-on-xks helm chart creates the inference gateway with HTTP only (port 80). These steps add HTTPS manually after the chart is installed.
Verified working on AKS (May 28–29, 2026).
| Case | Use case | Trusted by browsers? | curl -k needed? |
Extra setup? |
|---|---|---|---|---|
| Case 1 | Dev/test clusters, quick HTTPS enablement, internal APIs where clients can skip cert verification | No | Yes | No |
| Case 2 | Custom domain for internal teams, branded URLs, internal services OK with self-signed CA | No | Yes | DNS record |
| Case 3 | Enterprise / cloud production — existing corporate CA, cloud-managed certs (ACM, Google-managed SSL, Azure Key Vault), or wildcard certs | Yes | No | Existing cert files only |
| Case 4 | Public-facing demo/PoC endpoints, no existing cert infrastructure, need a free trusted cert quickly (Let's Encrypt) | Yes | No | DNS + cert-manager patching (see below) |
Most enterprise and cloud customers will use Case 3 — on AKS, EKS, GKE, or CoreWeave you typically bring your own cert from the cloud provider's certificate service or your corporate PKI. No cert-manager ACME flow needed, no extra flags, simplest path to production-trusted HTTPS.
Case 4 (Let's Encrypt / ACME) is for situations where you need a trusted cert but have no existing cert infrastructure. It requires additional cert-manager configuration that is not officially supported by the RHAI operator today. See the bold prerequisites in Case 4.
- rhai-on-xks helm chart deployed
- cert-manager running with
rhai-ca-issuerClusterIssuer available kubectlconfigured for the target cluster
Verify:
kubectl get clusterissuer rhai-ca-issuer
kubectl get gateway inference-gateway -n redhat-ods-applicationsThe rhai-on-xks helm chart (via the cloud manager CR) auto-provisions an internal PKI chain:
rhai-ca(Certificate,cert-managernamespace) — self-signed root CA (isCA: true), created by theopendatahub-selfsigned-issuerClusterIssuer. Owned by theAzureKubernetesEngineCR.rhai-ca-issuer(ClusterIssuer) — uses therhai-casecret as its CA signing key.- The issuer automatically signs these downstream certificates:
llmisvc-webhook-server(redhat-ods-applications) — TLS for the LLMInferenceService admission webhookrhai-operator-webhook-cert(redhat-ods-operator) — TLS for the RHAI operator webhook
The gateway TLS certificate (inference-gateway-cert) is not auto-provisioned — it must be created manually (Cases 1, 2, 4) or supplied as an existing secret (Case 3). All cases that use rhai-ca-issuer produce self-signed certificates, so external clients must skip verification (curl -k) or trust the CA.
Use case: Dev/test environments, internal cluster access, quick HTTPS enablement. Clients access the gateway by IP address and accept self-signed certificates (curl -k or verify=False in code).
HTTPS with internal CA. External clients must use curl -k (skip verification).
kubectl apply -f - <<'EOF'
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: inference-gateway-cert
namespace: redhat-ods-applications
spec:
secretName: inference-gateway-cert-secret
issuerRef:
name: rhai-ca-issuer
kind: ClusterIssuer
group: cert-manager.io
dnsNames:
- "*.redhat-ods-applications.svc.cluster.local"
- "*.redhat-ods-applications.svc"
EOFkubectl get certificate inference-gateway-cert -n redhat-ods-applications
# Wait for READY = Truekubectl patch gateway inference-gateway -n redhat-ods-applications --type=json -p='[
{"op": "add", "path": "/spec/listeners/-", "value": {
"name": "https",
"port": 443,
"protocol": "HTTPS",
"allowedRoutes": {"namespaces": {"from": "All"}},
"tls": {
"mode": "Terminate",
"certificateRefs": [{"group": "", "kind": "Secret", "name": "inference-gateway-cert-secret"}]
}
}}
]'On AKS, the Azure Load Balancer needs a health probe annotation for port 443, otherwise external traffic on port 443 is silently dropped:
kubectl annotate svc inference-gateway-istio -n redhat-ods-applications \
"service.beta.kubernetes.io/port_443_health-probe_protocol=tcp"Without this annotation, HTTPS works from inside the cluster but times out externally.
GATEWAY_IP=$(kubectl get gateway inference-gateway -n redhat-ods-applications -o jsonpath='{.status.addresses[0].value}')
# HTTPS (skip verification — self-signed)
curl -k https://${GATEWAY_IP}/redhat-ods-applications/<model-name>/v1/models
# Chat completion over HTTPS
curl -k https://${GATEWAY_IP}/redhat-ods-applications/<model-name>/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "<model-name>", "messages": [{"role": "user", "content": "What is Kubernetes?"}], "max_tokens": 50}'
# HTTP still works
curl http://${GATEWAY_IP}/redhat-ods-applications/<model-name>/v1/modelsUse case: Internal teams that want a branded URL (e.g., inference.mycompany.com) instead of a raw IP address. Still uses the internal rhai-ca-issuer, so the certificate is self-signed. Useful for internal DNS, nip.io testing, or environments where clients can trust the internal CA or accept -k.
HTTPS with a custom DNS name, still using the internal rhai-ca-issuer.
External clients must trust the internal CA or use curl -k.
Point your domain to the gateway LoadBalancer IP:
GATEWAY_IP=$(kubectl get gateway inference-gateway -n redhat-ods-applications -o jsonpath='{.status.addresses[0].value}')
echo "Create DNS A record: inference.mycompany.com -> ${GATEWAY_IP}"For quick testing without DNS setup, use nip.io:
# Resolves automatically to your gateway IP — no DNS record needed
curl -k https://inference.${GATEWAY_IP}.nip.io/redhat-ods-applications/<model-name>/v1/modelskubectl apply -f - <<'EOF'
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: inference-gateway-cert
namespace: redhat-ods-applications
spec:
secretName: inference-gateway-cert-secret
issuerRef:
name: rhai-ca-issuer
kind: ClusterIssuer
group: cert-manager.io
dnsNames:
- "inference.mycompany.com"
- "*.redhat-ods-applications.svc.cluster.local"
- "*.redhat-ods-applications.svc"
EOFSame as Case 1, Step 3.
Same as Case 1, Step 4.
curl -k https://inference.mycompany.com/redhat-ods-applications/<model-name>/v1/modelsUse case: Enterprise environments with an existing corporate CA, wildcard certificates, cloud-managed certs (AWS ACM, Google-managed SSL, Azure Key Vault), or certs purchased from a commercial CA (DigiCert, Sectigo, etc.). This is the most common case for production enterprise deployments. No cert-manager Certificate resource is needed — you supply the TLS secret directly.
Verified on AKS (May 29, 2026).
Choose Option A if you already have cert files, or Option B to generate a test cert.
Option A: You have a certificate from your corporate CA or cloud provider
You should have two files:
tls.crt— the certificate (PEM format), must cover the hostname clients will usetls.key— the private key (PEM format)
Create the TLS secret:
kubectl create secret tls inference-gateway-cert-secret \
--cert=tls.crt \
--key=tls.key \
-n redhat-ods-applicationsOption B: Generate a test certificate using the cluster's own CA
If you don't have a corporate cert yet, generate one signed by the cluster's rhai-ca
for testing. Clients will need --cacert or -k since rhai-ca is self-signed.
# 1. Export the cluster CA cert and key
kubectl get secret rhai-ca -n cert-manager -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/rhai-ca.crt
kubectl get secret rhai-ca -n cert-manager -o jsonpath='{.data.tls\.key}' | base64 -d > /tmp/rhai-ca.key
# 2. Get the gateway IP for the certificate hostname
GATEWAY_IP=$(kubectl get gateway inference-gateway -n redhat-ods-applications -o jsonpath='{.status.addresses[0].value}')
# 3. Generate a private key and certificate signing request (CSR)
openssl req -new -newkey rsa:2048 -nodes \
-keyout /tmp/byo-gateway.key \
-subj "/CN=inference.${GATEWAY_IP}.nip.io" \
-out /tmp/byo-gateway.csr
# 4. Sign the certificate with the cluster CA (valid for 365 days)
openssl x509 -req -in /tmp/byo-gateway.csr \
-CA /tmp/rhai-ca.crt -CAkey /tmp/rhai-ca.key -CAcreateserial \
-out /tmp/byo-gateway.crt -days 365 \
-extfile <(echo "subjectAltName=DNS:inference.${GATEWAY_IP}.nip.io,DNS:*.redhat-ods-applications.svc.cluster.local")
# 5. Create the TLS secret
kubectl create secret tls inference-gateway-cert-secret \
--cert=/tmp/byo-gateway.crt \
--key=/tmp/byo-gateway.key \
-n redhat-ods-applicationsSame as Case 1, Step 3.
Same as Case 1, Step 4.
# Option A: corporate/public CA cert — no -k needed
curl https://inference.mycompany.com/redhat-ods-applications/<model-name>/v1/models
# Option B: self-signed test cert — use --cacert to trust the cluster CA, or -k to skip
GATEWAY_IP=$(kubectl get gateway inference-gateway -n redhat-ods-applications -o jsonpath='{.status.addresses[0].value}')
curl --cacert /tmp/rhai-ca.crt https://inference.${GATEWAY_IP}.nip.io/redhat-ods-applications/<model-name>/v1/modelsUse case: Public-facing demo/PoC endpoints, startups, or situations where you need a browser-trusted certificate but have no existing cert infrastructure or corporate PKI. Uses Let's Encrypt (or another ACME CA) to issue a free, publicly trusted certificate. No curl -k needed.
⚠️ This case requires additional cert-manager configuration that is not officially supported by the RHAI operator today. The--enable-gateway-apiflag needed for the ACME HTTP-01 solver is not exposed via the odh-gitops Helm chart or the CertManager CR's supportedoverrideArgs. The workaround below usesunsupportedConfigOverrides, which Red Hat marks as unsupported. Most enterprise customers should use Case 3 (bring your own cert) instead.
Requires a real domain you own, pointing to the gateway LoadBalancer IP. The ACME HTTP-01 challenge must be reachable on port 80 from the public internet.
Verified with Let's Encrypt staging on AKS (May 29, 2026).
The xKS stack uses Gateway API (not legacy Ingress), so the ACME HTTP-01 solver must create a temporary HTTPRoute to serve the challenge. This requires two extra setup steps that are not needed for Cases 1, 2, or 3.
1. Enable Gateway API in cert-manager (unsupported workaround):
The RHAI cert-manager operator does not enable Gateway API by default. The
--enable-gateway-api flag is not listed
among the officially supported overrideArgs for the OpenShift cert-manager operator.
Direct deployment patches are reverted by the operator, so use unsupportedConfigOverrides
on the CertManager CR:
kubectl patch certmanager cluster --type=merge -p='{
"spec": {
"unsupportedConfigOverrides": {
"controller": {
"args": ["--enable-gateway-api"]
}
}
}
}'Verify the flag was added (wait ~30s for pod restart):
kubectl get deployment cert-manager -n cert-manager \
-o jsonpath='{.spec.template.spec.containers[0].args}' | grep enable-gateway-apiWhy is this needed? Without --enable-gateway-api, cert-manager cannot create
HTTPRoute resources for the ACME challenge. The xKS stack has no IngressClass —
it is Gateway API only — so the legacy ingress.class solver does not work.
See cert-manager Gateway API discussion #7360.
2. Add pull secret to default ServiceAccount:
The ACME solver pod runs in redhat-ods-applications using the default SA but
needs to pull from registry.redhat.io:
kubectl patch sa default -n redhat-ods-applications \
-p '{"imagePullSecrets":[{"name":"rhai-pull-secret"}]}'Same as Case 2, Step 1.
The solver uses gatewayHTTPRoute (not legacy ingress) because the xKS stack uses
Gateway API with Istio. cert-manager creates a temporary HTTPRoute on the
inference-gateway to serve the ACME challenge token on port 80, then cleans it up.
kubectl apply -f - <<'EOF'
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@mycompany.com
privateKeySecretRef:
name: letsencrypt-account-key
solvers:
- http01:
gatewayHTTPRoute:
parentRefs:
- name: inference-gateway
namespace: redhat-ods-applications
EOFkubectl apply -f - <<'EOF'
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: inference-gateway-cert
namespace: redhat-ods-applications
spec:
secretName: inference-gateway-cert-secret
issuerRef:
name: letsencrypt
kind: ClusterIssuer
group: cert-manager.io
dnsNames:
- "inference.mycompany.com"
EOFLet's Encrypt validation takes 1-2 minutes:
kubectl get certificate inference-gateway-cert -n redhat-ods-applications -w
# Wait for READY = True
# If stuck, check the challenge and order status:
kubectl get challenges -n redhat-ods-applications
kubectl get orders -n redhat-ods-applicationsACME flow: Certificate → Order → Challenge → solver pod + temporary HTTPRoute → Let's Encrypt validates via HTTP on port 80 → Challenge valid → Certificate issued → cleanup.
Same as Case 1, Step 3.
Same as Case 1, Step 4.
# Fully trusted HTTPS — no -k needed
curl https://inference.mycompany.com/redhat-ods-applications/<model-name>/v1/modelsThe RHOAI 3.4 storage-initializer image hardcodes HF_XET_HIGH_PERFORMANCE=1 which enables
the HuggingFace xet transfer protocol. This protocol has known bugs causing downloads to hang
at 99% (file fully downloaded but stuck as .incomplete).
Workaround — patch the deployment to disable xet after the LLMInferenceService is created:
kubectl patch deployment <name>-kserve -n <namespace> --type=json -p='[
{"op": "add", "path": "/spec/template/spec/initContainers/0/env/-", "value": {"name": "HF_HUB_DISABLE_XET", "value": "1"}}
]'This forces the standard HTTPS download path. Model downloads complete in seconds instead of hanging.
To remove HTTPS and revert to HTTP only:
# Remove HTTPS listener
kubectl patch gateway inference-gateway -n redhat-ods-applications --type=json -p='[
{"op": "remove", "path": "/spec/listeners/1"}
]'
# Remove Azure LB annotation
kubectl annotate svc inference-gateway-istio -n redhat-ods-applications \
"service.beta.kubernetes.io/port_443_health-probe_protocol-"
# Delete certificate and secret
kubectl delete certificate inference-gateway-cert -n redhat-ods-applications
kubectl delete secret inference-gateway-cert-secret -n redhat-ods-applications- The gateway keeps both HTTP (80) and HTTPS (443) listeners. To disable HTTP, remove the first listener.
- If the RHAI operator reconciles the gateway, manual changes may be overwritten. Monitor after operator upgrades.
- For allowedRoutes security, see INFERENG-7126 and PR #108.
- On AKS, the Azure LB health probe annotation is critical — without it port 443 works internally but not externally.