OpenShift Networking: From Ingress to Route
Author: Fabrice JAMMES (LinkedIn). Duration: 20-30 minutes
Objective
OpenShift predates the Kubernetes Ingress API by several years. Its native object for exposing HTTP(S) services is the Route (route.openshift.io/v1), handled by the HAProxy-based router. To stay compatible with portable Kubernetes manifests, OpenShift ships a controller — part of openshift-controller-manager / route-controller-manager — that watches every Ingress object cluster-wide and automatically creates a matching Route for it.
In this lab you’ll deploy the same application behind progressively richer Ingress manifests and inspect the Route that OpenShift generates for each one. You’ll learn:
- Which
Ingressfields map to whichRoutefields - How the
route.openshift.io/terminationannotation controls TLS termination - Why
spec.tls.insecureEdgeTerminationPolicy: Redirectappears even though you never asked for it
Prerequisites
- An OpenShift cluster, logged in with
oc/kubectl, with rights to create a project - The
openshift-defaultIngressClass(created by the cluster-ingress-operator on every OpenShift 4 cluster)
Setup
List the available IngressClasses:
openshift-default is the one handled by the ingress-to-route controller (openshift.io/ingress-to-route). Any Ingress whose ingressClassName points at a different controller — or at an IngressClass that doesn’t exist — is silently ignored: no Route is created for it at all.
Compute the cluster’s wildcard apps domain — you’ll need it for the Ingress host:
Exercise 1 — A plain, portable Ingress (no OpenShift annotations)
Create an Ingress with no annotations at all — the kind of manifest you’d write for any Kubernetes cluster:
Look at what got created:
The controller generated a Route named example-ingress-xxxxx (your Ingress name plus a random 5-character suffix), owned by your Ingress (ownerReferences). Fetch its full spec:
Question: Match each field of the Route’s spec to the Ingress field it came from. Then try both:
What’s the result of each command, and why?
Exercise 2 — Opting into TLS with route.openshift.io/termination
Add the OpenShift-specific annotation that tells the controller “terminate TLS at the router, edge-style”:
Re-fetch the Route:
Try both curls again:
Question: spec.tls.termination: edge is no surprise — you asked for it. But where does insecureEdgeTerminationPolicy: Redirect come from? You never set anything like that on the Ingress.
Going further — passthrough and reencrypt
route.openshift.io/termination accepts three values, mapping to the three Route TLS termination types:
| Annotation value | spec.tls.termination |
Router behaviour |
|---|---|---|
edge (or any unrecognized value, once TLS is enabled) |
edge |
Router decrypts TLS using its own (or the Ingress’s referenced) certificate; traffic to the Pod is plain HTTP |
passthrough |
passthrough |
Router forwards the encrypted TCP stream untouched — your Pod must terminate TLS itself. No certificate is stored on the Route |
reencrypt |
reencrypt |
Router decrypts, then re-encrypts towards the Pod using a CA certificate read from the secret named in route.openshift.io/destination-ca-certificate-secret |
For edge/reencrypt, an Ingress.spec.tls entry whose hosts matches the rule’s host and whose secretName points at a valid kubernetes.io/tls secret is used to populate spec.tls.certificate/key on the Route — that’s the portable way to ship a real certificate through an Ingress (instead of relying on the router’s default wildcard certificate, as in Exercise 2).
Cleanup
Key Takeaways
- OpenShift runs an
Ingress→Routecontroller (openshift.io/ingress-to-route, the controller behind theopenshift-defaultIngressClass): everyIngressrule/path becomes oneRoute, named<ingress-name>-xxxxxand owned by theIngress. Write portablenetworking.k8s.io/v1manifests; OpenShift runsRoutes underneath. - TLS is opt-in. A
Routeonly gets aspec.tlsblock if theIngresssetsroute.openshift.io/termination: edge|reencrypt|passthrough, or references a valid TLS secret viaspec.tls[].secretNamefor that host. Otherwisespec.tlsisnulland theRouteis HTTP-only — invisible to HTTPS clients, not just “insecure”. insecureEdgeTerminationPolicy: Redirectis hardcoded whenever TLS gets enabled on the generatedRoute— there’s noIngressannotation to requestAllow/None; edit theRoutedirectly if you need that.kubectl get route <name> -o yaml(oroc get route) is your primary debugging tool when anIngress“does nothing” on OpenShift — check whether aRoutewas created at all (IngressClasscontroller mismatch ⇒ none), and compare itsspec.tlsagainst what you expected.
Reference / full solution
- Demo repository: demo-nginx-controller
- Controller source: route-controller-manager —
pkg/route/ingress/ingress.go(conversion logic),pkg/routecontroller/wellknown.go(recognized annotations)