이 콘텐츠는 선택한 언어로 제공되지 않습니다.
Configuring and deploying Gateway policies with Connectivity Link
Secure, protect, and connect APIs on OpenShift
Abstract
Preface
Providing feedback on Red Hat documentation
Red Hat appreciates your feedback on product documentation.
To propose improvements, open a Jira issue and describe your suggested changes. Provide as much detail as possible to help the documentation team to address your request quickly.
Prerequisite
- You have a Red Hat Customer Portal account. This account enables you to log in to the Red Hat Jira Software instance. If you do not have an account, you will be prompted to create one.
Procedure
- Click the following link: Create issue.
- In the Summary text box, enter a brief description of the issue.
In the Description text box, provide the following information:
- The URL of the page where you found the issue.
- A detailed description of the issue. You can leave the information in other fields at their default values.
- In the Reporter field, enter your Jira user name.
- Click Create to submit the Jira issue to the documentation team.
Thank you for taking the time to provide feedback.
Chapter 1. Secure, protect, and connect APIs on OpenShift with Connectivity Link
This guide walks you through using Connectivity Link on OpenShift to secure, protect, and connect an API exposed by a Gateway that is based on Kubernetes Gateway API. You can use this walkthrough for a Gateway deployed on a single OpenShift cluster or a Gateway distributed across multiple OpenShift clusters with a shared HTTPS listener hostname. This guide shows how the platform engineer and application developer user roles can each use Connectivity Link to achieve their goals.
1.1. What Connectivity Link can do in multicluster environments
You can leverage Connectivity Link’s capabilities in single or multiple OpenShift clusters. The following features are designed to work across multiple clusters as well as in a single-cluster environment:
-
Multicluster ingress: Connectivity Link provides multicluster ingress connectivity using DNS to bring traffic to your Gateways by using a strategy defined in a
DNSPolicy
. -
Global rate limiting: Connectivity Link can enable global rate limiting use cases when configured to use a shared Redis or Dragonfly store for counters based on limits defined by a
RateLimitPolicy
. -
Global auth: You can configure a Connectivity Link
AuthPolicy
to leverage external auth providers to ensure that different clusters exposing the same API authenticate and authorize in the same way. -
Automatic TLS certificate generation: You can configure a
TLSPolicy
to automatically provision TLS certificates based on Gateway listener hosts by using integration with cert-manager and ACME providers such as Let’s Encrypt. - Integration with federated metrics stores: Connectivity Link has example dashboards and metrics for visualizing your Gateways and observing traffic hitting those Gateways across multiple clusters.
1.2. User role workflows
- Platform engineer: This guide walks through deploying a Gateway that provides secure communication and is protected and ready for use by application development teams to deploy an API. It then walks through using this Gateway in clusters in different geographic regions, leveraging Connectivity Link to bring specific traffic to your geo-located Gateways. This approach reduces latency and distributes load, while still protecting and securing with global rate limiting and auth.
Application developer: This guide walks through deploying your application API and shows how to override your Gateway-level global auth and rate limiting policies to configure your application-level auth and rate limiting requirements.
NoteFor details on how both user roles can observe and monitor Gateways when the OpenShift observability stack and user workload monitoring are deployed, see Connectivity Link Observability Guide.
1.3. Deployment management tooling
While this guide uses kubectl
commands for simplicity, working with multiple clusters is complex. It is best to use a tool such as Argo CD to manage the deployment of resources to multiple clusters.
Chapter 2. Check your Connectivity Link installation and permissions
This guide expects that you have successfully installed Connectivity Link on at least one OpenShift cluster, and that you have the correct user permissions.
Prerequisites
- You completed the Connectivity Link installation steps on one or more clusters, as described in Installing Connectivity Link on OpenShift.
-
You have the
kubectl
oroc
command installed. - You have write access to the OpenShift namespaces used in this guide.
- You have an AWS account with Amazon Route 53 and a DNS zone for the examples in this guide. Google Cloud DNS and Microsoft Azure DNS are also supported.
Optional:
- For rate limiting in a multicluster environment, you have installed Connectivity Link on more than one cluster and have a shared accessible Redis-based datastore. For more details, see Installing Connectivity Link on OpenShift.
- For Observability, OpenShift user workload monitoring is configured to remote write to a central storage system such as Thanos, as described in Connectivity Link Observability Guide.
Chapter 3. Connectivity Link platform engineer workflow
This section of the walkthrough shows how as a platform engineer you can deploy a Gateway that provides secure communication and is protected and ready for use by application development teams. It also shows how to use this Gateway in multiple clusters in different geographic regions.
Prerequisites
In multicluster environments, you must perform the following steps in each cluster individually, unless specifically excluded.
3.1. Step 1 - Set your environment variables
Procedure
Set the following environment variables, which are used for convenience in this guide:
export zid=change-to-your-DNS-zone-ID export rootDomain=demo.example.com export gatewayNS=api-gateway export gatewayName=external export devNS=toystore export AWS_ACCESS_KEY_ID=xxxx export AWS_SECRET_ACCESS_KEY=xxxx export AWS_REGION=us-east-1 export clusterIssuerName=lets-encrypt export EMAIL=foo@example.com
export zid=change-to-your-DNS-zone-ID export rootDomain=demo.example.com export gatewayNS=api-gateway export gatewayName=external export devNS=toystore export AWS_ACCESS_KEY_ID=xxxx export AWS_SECRET_ACCESS_KEY=xxxx export AWS_REGION=us-east-1 export clusterIssuerName=lets-encrypt export EMAIL=foo@example.com
Copy to Clipboard Copied! In this example,
zid
is your hosted zone ID displayed in the AWS Route 53 console.rootDomain
is the top-level AWS Route 53 domain name that you will use for Connectivity Link.NoteThis guide uses environment variables for convenience only. If you know the environment variable values, you can set up the required
.yaml
files in way that suits your needs.
3.2. Step 2 - Set up a DNS provider secret
The DNS provider supplies a credential to access the DNS zones that Connectivity Link can use to set up DNS configuration. You must ensure that this credential has access to only the zones that you want managed.
You must apply the following Secret
resources to each cluster. If you are adding an additional cluster, add them to the new cluster.
Procedure
If your Gateway namespace does not already exist, create it as follows:
kubectl create ns ${gatewayNS}
kubectl create ns ${gatewayNS}
Copy to Clipboard Copied! If the secret for your DNS provider credentials was not already created when installing Connectivity Link, create this secret in your Gateway namespace as follows:
kubectl -n ${gatewayNS} create secret generic aws-credentials \ --type=kuadrant.io/aws \ --from-literal=AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ --from-literal=AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
kubectl -n ${gatewayNS} create secret generic aws-credentials \ --type=kuadrant.io/aws \ --from-literal=AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ --from-literal=AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
Copy to Clipboard Copied! Before adding a TLS issuer, you must also create the credentials secret in the
cert-manager
namespace as follows:kubectl -n cert-manager create secret generic aws-credentials \ --type=kuadrant.io/aws \ --from-literal=AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ --from-literal=AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
kubectl -n cert-manager create secret generic aws-credentials \ --type=kuadrant.io/aws \ --from-literal=AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ --from-literal=AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
Copy to Clipboard Copied!
3.3. Step 3 - Add a TLS issuer
To secure communication to the Gateways, you will define a TLS issuer for TLS certificates. This example uses Let’s Encrypt, but you can use any certificate issuer supported by cert-manager
.
Procedure
Enter the following command to define a TLS issuer. This example uses Let’s Encrypt, which you must also apply to all clusters:
kubectl apply -f - <<EOF apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: ${clusterIssuerName} spec: acme: email: ${EMAIL} privateKeySecretRef: name: le-secret server: https://acme-v02.api.letsencrypt.org/directory solvers: - dns01: route53: hostedZoneID: ${zid} region: ${AWS_REGION} accessKeyIDSecretRef: key: AWS_ACCESS_KEY_ID name: aws-credentials secretAccessKeySecretRef: key: AWS_SECRET_ACCESS_KEY name: aws-credentials EOF
kubectl apply -f - <<EOF apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: ${clusterIssuerName} spec: acme: email: ${EMAIL} privateKeySecretRef: name: le-secret server: https://acme-v02.api.letsencrypt.org/directory solvers: - dns01: route53: hostedZoneID: ${zid} region: ${AWS_REGION} accessKeyIDSecretRef: key: AWS_ACCESS_KEY_ID name: aws-credentials secretAccessKeySecretRef: key: AWS_SECRET_ACCESS_KEY name: aws-credentials EOF
Copy to Clipboard Copied! Wait for the
ClusterIssuer
to become ready as follows:kubectl wait clusterissuer/${clusterIssuerName} --for=condition=ready=true
kubectl wait clusterissuer/${clusterIssuerName} --for=condition=ready=true
Copy to Clipboard Copied!
3.4. Step 4 - Set up a Gateway
For Connectivity Link to balance traffic using DNS across two or more clusters, you must define a Gateway with a shared host. You will define this by using an HTTPS listener with a wildcard hostname based on the root domain. As mentioned earlier, you must apply these resources to all clusters.
For now, the Gateway is set to accept an HTTPRoute
from the same namespace only. This allows you to restrict who can use the Gateway until it is ready for general use.
Procedure
Enter the following command to create the Gateway:
kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: ${gatewayName} namespace: ${gatewayNS} labels: kuadrant.io/gateway: "true" spec: gatewayClassName: istio listeners: - allowedRoutes: namespaces: from: Same hostname: "*.${rootDomain}" name: api port: 443 protocol: HTTPS tls: certificateRefs: - group: "" kind: Secret name: api-${gatewayName}-tls mode: Terminate EOF
kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: ${gatewayName} namespace: ${gatewayNS} labels: kuadrant.io/gateway: "true" spec: gatewayClassName: istio listeners: - allowedRoutes: namespaces: from: Same hostname: "*.${rootDomain}" name: api port: 443 protocol: HTTPS tls: certificateRefs: - group: "" kind: Secret name: api-${gatewayName}-tls mode: Terminate EOF
Copy to Clipboard Copied! Check the status of your Gateway as follows:
kubectl get gateway ${gatewayName} -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}' kubectl get gateway ${gatewayName} -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Programmed")].message}'
kubectl get gateway ${gatewayName} -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}' kubectl get gateway ${gatewayName} -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Programmed")].message}'
Copy to Clipboard Copied! Your Gateway should be accepted and programmed, which means valid and assigned an external address.
However, if you check your HTTPS listener status as follows, you will see that it is not yet programmed or ready to accept traffic due to bad TLS configuration:
kubectl get gateway ${gatewayName} -n ${gatewayNS} -o=jsonpath='{.status.listeners[0].conditions[?(@.type=="Programmed")].message}'
kubectl get gateway ${gatewayName} -n ${gatewayNS} -o=jsonpath='{.status.listeners[0].conditions[?(@.type=="Programmed")].message}'
Copy to Clipboard Copied! Connectivity Link can help with this by using a TLSPolicy, which is described in the next step.
3.4.1. Optional: Configure metrics to be scraped from the Gateway instance
If you have Prometheus set up in your cluster, you can configure a PodMonitor
to scrape metrics directly from the Gateway pod. This configuration is required for metrics such as istio_requests_total
. You must add the following configuration in the namespace where the Gateway is running:
kubectl apply -f - <<EOF apiVersion: monitoring.coreos.com/v1 kind: PodMonitor metadata: name: istio-proxies-monitor namespace: ${gatewayNS} spec: selector: matchExpressions: - key: istio-prometheus-ignore operator: DoesNotExist podMetricsEndpoints: - path: /stats/prometheus interval: 30s relabelings: - action: keep sourceLabels: ["__meta_kubernetes_pod_container_name"] regex: "istio-proxy" - action: keep sourceLabels: ["__meta_kubernetes_pod_annotationpresent_prometheus_io_scrape"] - action: replace regex: (\d+);(([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}) replacement: "[\$2]:\$1" sourceLabels: [ "__meta_kubernetes_pod_annotation_prometheus_io_port", "__meta_kubernetes_pod_ip", ] targetLabel: "__address__" - action: replace regex: (\d+);((([0-9]+?)(\.|$)){4}) replacement: "\$2:\$1" sourceLabels: [ "__meta_kubernetes_pod_annotation_prometheus_io_port", "__meta_kubernetes_pod_ip", ] targetLabel: "__address__" - action: labeldrop regex: "__meta_kubernetes_pod_label_(.+)" - sourceLabels: ["__meta_kubernetes_namespace"] action: replace targetLabel: namespace - sourceLabels: ["__meta_kubernetes_pod_name"] action: replace targetLabel: pod_name EOF
kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: istio-proxies-monitor
namespace: ${gatewayNS}
spec:
selector:
matchExpressions:
- key: istio-prometheus-ignore
operator: DoesNotExist
podMetricsEndpoints:
- path: /stats/prometheus
interval: 30s
relabelings:
- action: keep
sourceLabels: ["__meta_kubernetes_pod_container_name"]
regex: "istio-proxy"
- action: keep
sourceLabels:
["__meta_kubernetes_pod_annotationpresent_prometheus_io_scrape"]
- action: replace
regex: (\d+);(([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})
replacement: "[\$2]:\$1"
sourceLabels:
[
"__meta_kubernetes_pod_annotation_prometheus_io_port",
"__meta_kubernetes_pod_ip",
]
targetLabel: "__address__"
- action: replace
regex: (\d+);((([0-9]+?)(\.|$)){4})
replacement: "\$2:\$1"
sourceLabels:
[
"__meta_kubernetes_pod_annotation_prometheus_io_port",
"__meta_kubernetes_pod_ip",
]
targetLabel: "__address__"
- action: labeldrop
regex: "__meta_kubernetes_pod_label_(.+)"
- sourceLabels: ["__meta_kubernetes_namespace"]
action: replace
targetLabel: namespace
- sourceLabels: ["__meta_kubernetes_pod_name"]
action: replace
targetLabel: pod_name
EOF
For more information on configuring metrics, see the Connectivity Link Observability Guide.
3.5. Step 5 - Configure your Gateway policies and HTTP route
While your Gateway is now deployed, it has no exposed endpoints and your HTTPS listener is not programmed. Next, you can set up a TLSPolicy
that leverages your CertificateIssuer
to set up your HTTPS listener certificates.
You will define an AuthPolicy
that will set up a default HTTP 403
response for any unprotected endpoints, as well as a RateLimitPolicy
that will set up a default artificially low global limit to further protect any endpoints exposed by this Gateway.
You will also define a DNSPolicy
with a load balancing strategy, and an HTTPRoute
for your Gateway to communicate with your backend application API.
3.5.1. Set the TLS policy
Procedure
Set the
TLSPolicy
for your Gateway as follows:kubectl apply -f - <<EOF apiVersion: kuadrant.io/v1 kind: TLSPolicy metadata: name: ${gatewayName}-tls namespace: ${gatewayNS} spec: targetRef: name: ${gatewayName} group: gateway.networking.k8s.io kind: Gateway issuerRef: group: cert-manager.io kind: ClusterIssuer name: ${clusterIssuerName} EOF
kubectl apply -f - <<EOF apiVersion: kuadrant.io/v1 kind: TLSPolicy metadata: name: ${gatewayName}-tls namespace: ${gatewayNS} spec: targetRef: name: ${gatewayName} group: gateway.networking.k8s.io kind: Gateway issuerRef: group: cert-manager.io kind: ClusterIssuer name: ${clusterIssuerName} EOF
Copy to Clipboard Copied! Check that your TLS policy was accepted by the controller as follows:
kubectl get tlspolicy ${gatewayName}-tls -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}'
kubectl get tlspolicy ${gatewayName}-tls -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}'
Copy to Clipboard Copied!
3.5.2. Set the Auth policy
Procedure
Set a default, deny-all
AuthPolicy
for your Gateway as follows:kubectl apply -f - <<EOF apiVersion: kuadrant.io/v1 kind: AuthPolicy metadata: name: ${gatewayName}-auth namespace: ${gatewayNS} spec: targetRef: group: gateway.networking.k8s.io kind: Gateway name: ${gatewayName} defaults: rules: authorization: "deny": opa: rego: "allow = false" EOF
kubectl apply -f - <<EOF apiVersion: kuadrant.io/v1 kind: AuthPolicy metadata: name: ${gatewayName}-auth namespace: ${gatewayNS} spec: targetRef: group: gateway.networking.k8s.io kind: Gateway name: ${gatewayName} defaults: rules: authorization: "deny": opa: rego: "allow = false" EOF
Copy to Clipboard Copied! Check that your auth policy was accepted by the controller as follows:
kubectl get authpolicy ${gatewayName}-auth -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}'
kubectl get authpolicy ${gatewayName}-auth -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}'
Copy to Clipboard Copied!
3.5.3. Set the rate limit policy
Procedure
Set the default
RateLimitPolicy
for your Gateway as follows:kubectl apply -f - <<EOF apiVersion: kuadrant.io/v1 kind: RateLimitPolicy metadata: name: ${gatewayName}-rlp namespace: ${gatewayNS} spec: targetRef: group: gateway.networking.k8s.io kind: Gateway name: ${gatewayName} defaults: limits: "low-limit": rates: - limit: 2 window: 10s EOF
kubectl apply -f - <<EOF apiVersion: kuadrant.io/v1 kind: RateLimitPolicy metadata: name: ${gatewayName}-rlp namespace: ${gatewayNS} spec: targetRef: group: gateway.networking.k8s.io kind: Gateway name: ${gatewayName} defaults: limits: "low-limit": rates: - limit: 2 window: 10s EOF
Copy to Clipboard Copied! NoteIt might take a few minutes for the
RateLimitPolicy
to be applied depending on your cluster. The limit in this example is artificially low to show it working easily.To check your rate limits have been accepted, enter the following command:
kubectl get ratelimitpolicy ${gatewayName}-rlp -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}'
kubectl get ratelimitpolicy ${gatewayName}-rlp -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}'
Copy to Clipboard Copied!
3.5.4. Set the DNS policy
Procedure
Set the
DNSPolicy
for your Gateway as follows:kubectl apply -f - <<EOF apiVersion: kuadrant.io/v1 kind: DNSPolicy metadata: name: ${gatewayName}-dnspolicy namespace: ${gatewayNS} spec: targetRef: name: ${gatewayName} group: gateway.networking.k8s.io kind: Gateway providerRefs: - name: aws-credentials loadBalancing: weight: 120 geo: EU defaultGeo: true EOF
kubectl apply -f - <<EOF apiVersion: kuadrant.io/v1 kind: DNSPolicy metadata: name: ${gatewayName}-dnspolicy namespace: ${gatewayNS} spec: targetRef: name: ${gatewayName} group: gateway.networking.k8s.io kind: Gateway providerRefs: - name: aws-credentials loadBalancing: weight: 120 geo: EU defaultGeo: true EOF
Copy to Clipboard Copied! NoteThe
DNSPolicy
will use the DNS ProviderSecret
that you defined earlier. Thegeo
in this example isEU
, but you can change this to suit your requirements.Check that your
DNSPolicy
has been accepted as follows:kubectl get dnspolicy ${gatewayName}-dnspolicy -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}'
kubectl get dnspolicy ${gatewayName}-dnspolicy -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Accepted")].message}'
Copy to Clipboard Copied!
3.5.5. Create an HTTP route
For test purposes, this section assumes that the toystore application is deployed. For more information, see Chapter 4, Connectivity Link application developer workflow.
Procedure
Create an
HTTPRoute
to test your Gateway as follows:kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: test namespace: ${gatewayNS} labels: service: toystore spec: parentRefs: - name: ${gatewayName} namespace: ${gatewayNS} hostnames: - "test.${rootDomain}" rules: - backendRefs: - name: toystore port: 80 EOF
kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: test namespace: ${gatewayNS} labels: service: toystore spec: parentRefs: - name: ${gatewayName} namespace: ${gatewayNS} hostnames: - "test.${rootDomain}" rules: - backendRefs: - name: toystore port: 80 EOF
Copy to Clipboard Copied! Check your Gateway policies are enforced as follows:
kubectl get dnspolicy ${gatewayName}-dnspolicy -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Enforced")].message}' kubectl get authpolicy ${gatewayName}-auth -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Enforced")].message}' kubectl get ratelimitpolicy ${gatewayName}-rlp -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Enforced")].message}'
kubectl get dnspolicy ${gatewayName}-dnspolicy -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Enforced")].message}' kubectl get authpolicy ${gatewayName}-auth -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Enforced")].message}' kubectl get ratelimitpolicy ${gatewayName}-rlp -n ${gatewayNS} -o=jsonpath='{.status.conditions[?(@.type=="Enforced")].message}'
Copy to Clipboard Copied! Check your HTTPS listener is ready as follows:
kubectl get gateway ${gatewayName} -n ${gatewayNS} -o=jsonpath='{.status.listeners[0].conditions[?(@.type=="Programmed")].message}'
kubectl get gateway ${gatewayName} -n ${gatewayNS} -o=jsonpath='{.status.listeners[0].conditions[?(@.type=="Programmed")].message}'
Copy to Clipboard Copied!
3.6. Step 6 - Test connectivity and deny all auth
You can use curl
to test your endpoint connectivity and auth.
Procedure
Enter the following command:
curl -w "%{http_code}" https://$(kubectl get httproute test -n ${gatewayNS} -o=jsonpath='{.spec.hostnames[0]}')
curl -w "%{http_code}" https://$(kubectl get httproute test -n ${gatewayNS} -o=jsonpath='{.spec.hostnames[0]}')
Copy to Clipboard Copied! You should see an HTTP
403
response.
3.7. Step 7 - Open up the Gateway for other namespaces
Because you have configured the Gateway, secured it with Connectivity Link policies, and tested it, you can now open it up for use by other teams in other namespaces.
Procedure
Enter the following command:
kubectl patch gateway ${gatewayName} -n ${gatewayNS} --type='json' -p='[{"op": "replace", "path": "/spec/listeners/0/allowedRoutes/namespaces/from", "value":"All"}]'
kubectl patch gateway ${gatewayName} -n ${gatewayNS} --type='json' -p='[{"op": "replace", "path": "/spec/listeners/0/allowedRoutes/namespaces/from", "value":"All"}]'
Copy to Clipboard Copied!
3.8. Step 8 - Extend the Gateway to multiple clusters and configure geo-based routing
Procedure
To distribute this Gateway across multiple clusters, repeat this setup process for each cluster.
By default, this will implement a round-robin DNS strategy to distribute traffic evenly across the different clusters. Setting up your Gateways to serve clients based on their geographic location is straightforward with your current configuration.
Assuming that you have deployed Gateway instances across multiple clusters as per this guide, the next step involves updating the DNS controller with the geographic regions of the visible Gateways.
For instance, if you have one cluster in North America and another in the EU, you can direct traffic to these Gateways based on their location by configuring the appropriate policy. For your North American cluster, you can create a DNSPolicy and set the
loadBalancing:geo
field toUS
.
Chapter 4. Connectivity Link application developer workflow
This section of the walkthrough shows how as an application developer you can override your existing Gateway-level policies to configure your application-level routing, authentication, and rate limiting requirements.
Prerequisites
- Your Connectivity Link environment is set up and policies are configured as described in Chapter 3, Connectivity Link platform engineer workflow.
4.1. Step 1 - Deploy the toystore app
Procedure
Create the namespace for your application as follows, if it does not already exist:
kubectl create ns ${devNS}
kubectl create ns ${devNS}
Copy to Clipboard Copied! Deploy the
toystore
application to your developer namespace as follows, if it has not already been deployed:kubectl apply -f https://raw.githubusercontent.com/Kuadrant/Kuadrant-operator/main/examples/toystore/toystore.yaml -n ${devNS}
kubectl apply -f https://raw.githubusercontent.com/Kuadrant/Kuadrant-operator/main/examples/toystore/toystore.yaml -n ${devNS}
Copy to Clipboard Copied!
4.2. Step 2 - Set up the HTTPRoute for your API
Procedure
Enter the following command to define an HTTP route for your Toystore application API:
kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: toystore labels: deployment: toystore service: toystore spec: parentRefs: - name: ${gatewayName} namespace: ${gatewayNS} hostnames: - "api.${rootDomain}" rules: - matches: - method: GET path: type: PathPrefix value: "/cars" - method: GET path: type: PathPrefix value: "/dolls" backendRefs: - name: toystore port: 80 - matches: - path: type: PathPrefix value: "/admin" backendRefs: - name: toystore port: 80 EOF
kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: toystore labels: deployment: toystore service: toystore spec: parentRefs: - name: ${gatewayName} namespace: ${gatewayNS} hostnames: - "api.${rootDomain}" rules: - matches: - method: GET path: type: PathPrefix value: "/cars" - method: GET path: type: PathPrefix value: "/dolls" backendRefs: - name: toystore port: 80 - matches: - path: type: PathPrefix value: "/admin" backendRefs: - name: toystore port: 80 EOF
Copy to Clipboard Copied! With this
HTTPRoute
in place, the service that you deployed is now exposed by the Gateway.You can access your API endpoint over HTTPS as follows:
export INGRESS_HOST=$(kubectl get gtw ${gatewayName} -o jsonpath='{.status.addresses[0].value}' -n api-gateway) curl --resolve api.${rootDomain}:443:${INGRESS_HOST} "https://api.${rootDomain}/cars"
export INGRESS_HOST=$(kubectl get gtw ${gatewayName} -o jsonpath='{.status.addresses[0].value}' -n api-gateway) curl --resolve api.${rootDomain}:443:${INGRESS_HOST} "https://api.${rootDomain}/cars"
Copy to Clipboard Copied!
4.3. Step 3 - Override the Gateway’s deny-all AuthPolicy
Next, you will allow authenticated access to the Toystore API. You can do this by defining an AuthPolicy
that targets the HTTPRoute
resource created in the previous step.
Any new HTTPRoutes will still be affected by the existing Gateway-level policy. Because you want users to now access this API, you must override that Gateway policy. For simplicity, you can use API keys to authenticate the requests, but other options such as OpenID Connect are also available.
Procedure
Define API keys for bob and alice users as follows:
kubectl apply -f - <<EOF apiVersion: v1 kind: Secret metadata: name: bob-key labels: authorino.kuadrant.io/managed-by: authorino app: toystore annotations: secret.kuadrant.io/user-id: bob stringData: api_key: IAMBOB type: Opaque --- apiVersion: v1 kind: Secret metadata: name: alice-key labels: authorino.kuadrant.io/managed-by: authorino app: toystore annotations: secret.kuadrant.io/user-id: alice stringData: api_key: IAMALICE type: Opaque EOF
kubectl apply -f - <<EOF apiVersion: v1 kind: Secret metadata: name: bob-key labels: authorino.kuadrant.io/managed-by: authorino app: toystore annotations: secret.kuadrant.io/user-id: bob stringData: api_key: IAMBOB type: Opaque --- apiVersion: v1 kind: Secret metadata: name: alice-key labels: authorino.kuadrant.io/managed-by: authorino app: toystore annotations: secret.kuadrant.io/user-id: alice stringData: api_key: IAMALICE type: Opaque EOF
Copy to Clipboard Copied! Override the
AuthPolicy
to start accepting the API keys as follows:kubectl apply -f - <<EOF apiVersion: kuadrant.io/v1 kind: AuthPolicy metadata: name: toystore spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: toystore rules: authentication: "api-key-users": apiKey: selector: matchLabels: app: toystore credentials: authorizationHeader: prefix: APIKEY response: success: filters: "identity": json: properties: "userid": selector: auth.identity.metadata.annotations.secret\.kuadrant\.io/user-id EOF
kubectl apply -f - <<EOF apiVersion: kuadrant.io/v1 kind: AuthPolicy metadata: name: toystore spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: toystore rules: authentication: "api-key-users": apiKey: selector: matchLabels: app: toystore credentials: authorizationHeader: prefix: APIKEY response: success: filters: "identity": json: properties: "userid": selector: auth.identity.metadata.annotations.secret\.kuadrant\.io/user-id EOF
Copy to Clipboard Copied!
4.4. Step 4 - Override the Gateway’s RateLimitPolicy
The configured Gateway limits provide a good set of limits for the general case. However, as the developer of the Toystore API, you might want to only allow a certain number of requests for specific users, and a general limit for all other users.
Procedure
Enter the following command to set rate limits for specific users:
kubectl apply -f - <<EOF apiVersion: kuadrant.io/v1 kind: RateLimitPolicy metadata: name: toystore spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: toystore limits: "general-user": rates: - limit: 1 duration: 3 unit: second counters: - metadata.filter_metadata.envoy\.filters\.http\.ext_authz.identity.userid when: - selector: metadata.filter_metadata.envoy\.filters\.http\.ext_authz.identity.userid operator: neq value: bob "bob-limit": rates: - limit: 2 duration: 3 unit: second when: - selector: metadata.filter_metadata.envoy\.filters\.http\.ext_authz.identity.userid operator: eq value: bob EOF
kubectl apply -f - <<EOF apiVersion: kuadrant.io/v1 kind: RateLimitPolicy metadata: name: toystore spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: toystore limits: "general-user": rates: - limit: 1 duration: 3 unit: second counters: - metadata.filter_metadata.envoy\.filters\.http\.ext_authz.identity.userid when: - selector: metadata.filter_metadata.envoy\.filters\.http\.ext_authz.identity.userid operator: neq value: bob "bob-limit": rates: - limit: 2 duration: 3 unit: second when: - selector: metadata.filter_metadata.envoy\.filters\.http\.ext_authz.identity.userid operator: eq value: bob EOF
Copy to Clipboard Copied! NoteIt might take a few minutes for the
RateLimitPolicy
to be applied, depending on your cluster.As another example, you could give bob twice as many requests to use compared to all other users.
To test your new setup, send requests as alice as follows:
while :; do curl --resolve api.${rootDomain}:443:${INGRESS_HOST} --write-out '%{http_code}\n' --silent --output /dev/null -H 'Authorization: APIKEY IAMALICE' "https://api.${rootDomain}/cars" | grep -E --color "\b(429)\b|$"; sleep 1; done
while :; do curl --resolve api.${rootDomain}:443:${INGRESS_HOST} --write-out '%{http_code}\n' --silent --output /dev/null -H 'Authorization: APIKEY IAMALICE' "https://api.${rootDomain}/cars" | grep -E --color "\b(429)\b|$"; sleep 1; done
Copy to Clipboard Copied! Send requests as bob as follows:
while :; do curl --resolve api.${rootDomain}:443:${INGRESS_HOST} --write-out '%{http_code}\n' --silent --output /dev/null -H 'Authorization: APIKEY IAMBOB' "https://api.${rootDomain}/cars" | grep -E --color "\b(429)\b|$"; sleep 1; done
while :; do curl --resolve api.${rootDomain}:443:${INGRESS_HOST} --write-out '%{http_code}\n' --silent --output /dev/null -H 'Authorization: APIKEY IAMBOB' "https://api.${rootDomain}/cars" | grep -E --color "\b(429)\b|$"; sleep 1; done
Copy to Clipboard Copied! NoteIf you set up a DNS provider and configured a
DNSPolicy
as described in the platform engineer workflow, you can omit the--resolve api.${rootDomain}:443:${INGRESS_HOST}
flag. For example, for alice as follows:while :; do curl --write-out '%{http_code}\n' --silent --output /dev/null -H 'Authorization: APIKEY IAMALICE' "https://api.${rootDomain}/cars" | grep -E --color "\b(429)\b|$"; sleep 1; done
while :; do curl --write-out '%{http_code}\n' --silent --output /dev/null -H 'Authorization: APIKEY IAMALICE' "https://api.${rootDomain}/cars" | grep -E --color "\b(429)\b|$"; sleep 1; done
Copy to Clipboard Copied! NoteIf you followed through this guide on more than one cluster, the DNS record for the
HTTPRoute
hostname will have multiple IP addresses. This means that requests will be made in a round-robin pattern across clusters because your DNS provider sends different responses to lookups.
Appendix A. Using your Red Hat subscription
Red Hat Connectivity Link is provided through a software subscription. To manage your subscriptions, access your account at the Red Hat Customer Portal.
Managing your subscriptions
- Go to access.redhat.com.
- If you do not already have an account, create one.
- Log in to your account.
- In the menu bar, click Subscriptions to view and manage your subscriptions.
Revised on 2025-05-14 11:01:41 UTC