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

Architecture Overview

The architecture follows a consolidated ApplicationSet approach where all infrastructure components, operators, and applications are managed through a single applicationset-instance.yaml file. This simplifies deployment and ensures proper installation order through sync_wave annotations.

Installation Order (by sync_wave):

  1. Wave 0: OpenShift GitOps operator installation
  2. Wave 1: Namespaces creation
  3. Wave 2: Operators (rhbk-operator, RBAC configurations)
  4. Wave 3: Infrastructure components (Service Mesh, RHCL Operator, Developer Hub)
  5. Wave 4-7: Applications (NeuralBank Stack, LiteMaaS, etc.)

βš™οΈ Important Requirements

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

⚠️ Important: This repository contains demo cluster domain references (apps.cluster-wjvhz.dynamic.redhatworkshops.io) that must be updated to match your OpenShift cluster’s base domain before deployment.

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.

Automatic Domain Update

We provide a bash script to automatically replace all cluster domain references:

chmod +x update-cluster-domain.sh
./update-cluster-domain.sh <your-cluster-base-domain>

Example:

./update-cluster-domain.sh apps.your-cluster.example.com

The script will:

Manual Domain Update

If you prefer to update manually, search and replace apps.cluster-wjvhz.dynamic.redhatworkshops.io with your cluster’s base domain in the following locations:

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://github.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://github.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://github.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: my-app
            namespace: my-namespace
            helmRepoURL: 'https://example.com/helm'
            chart: my-chart
            chartVersion: "1.0.0"
            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

Use the install.sh script: it updates all cluster domain references and then runs the Ansible playbook.

Prerequisites: Python 3.11+, OpenShift CLI (oc), Ansible Core and collections (see README-INSTALL.md).

Run the installation:

chmod +x install.sh
./install.sh

What install.sh does: Pre-flight checks β†’ detects cluster domain β†’ updates domain in applicationset-instance.yaml, rhbk/keycloak.yaml, rhbk/keycloak-neuralbank-realm.yaml, neuralbank-stack/values.yaml, rhcl-operator/oidc-policy.yaml, servicemeshoperator3/gateway-route.yaml β†’ runs install-gitops.yaml.

What the playbook does: Skips GitOps install if already available β†’ Installs OpenShift GitOps (channel only, no version pin; Automatic) β†’ Applies ApplicationSet β†’ Enables dynamic console plugins via spec.plugins β†’ Obtains OIDC client secret from Keycloak (realm neuralbank, client neuralbank) and updates values/oidc-policy and patches OIDCPolicy β†’ Fixes operator configs (rhbk-operator, devspaces).

Manual Installation (Alternative)

If you prefer to install manually:

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: Keycloak and OIDC (Automated or Manual)

If you use install.sh and the playbook, the realm neuralbank includes the client neuralbank (confidential). The playbook obtains the client secret from Keycloak and updates values.yaml, oidc-policy.yaml, and patches the OIDCPolicy. Commit and push the updated files so ArgoCD syncs.

If installing manually, configure the client in the Keycloak console after the realm is imported:

Access Keycloak Console:

  1. Navigate to the Keycloak route (e.g. rhbk.apps.<your-cluster-domain>)
  2. Log in with admin credentials (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

System Architecture

The following diagram illustrates the complete system architecture, showing how all components interact:

System architecture diagram

Source: docs/diagrams/system-architecture.mmd. Regenerate PNG with ./docs/generate-diagrams.sh.

OIDC Authentication Flow

The following sequence diagram illustrates the complete OIDC authentication flow using Keycloak and Authorino:

OIDC authentication flow sequence diagram

Source: docs/diagrams/oidc-auth-flow.mmd. Regenerate PNG with ./docs/generate-diagrams.sh.

Installation Flow

The following diagram shows the automated installation process (install.sh and Ansible playbook):

Installation flow diagram

Source: docs/diagrams/installation-flow.mmd. Regenerate PNG with ./docs/generate-diagrams.sh.

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.