<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Ingress on TechBlog about OpenShift/Ansible/Satellite and much more</title><link>https://blog.stderr.at/categories/ingress/</link><description>TechBlog about OpenShift/Ansible/Satellite and much more</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>Toni Schmidbauer &amp; Thomas Jungbauer</copyright><lastBuildDate>Sun, 31 Aug 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.stderr.at/categories/ingress/index.xml" rel="self" type="application/rss+xml"/><item><title>A second look into the Kubernetes Gateway API on OpenShift</title><link>https://blog.stderr.at/openshift-platform/networking/2025-08-31-openshift-gateway-api-ii/</link><pubDate>Sun, 31 Aug 2025 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/openshift-platform/networking/2025-08-31-openshift-gateway-api-ii/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;This is our second look into the Kubernetes Gateway API an it’s
integration into OpenShift. This post covers TLS configuration.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Kubernetes Gateway API is new implementation of the ingress, load
balancing and service mesh API’s. See
&lt;a href="https://gateway-api.sigs.k8s.io/" target="_blank" rel="noopener"&gt;upstream&lt;/a&gt; for more information.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Also the &lt;a href="https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/ingress_and_load_balancing/configuring-ingress-cluster-traffic#nw-ingress-gateway-api-overview_ingress-gateway-api" target="_blank" rel="noopener"&gt;OpenShift documentation&lt;/a&gt; provides an overview of the Gateway API and it’s integration.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We demonstrate how to add TLS to our Nginx deployment, how to
implement a shared Gateway and finally how to implement HTTP to HTTPS
redirection with the Gateway API. Furthermore we cover how &lt;em&gt;HTTPRoute&lt;/em&gt;
objects attach to Gateways and dive into ordering of &lt;em&gt;HTTPRoute&lt;/em&gt;
objects.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock warning"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-warning" title="Warning"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
b2cc commented that deploying the gateway in the
&lt;em&gt;openshift-ingress&lt;/em&gt; namespace, as we did below, is not support by
Red Hat. We are not aware this is documented somewhere (please let use
now if it is). Only after opening a support ticket he learned that
this not supported. We will keep the examples below as they are, so
be careful and this may eat your cat.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_references"&gt;References&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/2025/08/gateway-api/"&gt;A first look into the Kubernetes Gateway API on OpenShift&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_adding_tls_to_our_nginx_deployment"&gt;Adding TLS to our Nginx deployment&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In our fist post we simply exposed a Nginx web server via the
Gateway API. We only enabled HTTP, so let’s try to do the same with
HTTPS now.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Remember we use a DNS wildcard domain &lt;code&gt;*.gtw.ocp.lan.stderr.at&lt;/code&gt; which
points to our Gateway. The gateway is exposed via a &lt;em&gt;Service&lt;/em&gt; of type
&lt;em&gt;LoadBalancer&lt;/em&gt;. We use
&lt;a href="https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/networking_operators/metallb-operator"&gt;MetalLB&lt;/a&gt;
for this.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The first step is setting up a wildcard TLS certificate for our custom
domain &lt;em&gt;*.gtw.ocp.lan.stderr.at&lt;/em&gt;. We are using
&lt;a href="https://github.com/OpenVPN/easy-rsa"&gt;EasyRSA&lt;/a&gt; here, but use whatever tool you like.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Just for reference this is how we created a wildcard cert with EasyRSA:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ EASYRSA_CERT_EXPIRE=3650 EASYRSA_EXTRA_EXTS=&amp;#34;subjectAltName=DNS:*.gtw.ocp.lan.stderr.at&amp;#34; ./easyrsa gen-req gtw.ocp.lan.stderr.at
$ EASYRSA_CERT_EXPIRE=3650 ./easyrsa sign-req serverClient gtw.ocp.lan.stderr.at&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;EasyRSA stores the public key under &lt;em&gt;pki/issued&lt;/em&gt; and the private key
under &lt;em&gt;pki/private&lt;/em&gt;. We copied the certificate and the private key to
a temporary directory.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Next we need to remove the private key passphrase and create a
Kubernetes secret from the private and pubic key:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ openssl rsa -in gtw.ocp.lan.stderr.at.key -out gtw.ocp.lan.stderr.at-insecure.key
$ oc create secret -n openshift-ingress tls gateway-api --cert=gtw.ocp.lan.stderr.at.crt --key=gtw.ocp.lan.stderr.at-insecure.key&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now it’s time to add a TLS listener to our &lt;em&gt;Gateway&lt;/em&gt; resource in the
&lt;em&gt;openshift-ingress&lt;/em&gt; namespace. Remember for the OpenShift Gateway API implementation, &lt;em&gt;Gateways&lt;/em&gt; have
to be deployed in the &lt;em&gt;openshift-ingress&lt;/em&gt; namespace.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: http-gateway
namespace: openshift-ingress
spec:
gatewayClassName: openshift-default
listeners:
- name: http
protocol: HTTP
port: 80
hostname: &amp;#34;*.gtw.ocp.lan.stderr.at&amp;#34;
- name: https
protocol: HTTPS &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
port: 443 &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
hostname: &amp;#34;*.gtw.ocp.lan.stderr.at&amp;#34; &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
tls:
mode: Terminate &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
certificateRefs:
- name: gateway-api &lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;(5)&lt;/b&gt;
allowedRoutes: &lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;(6)&lt;/b&gt;
namespaces:
from: All&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We want to support HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We use the default HTTPS port 443&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The URLs we support with this listener are the same as for HTTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We use edge termination for now, this means HTTP traffic will only be encrypted up to the gateway. From the gateway to our pod we speak plain HTTP.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;5&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;This is the name of the TLS secret we created above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We accept routes from all namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Also remember from our first post that we created a
&lt;em&gt;ReferenceGrant&lt;/em&gt; in the namespace where Nginx is running. Otherwise
HTTP routes will not be accepted.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Finally lets try to access our Nginx pod via HTTPS:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ curl -v https://nginx.gtw.ocp.lan.stderr.at
* Host nginx.gtw.ocp.lan.stderr.at:443 was resolved.
* IPv6: (none)
* IPv4: 10.0.0.150
* Trying 10.0.0.150:443...
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
* CApath: none
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / RSASSA-PSS
* ALPN: server accepted h2
* Server certificate:
* subject: CN=gtw.ocp.lan.stderr.at
* start date: Aug 30 10:01:33 2025 GMT
* expire date: Aug 28 10:01:33 2035 GMT
* subjectAltName: host &amp;#34;nginx.gtw.ocp.lan.stderr.at&amp;#34; matched cert&amp;#39;s &amp;#34;*.gtw.ocp.lan.stderr.at&amp;#34;
* issuer: CN=tntinfra CA
* SSL certificate verify ok.
* Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
* Certificate level 1: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
* Connected to nginx.gtw.ocp.lan.stderr.at (10.0.0.150) port 443
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://nginx.gtw.ocp.lan.stderr.at/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: nginx.gtw.ocp.lan.stderr.at]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.11.1]
* [HTTP/2] [1] [accept: */*]
&amp;gt; GET / HTTP/2
&amp;gt; Host: nginx.gtw.ocp.lan.stderr.at
&amp;gt; User-Agent: curl/8.11.1
&amp;gt; Accept: */*
&amp;gt;
* Request completely sent off
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
&amp;lt; HTTP/2 200
&amp;lt; server: nginx/1.29.1
&amp;lt; date: Sat, 30 Aug 2025 14:30:20 GMT
&amp;lt; content-type: text/html
&amp;lt; content-length: 615
&amp;lt; last-modified: Wed, 13 Aug 2025 14:33:41 GMT
&amp;lt; etag: &amp;#34;689ca245-267&amp;#34;
&amp;lt; accept-ranges: bytes
(output omitted)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Yes, we can reach our Nginx via HTTPS, and the gateway presents the TLS certificate we created.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Be aware that we are still using the same &lt;em&gt;HTTPRoute&lt;/em&gt; for Nginx from our previous blog post.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Just for completeness here is the &lt;em&gt;HTTPRoute&lt;/em&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: nginx-route
spec:
parentRefs:
- name: http-gateway
namespace: openshift-ingress
hostnames: [&amp;#34;nginx.gtw.ocp.lan.stderr.at&amp;#34;]
rules:
- backendRefs:
- name: nginx
namespace: gateway-api-test
port: 8080&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Also Remember that we are using a dedicated &lt;em&gt;Gateway&lt;/em&gt; and all
&lt;em&gt;HTTPRoutes&lt;/em&gt; must be in the namespace &lt;em&gt;openshift-ingress&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_moving_to_a_shared_gateway"&gt;Moving to a shared gateway&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Up until now we had to create all &lt;em&gt;HTTPRoute&lt;/em&gt; objects in the
&lt;em&gt;openshift-ingress&lt;/em&gt; namespace. The Gateway API support two modes of
operations:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Dedicated gateway: all &lt;em&gt;HTTPRoute&lt;/em&gt; object need to be in the same namespace as the gateway&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Shared gateway: The gateway runs in the &lt;em&gt;openshift-ingress&lt;/em&gt;
namespace and we allow &lt;em&gt;HTTPRoute&lt;/em&gt; objects from all or specific namespaces.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The first step in creating a shared gateway is to modify the gateway resource:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: http-gateway
namespace: openshift-ingress
spec:
gatewayClassName: openshift-default
listeners:
- name: http
protocol: HTTP
port: 80
hostname: &amp;#34;*.gtw.ocp.lan.stderr.at&amp;#34;
allowedRoutes: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
namespaces:
from: All&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We now allow &lt;em&gt;HTTPRoute&lt;/em&gt; objects from all namespaces in the cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Next we delete the existing &lt;em&gt;HTTPRoute&lt;/em&gt; for Nginx in the
&lt;em&gt;openshift-ingress&lt;/em&gt; namespaces, and verify that we can’t reach Nginx:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc delete httproutes.gateway.networking.k8s.io -n openshift-ingress nginx-route
httproute.gateway.networking.k8s.io &amp;#34;nginx-route&amp;#34; deleted
$ curl -I http://nginx.gtw.ocp.lan.stderr.at
HTTP/1.1 404 Not Found &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
date: Sat, 30 Aug 2025 15:02:23 GMT
transfer-encoding: chunked&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Our Nginx route stopped working&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Next we apply our modified &lt;em&gt;Gateway&lt;/em&gt; resource in the
&lt;em&gt;openshift-ingress&lt;/em&gt; namespace and the &lt;em&gt;HTTPRoute&lt;/em&gt; object in the
&lt;em&gt;gateway-api-test&lt;/em&gt; namespace.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc apply -n openshift-ingress -f gateway--selector.yaml
gateway.gateway.networking.k8s.io/http-gateway configured
$ oc apply -n gateway-api-test -f httproute.yaml &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
httproute.gateway.networking.k8s.io/nginx-route created
$ curl -I http://nginx.gtw.ocp.lan.stderr.at
HTTP/1.1 200 OK &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
server: nginx/1.29.1
date: Sat, 30 Aug 2025 15:04:34 GMT
content-type: text/html
content-length: 615
last-modified: Wed, 13 Aug 2025 14:33:41 GMT
etag: &amp;#34;689ca245-267&amp;#34;
accept-ranges: bytes&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We create the &lt;em&gt;HTTPRoute&lt;/em&gt; in the gateway-api-test namespace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We can reach our Nginx pod again&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;So our shared gateway seems to be working. But what if we want to
restrict which namespaces are allowed to create route objects?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Gateway API allows the following settings under &lt;em&gt;spec.listeners[].allowedRoutes.namespaces.from&lt;/em&gt; field&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;All&lt;/strong&gt;: Allow from all namespaces&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Selector&lt;/strong&gt;: Specify a selector&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Same&lt;/strong&gt;: Only allow &lt;em&gt;HTTPRoutes&lt;/em&gt; in the same namespaces&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;None&lt;/strong&gt;: Do not allow any routes to attach&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;See the API specification &lt;a href="https://gateway-api.sigs.k8s.io/reference/spec/#fromnamespaces"&gt;FromNamespaces&lt;/a&gt; for details.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let’s try to use a more specific selector for our gateway:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: http-gateway
namespace: openshift-ingress
spec:
gatewayClassName: openshift-default
listeners:
- name: http
protocol: HTTP
port: 80
hostname: &amp;#34;*.gtw.ocp.lan.stderr.at&amp;#34;
allowedRoutes:
namespaces:
from: Selector &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
selector:
matchLabels:
kubernetes.io/metadata.name: gateway-api-test &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Now we are using the Selector option&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Because we do not have a specific label on the namespace we would like to use, let’s use the &lt;em&gt;metadata.name&lt;/em&gt; label Kubernetes created for us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We create a new yaml file &lt;em&gt;gateway-selector.yaml&lt;/em&gt; and appy the new configuration:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc apply -n openshift-ingress -f gateway-selector.yaml
gateway.gateway.networking.k8s.io/http-gateway configured
$ curl -I http://nginx.gtw.ocp.lan.stderr.at
HTTP/1.1 200 OK
server: nginx/1.29.1
date: Sat, 30 Aug 2025 15:17:17 GMT
content-type: text/html
content-length: 615
last-modified: Wed, 13 Aug 2025 14:33:41 GMT
etag: &amp;#34;689ca245-267&amp;#34;
accept-ranges: bytes&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;All good, still working.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Just for testing we modified the namespace name in the Gateway definition to &lt;strong&gt;NOT&lt;/strong&gt; match the namespace of our Nginx deployment and confirmed that we receive a &lt;em&gt;404&lt;/em&gt; not found response.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_implementing_http_to_https_redirect"&gt;Implementing HTTP to HTTPS redirect&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;As a last test for this post let’s try to implement HTTP to HTTPS redirects.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We deployed the following &lt;em&gt;Gateway&lt;/em&gt; configuration:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: http-gateway
namespace: openshift-ingress &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
spec:
gatewayClassName: openshift-default
listeners:
- name: http
protocol: HTTP
port: 80
hostname: &amp;#34;*.gtw.ocp.lan.stderr.at&amp;#34;
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: gateway-api-test2
- name: https &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
protocol: HTTPS
port: 443
hostname: &amp;#34;*.gtw.ocp.lan.stderr.at&amp;#34;
tls:
mode: Terminate
certificateRefs:
- name: gateway-api
allowedRoutes:
namespaces:
from: All&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Always deploy the gateway to the &lt;em&gt;openshift-ingress&lt;/em&gt; namespace for the OpenShift Gateway API implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We added the HTTPS configuration back&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The &lt;a href="https://gateway-api.sigs.k8s.io/guides/http-redirect-rewrite/"&gt;upstream&lt;/a&gt; documentation contains an example on how to implements HTTP to HTTPS redirects. We created the following additional &lt;em&gt;HTTPRoute&lt;/em&gt; object in the &lt;em&gt;gateway-api-test&lt;/em&gt; namespace:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-https-redirect
spec:
parentRefs:
- name: http-gateway &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
namespace: openshift-ingress
sectionName: http &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
hostnames:
- nginx.gtw.ocp.lan.stderr.at
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Match our &lt;em&gt;Gateway&lt;/em&gt; &lt;em&gt;http-gateway&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Match the &lt;em&gt;http&lt;/em&gt; section in our gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Just for reference this is the &lt;em&gt;HTTPRoute&lt;/em&gt; object to expose Nginx:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: nginx-route
spec:
parentRefs:
- name: http-gateway
namespace: openshift-ingress
hostnames: [&amp;#34;nginx.gtw.ocp.lan.stderr.at&amp;#34;]
rules:
- backendRefs:
- name: nginx
namespace: gateway-api-test
port: 8080&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;First we re-applied our &lt;em&gt;Gateway&lt;/em&gt; configuration&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc apply -f gateway-https-selector.yaml
gateway.gateway.networking.k8s.io/http-gateway configured&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let’s try and verify if our redirect is working, we need to apply both routes:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc apply -f httproute.yaml
httproute.gateway.networking.k8s.io/nginx-route created
$ oc apply -f http-https-redirect-route.yaml
httproute.gateway.networking.k8s.io/http-https-redirect created&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;And test with curl:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ curl -I http://nginx.gtw.ocp.lan.stderr.at
HTTP/1.1 200 OK &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
server: nginx/1.29.1
date: Sat, 30 Aug 2025 15:37:20 GMT
content-type: text/html
content-length: 615
last-modified: Wed, 13 Aug 2025 14:33:41 GMT
etag: &amp;#34;689ca245-267&amp;#34;
accept-ranges: bytes&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Hm, strange we still get 200 OK and &lt;strong&gt;NOT&lt;/strong&gt; a redirect to HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_understanding_httproute_ordering"&gt;Understanding HTTPRoute ordering&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;After a longer search through the documentation we found some hints on why this is happening.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let’s take a more detailed look at our http-to-https route again, as a
&lt;em&gt;HTTPRoute&lt;/em&gt; &lt;strong&gt;attaches&lt;/strong&gt; to a &lt;em&gt;Gateway&lt;/em&gt;, we focus on the &lt;em&gt;parentRefs&lt;/em&gt; in
the &lt;em&gt;HTTPRoute&lt;/em&gt; object. In our current understanding &lt;em&gt;parentRefs&lt;/em&gt; select a &lt;em&gt;Gateway&lt;/em&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-https-redirect
spec:
parentRefs: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- name: http-gateway &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
namespace: openshift-ingress &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
sectionName: http &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
hostnames:
- nginx.gtw.ocp.lan.stderr.at
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Ok, this is the &lt;em&gt;parentRefs&lt;/em&gt; section we are looking for&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;name&lt;/em&gt; selects the name of the &lt;em&gt;Gateway&lt;/em&gt; we want to attach to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;namespace&lt;/em&gt; specifies the namespace where we can find the &lt;em&gt;Gateway&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;sectionName&lt;/em&gt; selects the section in the &lt;em&gt;Gateway&lt;/em&gt; where we want to attach to.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;So this &lt;em&gt;HTTPRoute&lt;/em&gt; explicitly attaches to a &lt;em&gt;Gateway&lt;/em&gt; in a
&lt;em&gt;Namespace&lt;/em&gt; that has a &lt;em&gt;Section&lt;/em&gt; http defined.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;If you look at the Gateway configuration above you will see that we
have a section for HTTP traffic and one for HTTPS traffic.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let’s compare this with our Nginx &lt;em&gt;HTTPRoute&lt;/em&gt; definition:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: nginx-route
spec:
parentRefs: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- name: http-gateway &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
namespace: openshift-ingress &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
hostnames: [&amp;#34;nginx.gtw.ocp.lan.stderr.at&amp;#34;]
rules:
- backendRefs:
- name: nginx
namespace: gateway-api-test
port: 8080&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The &lt;em&gt;parentRefs&lt;/em&gt; section&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The &lt;em&gt;Gateway&lt;/em&gt; we would like to attach to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The &lt;em&gt;namespace&lt;/em&gt; where the &lt;em&gt;Gateway&lt;/em&gt; is deploy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Note that &lt;em&gt;Section&lt;/em&gt; is missing in this configuration.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;So this &lt;em&gt;HTTPRoute&lt;/em&gt; actually attaches to &lt;strong&gt;both&lt;/strong&gt; sections in our
&lt;em&gt;Gateway&lt;/em&gt; definition, HTTP and HTTPS. Which is not what we want.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;When a client hits the HTTP endpoint we want to redirect the traffic to HTTPS&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When a client hits the HTTPS endpoint we want the traffic to be forward to our Nginx deployment&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We found the following statement
&lt;a href="https://gateway-api.sigs.k8s.io/reference/spec/#httprouterule"&gt;statement&lt;/a&gt;
how ordering works in the Gateway API:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;If ties still exist across multiple Routes, matching precedence MUST be
determined in order of the following criteria, continuing on ties:
The oldest Route based on creation timestamp.&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;When we look at the timestamps of our &lt;em&gt;HTTPRoutes&lt;/em&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;oc get httproute -o jsonpath=&amp;#39;{range .items[*]}{.metadata.name}{&amp;#34;\t&amp;#34;}{.metadata.creationTimestamp}{&amp;#34;\n&amp;#34;}{end}&amp;#39;
http-https-redirect 2025-08-31T09:17:46Z &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
nginx-route 2025-08-31T09:17:40Z &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Creation timestamp of the redirect route&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Creation timestamp of the nginx route&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Nginx &lt;em&gt;HTTPRoute&lt;/em&gt; is &lt;strong&gt;older&lt;/strong&gt; than the HTTP-to-HTTP &lt;em&gt;HTTPRoute&lt;/em&gt;. So
this matches first and a 200 OK is returned.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;So let’s try to revers how we applied our &lt;em&gt;HTTPRoutes&lt;/em&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc delete httproutes.gateway.networking.k8s.io --all
httproute.gateway.networking.k8s.io &amp;#34;http-https-redirect&amp;#34; deleted
httproute.gateway.networking.k8s.io &amp;#34;nginx-route&amp;#34; deleted
$ oc apply -f http-to-https-httproute.yaml
httproute.gateway.networking.k8s.io/http-https-redirect created
$ oc apply -f nginx-httproute.yaml
httproute.gateway.networking.k8s.io/nginx-route created
$ oc get httproute -o jsonpath=&amp;#39;{range .items[*]}{.metadata.name}{&amp;#34;\t&amp;#34;}{.metadata.creationTimestamp}{&amp;#34;\n&amp;#34;}{end}&amp;#39;
http-https-redirect 2025-08-31T10:34:55Z &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
nginx-route 2025-08-31T10:35:11Z &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Creation timestamp of the HTTP-to-HTTPS route&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Creation timestamp of the nginx route&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now the HTTP-to-HTTPS route is the oldest route. Let’s try again calling Nginx with curl:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ curl -I http://nginx.gtw.ocp.lan.stderr.at
HTTP/1.1 301 Moved Permanently &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
location: https://nginx.gtw.ocp.lan.stderr.at/
date: Sun, 31 Aug 2025 10:37:13 GMT
transfer-encoding: chunked
$ curl -I https://nginx.gtw.ocp.lan.stderr.at
HTTP/2 200 &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
server: nginx/1.29.1
date: Sun, 31 Aug 2025 10:37:17 GMT
content-type: text/html
content-length: 615
last-modified: Wed, 13 Aug 2025 14:33:41 GMT
etag: &amp;#34;689ca245-267&amp;#34;
accept-ranges: bytes&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The HTTP endpoint returns a redirect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;the HTTPS endpoint returns 200 OK from Nginx&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;So now we have the expected behavior: HTTP is redirect to HTTPS!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;As depending on the time when an object is created is definitely &lt;strong&gt;NOT&lt;/strong&gt;
a good idea, let’s be more specific in our Nginx &lt;em&gt;HTTPRoute&lt;/em&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: nginx-route
spec:
parentRefs:
- name: http-gateway
namespace: openshift-ingress
sectionName: https &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
hostnames: [&amp;#34;nginx.gtw.ocp.lan.stderr.at&amp;#34;]
rules:
- backendRefs:
- name: nginx
namespace: gateway-api-test
port: 8080&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We explicitly select the &lt;strong&gt;HTTPS&lt;/strong&gt; section in our &lt;em&gt;Gateway&lt;/em&gt; configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Next we delete our &lt;em&gt;HTTPRoutes&lt;/em&gt; again, and re-apply them in the order that didn’t work the first time (Nginx is the oldest route):&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc delete httproutes.gateway.networking.k8s.io --all
httproute.gateway.networking.k8s.io &amp;#34;http-https-redirect&amp;#34; deleted
httproute.gateway.networking.k8s.io &amp;#34;nginx-route&amp;#34; deleted
$ oc apply -f http-to-https-httproute.yaml
httproute.gateway.networking.k8s.io/http-https-redirect created
$ oc get httproute -o jsonpath=&amp;#39;{range .items[*]}{.metadata.name}{&amp;#34;\t&amp;#34;}{.metadata.creationTimestamp}{&amp;#34;\n&amp;#34;}{end}&amp;#39;
http-https-redirect 2025-08-31T10:45:01Z
nginx-route 2025-08-31T10:44:57Z &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
$ curl -I http://nginx.gtw.ocp.lan.stderr.at
HTTP/1.1 301 Moved Permanently &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
location: https://nginx.gtw.ocp.lan.stderr.at/
date: Sun, 31 Aug 2025 10:46:22 GMT
transfer-encoding: chunked
$ curl -I https://nginx.gtw.ocp.lan.stderr.at
HTTP/2 200 &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
server: nginx/1.29.1
date: Sun, 31 Aug 2025 10:46:30 GMT
content-type: text/html
content-length: 615
last-modified: Wed, 13 Aug 2025 14:33:41 GMT
etag: &amp;#34;689ca245-267&amp;#34;
accept-ranges: bytes&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The Nginx route is the oldest route&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The HTTP endpoint returns a redirect to HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The response from our Nginx deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Finally everything works as expected!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
A &lt;em&gt;HTTPRoute&lt;/em&gt; attaches to a &lt;em&gt;Gateway&lt;/em&gt;. Always be as specific as
possible which &lt;em&gt;Gateway&lt;/em&gt; to match and which &lt;em&gt;section&lt;/em&gt; in the
&lt;em&gt;Gateway&lt;/em&gt;.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In this blog post we demonstrated to implement TLS with the
Gateway API. We also implemented a shared &lt;em&gt;Gateway&lt;/em&gt; with &lt;em&gt;HTTPRoute&lt;/em&gt;
objects in different namespaces.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Furthermore we configured HTTP to HTTPS redirects and dove into
&lt;em&gt;HTTPRoute&lt;/em&gt; ordering if a route matches multiple listeners in a
&lt;em&gt;Gateway&lt;/em&gt; definition.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>A first look into the Kubernetes Gateway API on OpenShift</title><link>https://blog.stderr.at/openshift-platform/networking/2025-08-29-openshift-gateway-api/</link><pubDate>Fri, 29 Aug 2025 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/openshift-platform/networking/2025-08-29-openshift-gateway-api/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;This blog post summarizes our first look into the Kubernetes Gateway
API and how it is integrated in OpenShift.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Kubernetes Gateway API is new implementation of the ingress, load
balancing and service mesh API’s. See
&lt;a href="https://gateway-api.sigs.k8s.io/" target="_blank" rel="noopener"&gt;upstream&lt;/a&gt; for more information.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Also the &lt;a href="https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/ingress_and_load_balancing/configuring-ingress-cluster-traffic#nw-ingress-gateway-api-overview_ingress-gateway-api" target="_blank" rel="noopener"&gt;OpenShift documentation&lt;/a&gt; provides an overview of the Gateway API and it’s integration.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_things_to_consider_when_using_gateway_api_with_openshift"&gt;Things to consider when using Gateway API with OpenShift&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Currently UDN (User Defined Networks) with Gateway API are not supported.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Only TLS termination on the edge is supported (no pass-through or re-encrypt), this needs to be confirmed. We can’t find the original source of this statement&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The standard OpenShift ingress controller manages Gateway API Resources&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Gateway API provides a standard on how to get client traffic into a
Kubernetes cluster. Vendors provide an implementation of the API. So
OpenShift provides &lt;strong&gt;ONE&lt;/strong&gt; possible implementation, but there could
be more than one in a cluster.&lt;/p&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We found the following sentence in the OpenShift documentation
interesting:&lt;/p&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;Because OpenShift Container Platform uses a specific
version of Gateway API CRDs, any use of third-party implementations
of Gateway API must conform to the OpenShift Container Platform
implementation to ensure that all fields work as expected&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_setting_up_gateway_api_on_openshift"&gt;Setting up Gateway API on OpenShift&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Before you begin, ensure you have the following:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;OpenShift 4.19 or higher with cluster-admin access&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;First you need to create a &lt;code&gt;GatewayClass&lt;/code&gt; object.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Be aware that the &lt;code&gt;GatewayClass&lt;/code&gt; object is &lt;strong&gt;NOT&lt;/strong&gt; namespaced.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: openshift-default
spec:
controllerName: openshift.io/gateway-controller/v1 &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The controller name needs to be exactly as shown. Otherwise the
ingress controller will &lt;strong&gt;NOT&lt;/strong&gt; manage the gateway and associated
resources.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This creates a new pod in the openshift-ingress namespace:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc get po -n openshift-ingress
NAME READY STATUS RESTARTS AGE
istiod-openshift-gateway-7b567bc8b4-4lrt2 1/1 Running 0 12m &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
router-default-6db958cbd-dlbwz 1/1 Running 12 14d&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;this pod got create after applying the gateway class resource&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;code&gt;router-default&lt;/code&gt; is the default openshift ingress pod. The first
difference seems to be the SCC (security context constraint) the pods
are using.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc get po -n openshift-ingress -o jsonpath=&amp;#39;{range .items[*]}{.metadata.name}{&amp;#34;\t&amp;#34;}{.metadata.annotations.openshift\.io/scc}{&amp;#34;\n&amp;#34;}{end}&amp;#39;
istiod-openshift-gateway-7b567bc8b4-4lrt2 restricted-v2
router-default-6db958cbd-dlbwz hostnetwork&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The standard router used host networking for listing on port 80 and 443
on the node where it is running. Our &lt;code&gt;GatewayClass&lt;/code&gt; currently only
provides a pod running Istiod awaiting further configuration. To
actually listen for client request additional configuration is
required.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;A &lt;code&gt;Gateway&lt;/code&gt; is required to listen for client requests. We create the
following gateway:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: http-gateway
namespace: openshift-ingress &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
spec:
gatewayClassName: openshift-default
listeners:
- name: http
protocol: HTTP
port: 80
hostname: &amp;#34;*.apps.ocp.lan.stderr.at&amp;#34;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We create this gateway in the same namespace as the istio
deployment. This is required for OpenShift.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This creates an additional pod in the &lt;code&gt;openshift-ingress&lt;/code&gt; namespace:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc get po
NAME READY STATUS RESTARTS AGE
http-gateway-openshift-default-d476664f5-h87mp 1/1 Running 0 36s&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We also got a new service for the our http-gateway&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;➜ oc get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
http-gateway-openshift-default LoadBalancer 172.30.183.48 10.0.0.150 15021:30251/TCP,80:30437/TCP 4m52s&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The interesting thing is the &lt;code&gt;TYPE&lt;/code&gt; of the service. It’s of type
&lt;code&gt;LoadBalancer&lt;/code&gt;. We have
&lt;a href="https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/networking_operators/metallb-operator"&gt;MetalLB&lt;/a&gt;
deployed in our cluster, this might be the reason for this. We will
try to configure a gateway without MetalLB in in upcoming post.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Lets take a look at the gateway resource&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc get gtw
NAME CLASS ADDRESS PROGRAMMED AGE
http-gateway openshift-default 10.0.0.150 True 3m&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;So this seems to be working. But know we have a problem: the &lt;code&gt;*.apps&lt;/code&gt;
domain that we used for our gateway points already to the default
OpenShift Ingress. We could either&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;redeploy the gateway with a different wildcard domain (e.g. *.gtw…​)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;create a more specific DNS record that points to our new load balancer&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let’s try to confirm this with &lt;em&gt;curl&lt;/em&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ curl -I http://bla.apps.ocp.lan.stderr.at
HTTP/1.0 503 Service Unavailable
pragma: no-cache
cache-control: private, max-age=0, no-cache, no-store
content-type: text/html&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;code&gt;503&lt;/code&gt; is the response of default OpenShift Ingress.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ curl -I http://10.0.0.150
HTTP/1.1 404 Not Found
date: Fri, 29 Aug 2025 14:31:31 GMT
transfer-encoding: chunked&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Our new gateway returns a &lt;code&gt;404&lt;/code&gt; not found response. We choose the
first option and create another wildcard DNS entry for
&lt;code&gt;*.gtw.ocp.lan.stderr.at&lt;/code&gt;. We re-deployed our gateway with the new hostname:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: http-gateway
namespace: openshift-ingress
spec:
gatewayClassName: openshift-default
listeners:
- name: http
protocol: HTTP
port: 80
hostname: &amp;#34;*.gtw.ocp.lan.stderr.at&amp;#34; &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;New hostname for resources exposed via our gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc apply -f gateway.yaml
gateway.gateway.networking.k8s.io/http-gateway created&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This also creates a DNSRecord resource:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc describe dnsrecords.ingress.operator.openshift.io -n openshift-ingress http-gateway-c8d7bfc67-wildcard
Name: http-gateway-c8d7bfc67-wildcard
Namespace: openshift-ingress
Labels: gateway.istio.io/managed=openshift.io-gateway-controller-v1
gateway.networking.k8s.io/gateway-name=http-gateway
istio.io/rev=openshift-gateway
Annotations: &amp;lt;none&amp;gt;
API Version: ingress.operator.openshift.io/v1
Kind: DNSRecord
Metadata:
Creation Timestamp: 2025-08-29T14:49:45Z
Finalizers:
operator.openshift.io/ingress-dns
Generation: 1
Owner References:
API Version: v1
Kind: Service
Name: http-gateway-openshift-default
UID: a023de5d-c428-4249-a190-de3cbfeb6964
Resource Version: 141150968
UID: 7a61a867-216e-40b7-88f3-e3934493c477
Spec:
Dns Management Policy: Managed
Dns Name: *.gtw.ocp.lan.stderr.at.
Record TTL: 30
Record Type: A
Targets:
10.0.0.150
Events: &amp;lt;none&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This resource is only internally used by the OpenShift ingress
operator (see &lt;code&gt;oc explain dnsrecord&lt;/code&gt; for details).&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_creating_httproutes_for_exposing_our_service"&gt;Creating HTTPRoutes for exposing our service.&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To actually expose a HTTP pod via our new gateway we need:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A &lt;em&gt;Namespace&lt;/em&gt; to deploy an example pod. We will use a Nginx for this&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A &lt;em&gt;Service&lt;/em&gt; that exposes our Nginx pod&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;and finally a &lt;em&gt;HTTPRoute&lt;/em&gt; resource&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;For the nginx deployment we used the following manifest:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: v1
kind: Namespace
metadata:
name: gateway-api-test
spec:
finalizers:
- kubernetes
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: gateway-api-test
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: quay.io/nginx/nginx-unprivileged:1.29.1
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: gateway-api-test
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 8080
targetPort: 8080&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let’s see if our nginx pod got deployed successfully:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc get po,svc -n gateway-api-test
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-796cdf7474-b7bqz 1/1 Running 0 20s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx ClusterIP 172.30.42.36 &amp;lt;none&amp;gt; 8080/TCP 21s&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;And finally confirm our &lt;code&gt;Service&lt;/code&gt; is working:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc port-forward -n gateway-api-test svc/nginx 8080 &amp;amp;
Forwarding from 127.0.0.1:8080 -&amp;gt; 8080
Forwarding from [::1]:8080 -&amp;gt; 8080
$ curl -I localhost:8080
Handling connection for 8080
HTTP/1.1 200 OK
Server: nginx/1.29.1
Date: Fri, 29 Aug 2025 15:45:12 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Wed, 13 Aug 2025 14:33:41 GMT
Connection: keep-alive
ETag: &amp;#34;689ca245-267&amp;#34;
Accept-Ranges: bytes&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We received a response from our nginx pod, hurray!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;So next let’s try to create a &lt;code&gt;HTTPRoute&lt;/code&gt; to expose our nginx service to external clients:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: nginx-route
spec:
parentRefs:
- name: http-gateway
namespace: openshift-ingress
hostnames: [&amp;#34;nginx.gtw.ocp.lan.stderr.at&amp;#34;]
rules:
- backendRefs:
- name: nginx
namespace: gateway-api-test
port: 8080&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;One important point here, the &lt;code&gt;Gateway&lt;/code&gt; actually come in two flavors&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;dedicated gateways, only accepting HTTP routes in the same namespace (&lt;code&gt;openshift-ingress&lt;/code&gt;) in our case.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;shared gateways, which also accept HTTP route objects from other namespaces&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;see &lt;a href="https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/ingress_and_load_balancing/configuring-ingress-cluster-traffic#nw-ingress-gateway-api-deployment_ingress-gateway-api"&gt;Gateway API deployment topologies&lt;/a&gt; in the OpenShift documentation for more information.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;As this post is already rather long, we focus on the dedicated gateway topology for now.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
The HTTP route must be deployed in the same namespace as the
gateway if the dedicated topology is used.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;So let’s deploy our &lt;code&gt;HTTPRoute&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc apply -f httproute.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Verify we can reach our nginx pod:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;curl -I http://nginx.gtw.ocp.lan.stderr.at
HTTP/1.1 500 Internal Server Error
date: Fri, 29 Aug 2025 15:57:34 GMT
transfer-encoding: chunked&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This return a &lt;em&gt;500&lt;/em&gt; error, something seems to be wrong with our route,
let’s take a look at the status of the &lt;code&gt;HTTPRoute&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ oc describe gtw http-gateway
.
. (output omitted)
.
Status:
Parents:
Conditions:
Last Transition Time: 2025-08-29T15:54:43Z
Message: Route was valid
Observed Generation: 1
Reason: Accepted
Status: True
Type: Accepted
Last Transition Time: 2025-08-29T15:54:43Z
Message: backendRef nginx/gateway-api-test not accessible to a HTTPRoute in namespace &amp;#34;openshift-ingress&amp;#34; (missing a ReferenceGrant?) &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
Observed Generation: 1
Reason: RefNotPermitted
Status: False &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
Type: ResolvedRefs
Controller Name: openshift.io/gateway-controller/v1
Parent Ref:
Group: gateway.networking.k8s.io
Kind: Gateway
Name: http-gateway
Namespace: openshift-ingress&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Something seems to be wrong as the status is &lt;em&gt;False&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Seems we are missing a ReferenceGrant&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Looking at the
&lt;a href="https://gateway-api.sigs.k8s.io/api-types/referencegrant/" target="_blank" rel="noopener"&gt;upstream&lt;/a&gt;
documentation reveals a security feature of the Gateway API. Before a
&lt;code&gt;HTTPRoute&lt;/code&gt; can reach a service in a &lt;em&gt;different&lt;/em&gt; namespace we must
create a &lt;code&gt;ReferenceGrant&lt;/code&gt; in the namespace providing the service.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;So let’s try to deploy following &lt;code&gt;ReferenceGrant&lt;/code&gt; in the &lt;em&gt;gateway-api-test&lt;/em&gt; namespace:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: nginx
namespace: gateway-api-test
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: openshift-ingress
to:
- group: &amp;#34;&amp;#34;
kind: Service&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Checking the status field of our &lt;code&gt;HTTPRoute&lt;/code&gt; again:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;(output omitted)
Status:
Addresses:
Type: IPAddress
Value: 10.0.0.150
Conditions:
Last Transition Time: 2025-08-29T15:47:07Z
Message: Resource accepted
Observed Generation: 1
Reason: Accepted
Status: True
Type: Accepted
Last Transition Time: 2025-08-29T15:47:08Z
Message: Resource programmed, assigned to service(s) http-gateway-openshift-default.openshift-ingress.svc.cluster.local:80
Observed Generation: 1
Reason: Programmed
Status: True &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
Type: Programmed&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Status&lt;/em&gt; is now &lt;em&gt;True&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;and finally calling the nginx pod again via our gateway:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-console hljs" data-lang="console"&gt;$ curl -I http://nginx.gtw.ocp.lan.stderr.at
HTTP/1.1 200 OK
server: nginx/1.29.1
date: Fri, 29 Aug 2025 16:01:33 GMT
content-type: text/html
content-length: 615
last-modified: Wed, 13 Aug 2025 14:33:41 GMT
etag: &amp;#34;689ca245-267&amp;#34;
accept-ranges: bytes&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Finally everything seems to be in place and working.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In this blog post we took a first look at the Kubernetes Gateway API
and it’s integration into OpenShift. We enabled the Gateway API via a
&lt;code&gt;GatewayClass&lt;/code&gt; resource, created a simple HTTP Gateway via a
&lt;code&gt;Gateway&lt;/code&gt;, deploy a Nginx pod and a Service and exposed the service
via a &lt;code&gt;HTTPRoute&lt;/code&gt; and a &lt;code&gt;ReferenceGrant&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Hopefully an upcoming blog post will cover how to&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;How to deploy a Gateway without MetalLB&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Deploy a TLS secured service&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;implement HTTP redirects&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;rewriting URL’s (if possible)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;and other possibilities of the Gateway API&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item></channel></rss>