Connectivity Link

Zero Trust Architecture: Automate the installation with OpenShift GitOps of Connectivity Link, Service Mesh 3, Red Hat Build of Keycloak, and a full-stack application secured with Authorino (OIDC) and rate limiting on OpenShift 4.20+

Redhat OpenSHift kubernetes Helm github linkedin

Developed and written by Maximiliano Pizarro - Specialist Solution Architect at Red Hat LATAM

πŸš€ Quick Start Recommendation

For the best experience, we recommend forking this repository to your own GitHub organization or user account. This allows you to:

After forking, update the repository references in applicationset-instance.yaml to point to your fork.

πŸ“‹ TL;DR

πŸ“– Overview

This repository contains a comprehensive demo of Connectivity Link using a GitOps workflow. It demonstrates how applications and infrastructure are declared as Kubernetes/Helm manifests and managed with ArgoCD (OpenShift GitOps). The demo includes:

Key Components

βš™οΈ Important Requirements

πŸ”§ Configuration: Pre-configure DNS with ApplicationSets

⚠️ Important: Instead of manually updating cluster domain references, you can pre-configure the DNS hostnames directly in the ApplicationSet definitions. This approach uses Kustomize patches and Helm parameters to dynamically inject your cluster domain values at deployment time.

Finding Your Cluster Domain

First, find your OpenShift cluster’s base domain:

oc get ingress.config/cluster -o jsonpath='{.spec.domain}'

Or check your cluster’s console URL - it typically follows the pattern: console-openshift-console.apps.<your-cluster-domain>

This approach uses Kustomize patches to update DNS hostnames in Keycloak, Routes, and OIDC policies. Update the keycloak_host and app_host values in the generator elements:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: connectivity-infra-plain
  namespace: openshift-gitops
spec:
  goTemplate: true
  generators:
    - list:
        elements:
          - name: namespaces
            namespace: openshift-gitops
            path: namespaces
            sync_wave: "1"
          - name: operators
            namespace: openshift-gitops
            path: operators
            sync_wave: "2"
          - name: developer-hub
            namespace: developer-hub
            path: developer-hub
            sync_wave: "2"
          - name: servicemeshoperator3
            namespace: openshift-operators
            path: servicemeshoperator3
            sync_wave: "3"
          - name: rhcl-operator
            namespace: openshift-operators
            path: rhcl-operator
            sync_wave: "3"
  template:
    metadata:
      name: ''
    spec:
      project: default
      source:
        repoURL: 'https://gitlab.com/maximilianoPizarro/connectivity-link.git'
        targetRevision: main
        path: ''
      destination:
        server: 'https://kubernetes.default.svc'
        namespace: ''
      syncPolicy:
        automated:
          selfHeal: true
          prune: true
        syncOptions:
          - CreateNamespace=true
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: connectivity-infra-rhbk
  namespace: openshift-gitops
spec:
  goTemplate: true
  generators:
    - list:
        elements:
          - name: rhbk
            namespace: rhbk-operator
            path: rhbk
            sync_wave: "2"
            keycloak_host: rhbk.apps.cluster-24p6f.24p6f.sandbox2386.opentlc.com
            app_host: neuralbank.apps.cluster-24p6f.24p6f.sandbox2386.opentlc.com
  template:
    metadata:
      name: ''
    spec:
      project: default
      source:
        repoURL: 'https://gitlab.com/maximilianoPizarro/connectivity-link.git'
        targetRevision: main
        path: ''
        kustomize:
          patches:
          - target:
              group: k8s.keycloak.org
              kind: Keycloak
              name: rhbk
            patch: |-
              - op: replace
                path: /spec/hostname/hostname
                value: ""
          - target:
              group: k8s.keycloak.org
              kind: KeycloakRealmImport
              name: neuralbank-full-import
            patch: |-
              - op: replace
                path: /spec/realm/clients/0/redirectUris/0
                value: "https:///*"
          - target:
              group: route.openshift.io
              kind: Route
              name: neuralbank-external-route
            patch: |-
              - op: replace
                path: /spec/host
                value: ""
          - target:
              group: extensions.kuadrant.io
              kind: OIDCPolicy
              name: neuralbank-oidc
            patch: |-
              - op: replace
                path: /spec/provider/issuerURL
                value: "https:///realms/neuralbank"
              - op: replace
                path: /spec/provider/authorizationEndpoint
                value: "https:///realms/neuralbank/protocol/openid-connect/auth"
              - op: replace
                path: /spec/provider/tokenEndpoint
                value: "https:///realms/neuralbank/protocol/openid-connect/token"
              - op: replace
                path: /spec/provider/redirectURI
                value: "https:///auth/callback"
      destination:
        server: 'https://kubernetes.default.svc'
        namespace: ''
      syncPolicy:
        automated:
          selfHeal: true
          prune: true
        syncOptions:
          - CreateNamespace=true

Key Configuration Points:

Option 2: ApplicationSet with Helm Parameters

For Helm-based deployments, use Helm parameters to inject DNS values. Update the keycloak_host and app_host values in the generator:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: connectivity-apps-helm-internal
  namespace: openshift-gitops
spec:
  goTemplate: true
  generators:
    - list:
        elements:
          - name: neuralbank-stack
            namespace: neuralbank-stack
            path: neuralbank-stack
            sync_wave: "5"
            keycloak_host: rhbk.apps.cluster-24p6f.24p6f.sandbox2386.opentlc.com
            app_host: neuralbank.apps.cluster-24p6f.24p6f.sandbox2386.opentlc.com
  template:
    metadata:
      name: ''
    spec:
      project: default
      source:
        repoURL: 'https://gitlab.com/maximilianoPizarro/connectivity-link.git'
        targetRevision: main
        path: ''
        helm:
          parameters:
            - name: "keycloak.issuerUrl"
              value: "https:///realms/neuralbank"
            - name: "keycloak.redirectUri"
              value: "https:///auth/callback"
            - name: "keycloak.authorizationEndpoint"
              value: "https:///realms/neuralbank/protocol/openid-connect/auth"
            - name: "keycloak.tokenEndpoint"
              value: "https:///realms/neuralbank/protocol/openid-connect/token"
            - name: "keycloak.postLogoutRedirectUri"
              value: "https://"
      destination:
        server: 'https://kubernetes.default.svc'
        namespace: ''
      syncPolicy:
        automated:
          selfHeal: true
          prune: true
        syncOptions:
          - CreateNamespace=true

Key Configuration Points:

Option 3: External Helm Chart ApplicationSet

For external Helm charts, configure the chart repository and version:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: connectivity-apps-helm-external
  namespace: openshift-gitops
spec:
  goTemplate: true
  generators:
    - list:
        elements:
          - name: workshop-pipelines
            namespace: workshop-pipelines
            helmRepoURL: 'https://maximilianopizarro.github.io/workshop-pipelines/'
            chart: workshop-pipelines
            chartVersion: "0.1.6"
            sync_wave: "5"
  template:
    metadata:
      name: ''
    spec:
      project: default
      source:
        repoURL: ''
        targetRevision: ''
        chart: ''
      destination:
        server: 'https://kubernetes.default.svc'
        namespace: ''
      syncPolicy:
        automated:
          selfHeal: true
          prune: true
        syncOptions:
          - CreateNamespace=true

Benefits of Pre-configuring DNS

πŸš€ Getting Started

Step 1: Install OpenShift GitOps Operator

Install the OpenShift GitOps Operator (via OperatorHub in the OpenShift console or via OLM). This is the only manual step required before applying the manifests in this demo.

Step 2: Configure DNS in ApplicationSets (Required)

Before proceeding, pre-configure your cluster domain in the ApplicationSet definitions as described in the Configuration section above. Update the keycloak_host and app_host values in the ApplicationSet generators to match your OpenShift cluster domain.

Step 3: Create ApplicationSet Instance

Create the ApplicationSet / ArgoCD instance using the top-level manifest:

oc apply -f applicationset-instance.yaml

Step 4: Configure Keycloak Client Settings (Manual)

After Keycloak is deployed and the realm is imported, you need to manually configure the client settings in the Red Hat Build of Keycloak console. This step is required for proper OIDC authentication flow.

Access Keycloak Console:

  1. Navigate to the Keycloak route in OpenShift (typically rhbk.apps.<your-cluster-domain>)
  2. Log in with the admin credentials (configured in rhbk/keycloak-initial-admin.yaml)
  3. Select the neuralbank realm

Configure Client Settings:

For the neuralbank client (or neuralbank-frontend if using that client):

  1. Navigate to Clients β†’ Select your client (e.g., neuralbank)
  2. Enable Client Authentication:
    • Set Client authentication to ON (this makes it a confidential client)
    • Ensure publicClient is set to false in the configuration
  3. Enable Direct Access Grants:
    • Enable Direct access grants (allows Resource Owner Password Credentials grant type)
  4. Configure PKCE:
    • Set Proof Key for Code Exchange Code Challenge Method to S256
    • This enables PKCE (RFC 7636) for enhanced security in authorization code flows

Generate Client Secret:

After enabling client authentication, you need to generate and retrieve the client secret:

  1. Go to the Credentials tab of your client
  2. Copy the Client secret value
  3. Update the clientSecret field in rhcl-operator/oidc-policy.yaml with this value
  4. Commit and push the change to your repository for ArgoCD to sync

Note: The client secret is required for the OIDC Policy to authenticate with Keycloak. Without it, the OIDC authentication flow will fail.

Step 5: Create OpenShift Route for Gateway (Manual)

The Gateway API Gateway resource (neuralbank-gateway) needs an OpenShift Route to expose it externally. This step must be done manually from the OpenShift console or CLI.

Option 1: Using OpenShift Console

  1. Navigate to Networking β†’ Routes in the istio-system namespace
  2. Click Create Route
  3. Configure the route:
    • Name: neuralbank-external-route (or your preferred name)
    • Hostname: neuralbank.apps.<your-cluster-domain> (or use a wildcard *.apps.<your-cluster-domain>)
    • Service: Select neuralbank-gateway-istio service
    • Target Port: Select http (port 8080)
    • TLS Termination: Edge
    • Insecure Traffic: Redirect

Option 2: Using CLI

You can also create the route using oc:

oc create route edge neuralbank-external-route \
  --service=neuralbank-gateway-istio \
  --hostname=neuralbank.apps.<your-cluster-domain> \
  --port=http \
  --namespace=istio-system

For Wildcard Route:

If you want to use a wildcard route to access both frontend and backend through the same hostname:

oc create route edge neuralbank-external-route \
  --service=neuralbank-gateway-istio \
  --hostname="*.apps.<your-cluster-domain>" \
  --port=http \
  --namespace=istio-system

Note: The wildcard route allows you to access the application using any subdomain under apps.<your-cluster-domain>, which is useful for development and testing. The Gateway and HTTPRoute resources will handle the actual routing based on the hostnames specified in the HTTPRoute manifests.

πŸ“ Repository Structure

Top-Level Files

Developer Hub (developer-hub/)

Red Hat Developer Hub (Backstage) configuration and manifests:

NeuralBank Stack (neuralbank-stack/)

Helm chart for the NeuralBank demo application (frontend, backend, database, and proxy):

Operators (operators/)

Helm charts for operators used in the demo:

Red Hat Build of Keycloak (rhbk/)

Keycloak and related secrets/realm setup used for authentication in the demo:

RHCL Operator (rhcl-operator/)

Red Hat Connectivity Link operator configurations for API gateway, OIDC authentication, and authorization policies:

Key Features:

HTTPRoute Resources

The HTTPRoute resources define how traffic is routed from the Gateway to backend services. These routes use the Kubernetes Gateway API standard and are managed by the Istio Gateway implementation.

Structure:

Example HTTPRoute (neuralbank-api-route):

spec:
  parentRefs:
    - name: neuralbank-gateway
      namespace: istio-system
  hostnames:
    - "neuralbank.apps.<your-cluster-domain>"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api
      backendRefs:
        - name: neuralbank-backend-svc
          port: 8080

This route matches requests to /api/* and /q/* and forwards them to the neuralbank-backend-svc service on port 8080.

OIDC Policy Configuration

The OIDCPolicy resource configures OIDC authentication for protected routes. It integrates with Authorino (via Kuadrant) to enforce authentication at the gateway level.

Key Configuration Fields:

  1. provider.issuerURL: The Keycloak realm issuer URL
    • Format: https://<keycloak-host>/realms/<realm-name>
    • Example: https://rhbk.apps.<your-cluster-domain>/realms/neuralbank
  2. provider.clientID: The Keycloak client ID (must match the client configured in Keycloak)
    • Example: neuralbank
  3. provider.clientSecret: ⚠️ IMPORTANT β€” The client secret generated from Keycloak console
    • This must be obtained from Keycloak after enabling client authentication
    • Steps to get the secret:
      1. Log into Keycloak console
      2. Navigate to your realm β†’ Clients β†’ Select your client
      3. Go to the Credentials tab
      4. Copy the Client secret value
      5. Update the clientSecret field in oidc-policy.yaml
    • Security Note: Consider using a Kubernetes Secret to store the client secret instead of hardcoding it in the YAML file
  4. provider.authorizationEndpoint: Keycloak authorization endpoint
    • Format: https://<keycloak-host>/realms/<realm-name>/protocol/openid-connect/auth
  5. provider.redirectURI: OAuth callback URL (must match a redirect URI configured in Keycloak client)
    • Example: https://neuralbank.apps.<your-cluster-domain>/auth/callback
  6. provider.tokenEndpoint: Keycloak token endpoint
    • Format: https://<keycloak-host>/realms/<realm-name>/protocol/openid-connect/token
  7. targetRef: References the HTTPRoute resource that should be protected by this OIDC policy
    • Example: neuralbank-api-route (protects the /api and /q endpoints)
  8. auth.tokenSource: Defines where to look for the authentication token
    • authorizationHeader: Token in Authorization: Bearer <token> header
    • cookie: Token stored in a cookie (e.g., jwt cookie)

Example OIDC Policy:

spec:
  provider:
    issuerURL: "https://rhbk.apps.<your-cluster-domain>/realms/neuralbank"
    clientID: neuralbank
    clientSecret: "<your-client-secret-from-keycloak>"  # ⚠️ Update this!
    authorizationEndpoint: "https://rhbk.apps.<your-cluster-domain>/realms/neuralbank/protocol/openid-connect/auth"
    redirectURI: "https://neuralbank.apps.<your-cluster-domain>/auth/callback"
    tokenEndpoint: "https://rhbk.apps.<your-cluster-domain>/realms/neuralbank/protocol/openid-connect/token"
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: neuralbank-api-route
  auth:
    tokenSource:
      authorizationHeader:
        prefix: Bearer
        name: Authorization
      cookie:
        name: jwt

Important Notes:

Service Mesh Operator 3 (servicemeshoperator3/)

Service Mesh Operator (Istio) configurations for service mesh control plane and gateway:

Key Features:

Namespaces (namespaces/)

πŸ“ Notes

πŸ—οΈ Architecture Diagrams

The Application Solution without Auth πŸ™Œ:

The Application Solution with Auth πŸ” powered by Red Hat Build of Keycloak & Authorino:

🌟 Benefits of Cloud Native Integration with Kuadrant

Integrating with a cloud native strategy and the Kuadrant project brings significant advantages to modern application deployment and security. The simplicity of this approach is remarkable: by adding just a few manifest files with the appropriate configuration, you can transform your application’s security posture. GitOps plays a crucial role in orchestrating these changes in a clean environment within seconds, enabling a true Zero Trust architecture implementation.

The power of this solution lies in its declarative natureβ€”you define the desired state through Kubernetes manifests, and the GitOps workflow ensures that state is achieved and maintained automatically. This approach eliminates manual configuration errors, provides complete audit trails through Git history, and enables rapid deployment across multiple environments with consistency. The Zero Trust model is enforced at every layer: authentication through Keycloak, authorization via Authorino, rate limiting for API protection, and service mesh policies for inter-service communication. With Connectivity Link and Kuadrant, you’re establishing a comprehensive security framework that scales with your infrastructure, creating a robust foundation for modern, secure microservices architectures.


πŸ“’ Share This Content

If you found this guide helpful, please share it with your network! Help others discover how to implement Zero Trust security with Connectivity Link and GitOps.

Share on LinkedIn Share on Twitter Share on Facebook Share on WhatsApp Share via Email

Connect with the author:

LinkedIn Profile GitHub Repository

Thank you for sharing! Your support helps the community grow and learn together.