Skip to content

About

Portable Helm chart: Traefik v3.7.6 + Keycloak-protected dashboard (oauth2-proxy/ForwardAuth) for OpenShift, RKE2, k3s, kubeadm & Tanzu. Feature-flag driven — MetalLB/NSX ALB/kube-vip/Cilium LB, External Secrets+Vault, optional NetworkPolicies, air-gap. Fail-fast validation. AI-generated, not yet tested on a live cluster.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Traefik + Keycloak-protected dashboard — portable across Kubernetes distros

CI License: MIT Last commit Repo size Issues Stars PRs welcome Status: untested on a live cluster Generated by Claude Opus 4.8

Platforms On-prem Air-gapped / disconnected Traefik v3.7.6 Keycloak OIDC Helm chart 41.0.2 oauth2-proxy v7.7.1 Argo CD LoadBalancer

⚠️ AI-generated & untested — read before using. This repository was generated by Claude (Anthropic's Claude Code, Opus 4.8) while designing a portable, multi-distribution Helm layout for Traefik + Keycloak. It has not been deployed or tested on a real cluster — neither OpenShift, RKE2, k3s, kubeadm nor Tanzu. Validation so far is limited to helm template, helm lint and offline YAML/schema checks — no live install, no end-to-end auth flow has been exercised. Treat it as a reviewed starting point: read every manifest and values file, replace the dummy values (domains, realms, client secrets, LB pools, image registries), and validate in a non-production environment before any real use.

A single umbrella Helm chart that deploys the official Traefik chart (vendored) behind a LoadBalancer, with the Traefik dashboard authenticated against Keycloak (oauth2-proxy + ForwardAuth) and restricted by role (traefik-admin). It is portable across on-prem / air-gapped Kubernetes distributions — OpenShift, Rancher RKE2, k3s, kubeadm, VMware Tanzu/TKG — driven by a small set of feature flags, with fail-fast validation that rejects impossible combinations at helm template time instead of shipping a broken deployment.

Rather than a per-distribution fork, one core composes along a few independent axes:

  • Distribution — platform: OpenShift (SCC-aware UID handling) · RKE2 · k3s · kubeadm · generic. (§2, §4)
  • Load balancer — loadBalancer.backend: MetalLB · NSX ALB (Avi) · kube-vip · Cilium LB-IPAM · generic. (§4)
  • Secrets — secret.mode: pre-created (Sealed Secrets) · inline (dev) · External Secrets Operator + HashiCorp Vault. (§10)
  • Keycloak TLS trust — caTrust.mode: none · OpenShift CA injector · custom CA ConfigMap · insecure (test). (§9)
  • Network — optional NetworkPolicies for default-deny CNIs (Calico / Cilium); storage (CSI) is not applicable — the workload is stateless. (§11)
  • Air-gap — vendored Traefik chart (no traefik.github.io pull) + internal registry-mirror overrides for every image. (§12)
  • Delivery — imperative (install.sh) or GitOps (single multi-source ArgoCD Application). (§8, §16)

This is the multi-distribution sibling of traefik-keycloak-openshift-gitops (OpenShift-only). If you deploy on OpenShift exclusively, either works; this repo generalises the same design to any distribution.

🗺️ Quick Navigation Map

This repository provides an enterprise, production-ready umbrella Helm chart that deploys the official Traefik Ingress Controller (v3.7+) with a Keycloak-protected dashboard (oauth2-proxy + ForwardAuth), portable across Red Hat OpenShift, Rancher RKE2, k3s, kubeadm, and VMware Tanzu. Use this map to navigate the repository layout, architectural documentation, and multimedia series:

🧭 Repository Architecture Blueprint

traefik-keycloak-portable/                   # 🌐 Multi-Distribution Ingress & Keycloak SSO Platform
├── 📁 argocd/                               # Declarative GitOps Multi-Source Delivery
│   ├── 📁 apps/                             # ArgoCD Applications (traefik-keycloak)
│   ├── 📄 project.yaml                      # AppProject scoping repos, namespaces, and cluster resources
│   └── 📄 README.md                         # Multi-source $values GitOps pattern & runbooks
├── 📁 docs/                                 # Architectural Deep Dives & Distribution Guides
│   ├── 📄 air-gapped.md                     # Disconnected deployment, vendoring & image mirroring
│   ├── 📄 external-secrets.md               # HashiCorp Vault ESO SecretStore & token sync
│   ├── 📄 network-policies.md               # Calico & Cilium default-deny NetworkPolicies
│   └── 📄 tls-secret.md                     # CA trust chains, cert-manager & TLS secret injection
├── 📁 helm/                                 # Umbrella Helm Chart & Templates
│   └── 📁 traefik-keycloak/                 # Portable umbrella chart (Chart v41.0.2 / Traefik v3.7.6)
│       ├── 📁 charts/                       # Vendored official Traefik subchart (offline-ready)
│       ├── 📁 templates/                    # Core oauth2-proxy, IngressRoute & _validate.tpl
│       └── 📄 values.yaml                   # Base values & orthogonal feature flag defaults
├── 📁 keycloak/                             # Identity Provider Configuration & Setup
│   └── 📄 keycloak-client-setup.md          # OIDC client, mapper & traefik-admin role guide
├── 📁 metallb/                              # Bare-Metal Layer 2 / BGP Load Balancing
│   └── 📄 ipaddresspool.example.yaml        # On-prem IPAddressPool & L2Advertisement definition
├── 📁 secrets/                              # Secret Synchronization Templates
│   └── 📄 oauth2-proxy-secret.example.yaml  # Sealed/Vault secret reference for cookie & client secrets
├── 📁 sites/                                # Distribution-Specific Values Overlays
│   ├── 📄 values-openshift.yaml             # Red Hat OpenShift (restricted-v2 SCC, dynamic UIDs)
│   ├── 📄 values-rke2.yaml                  # Rancher RKE2 (Cilium LB-IPAM, non-root)
│   ├── 📄 values-k3s.yaml                   # Lightweight edge k3s (MetalLB, Traefik disabled)
│   ├── 📄 values-kubeadm.yaml               # Bare-metal kubeadm (MetalLB, local CA ConfigMap)
│   ├── 📄 values-tanzu-nsx.yaml             # VMware Tanzu / TKG with NSX ALB (Avi Vantage)
│   └── 📄 values-tanzu-kubevip.yaml         # VMware Tanzu / TKG with kube-vip virtual IP
├── 📄 CHANGELOG.md                          # Release history & version notes
├── 📄 install.sh                            # Imperative deployment script with pre-flight linting
└── 📄 PORTABILITY.md                        # Portability matrix across distros & load balancers

🎬 AI-Generated Multimedia Series (YouTube)

This repository is accompanied by an educational video masterclass series and technical shorts synthesized with Gemini NotebookLM based directly on the multi-distribution architecture, ForwardAuth security flows, and air-gapped runbooks from this project. All videos are freely accessible on YouTube on the @nubenetes channel.

Note

Multilingual Learning Experience:
Content features sessions with original spoken audio in English 🇺🇸, with automated YouTube closed captions (CC) translated into 20+ languages for global platform engineering and SRE teams.

📽️ Full-Length Technical Deep Dives (Architecture Masterclasses)

# Video Guide Title Engineering Domain & Core Architecture Audio Duration Direct Link
01 Portable Traefik & Keycloak Helm Chart Multi-Distro Architecture & Feature Flags
Orthogonal axes, fail-fast validation in _validate.tpl, vendored offline charts
🇺🇸 EN 9:26 ▶️ Watch
02 Multi-Distro Kubernetes Portability Platform Engineering & Distro Quirks
OpenShift restricted-v2 SCC vs vanilla K8s, dynamic UIDs & CNI NetworkPolicies
🇺🇸 EN 9:26 ▶️ Watch
03 Traefik & Keycloak OIDC Authentication Zero-Trust Ingress & ForwardAuth
oauth2-proxy token exchange, statusRewrites 401 to 302, role RBAC (traefik-admin)
🇺🇸 EN 7:04 ▶️ Watch
04 Portable Kubernetes Networking On-Premises LoadBalancer Backends
MetalLB (L2/BGP), VMware NSX ALB (Avi), kube-vip virtual IP & Cilium LB-IPAM
🇺🇸 EN 7:52 ▶️ Watch
05 End-to-End Portable Traefik & Keycloak Air-Gapped GitOps Masterclass
Full Day 0 to Day 2 lifecycle, HashiCorp Vault ESO sync, multi-source ArgoCD
🇺🇸 EN 9:46 ▶️ Watch

⚡ Video Shorts Matrix

# Short Title Architectural Domain & Focus Audio Duration Action
01 The Ultimate Portable Traefik & Keycloak Helm Chart Unified Ingress & OIDC
Zero forks, ForwardAuth middleware, status 401 to 302 rewrite & role-gating
🇺🇸 EN 1:30 ▶️ Watch
02 How Air-Gapped Kubernetes Clusters Install Ingress Air-Gapped Architecture
Vendored Helm dependencies & internal OCI mirrors for disconnected datacenters
🇺🇸 EN 1:03 ▶️ Watch
03 How External Secrets Operator Connects Vault Zero-Trust Secret Sync
Bridging HashiCorp Vault into Kubernetes secrets without committing passwords
🇺🇸 EN 1:02 ▶️ Watch
04 Why OpenShift SCC Breaks Standard Pods OpenShift Hardening
restricted-v2 dynamic UID ranges vs hardcoded non-root UIDs (65532)
🇺🇸 EN 1:20 ▶️ Watch

For complete technical summaries, topic breakdowns, and direct studio links, see Section 18: Video Walkthroughs & Architecture References.


Table of contents

  1. Architecture
  2. How portability works (orthogonal axes)
  3. Deployment topology
  4. Supported platforms & presets
  5. Repository layout
  6. Prerequisites
  7. Configuration
  8. Step-by-step install
  9. Feature-flag reference
  10. Secrets management
  11. CNI & CSI
  12. Air-gapped / disconnected
  13. Upgrade
  14. Decommission
  15. Operations & troubleshooting
  16. GitOps with ArgoCD
  17. Contributing & license
  18. Video Walkthroughs & Architecture References (YouTube)

1. Architecture

The authentication flow is the same on every platform — only the load balancer and the pod-security profile differ.

  • Traefik OSS has no native OIDC. oauth2-proxy performs the OIDC exchange with Keycloak; Traefik only asks "is this request authenticated?" through the ForwardAuth middleware against /oauth2/auth.
  • The errors middleware with statusRewrites: "401": 302 turns the 401 into a real redirect to Keycloak. Requires Traefik ≥ v3.4. Without it you would see a blank 401 instead of the login page.
  • Role gate: oauth2Proxy.allowedRoles=traefik-dashboard:traefik-admin only lets users carrying that role through; everyone else gets 403 after login.
Diagram — authentication flow (click to expand)
flowchart TD
    B["Browser"] -->|"HTTPS :443 — TLS terminates in Traefik"| T["Traefik (websecure entrypoint)"]
    T -->|"IngressRoute /dashboard, /api<br/>middlewares: [oauth-errors, oauth-auth]"| AUTH["oauth-auth (ForwardAuth)<br/>GET /oauth2/auth"]
    AUTH --> OP["oauth2-proxy"]
    OP -->|"202 authenticated"| DASH["api@internal<br/>(dashboard loads)"]
    OP -->|"401 not authenticated"| ERR["oauth-errors<br/>rewrites 401 → 302 /oauth2/sign_in"]
    ERR -->|"redirect"| T
    T -->|"IngressRoute /oauth2/*"| OP
    OP -->|"OIDC (skip provider button)"| KC["Keycloak (realm)"]
    KC -->|"login OK + role traefik-admin → session cookie"| OP
Loading

2. How portability works (orthogonal axes)

There is one core (Traefik + oauth2-proxy + the dashboard routes, identical everywhere). Portability comes from a few independent axes you pick from; the chart renders the same core and _validate.tpl rejects the combinations that cannot work. This is why there is no per-platform fork of the chart — you compose one, you don't copy it.

Diagram — orthogonal axes compose one core (click to expand)
flowchart LR
    subgraph AXES["Pick one value per axis"]
        direction TB
        P["platform<br/>openshift · rke2 · k3s · kubeadm · generic"]
        LB["loadBalancer.backend<br/>metallb · nsx-alb · kube-vip · generic"]
        CA["caTrust.mode<br/>none · openshift-injector · configmap · insecure"]
        SEC["secret.mode<br/>external · inline"]
        REG["image.registry<br/>public · internal mirror (air-gap)"]
    end
    AXES --> CORE["traefik-keycloak umbrella chart<br/>(same core: Traefik + oauth2-proxy + routes)"]
    CORE --> VAL{"_validate.tpl<br/>rejects impossible combos"}
    VAL -->|"valid"| OUT["Rendered manifests"]
    VAL -->|"invalid"| ERRP["helm aborts with an actionable message"]
    classDef axis fill:#DDE8FF,stroke:#3B6FD4,color:#111;
    classDef gate fill:#FFF3CD,stroke:#E0A800,color:#111;
    class P,LB,CA,SEC,REG axis;
    class VAL gate;
Loading

The axes are genuinely independent — e.g. OpenShift can use MetalLB or another LB, and Tanzu can use NSX ALB or kube-vip. See §4 for the combinations that matter; the full Cartesian product is intentionally not enumerated (hundreds of rows describing the same core).

3. Deployment topology

Full picture — delivery (GitOps) + runtime, all components and the optional pieces (External Secrets/Vault, air-gap mirror, the four LB backends). Node colours map to the legend below.

Diagram — full architecture (delivery + runtime, all components) (click to expand)
flowchart TB
    subgraph EXT["⬜ Client · on-prem (may be air-gapped)"]
      direction TB
      BROWSER["Browser"]
      DNS["DNS · A record → LB IP"]
    end
    subgraph GIT["🔁 GitOps · ArgoCD (optional delivery)"]
      direction TB
      REPO[("Git repo")]
      APP["Application: traefik-keycloak<br>vendored chart + $values preset"]
      PROJ["AppProject: traefik"]
      REPO --> APP
      PROJ -. scopes .-> APP
    end
    subgraph LBG["🟦 LoadBalancer · pick one (loadBalancer.backend)"]
      direction TB
      METAL["MetalLB"]
      NSX["NSX ALB (Avi)"]
      KVIP["kube-vip"]
      CIL["Cilium LB-IPAM"]
    end
    subgraph NS["Namespace: traefik (any distribution)"]
      direction TB
      TRAEFIK["Traefik<br>web :80→443 · websecure :443 TLS"]
      subgraph RT["🟪 Traefik CRDs · routing"]
        direction TB
        IRD["IngressRoute · /dashboard · /api"]
        MERR["Middleware · oauth-errors"]
        MAUTH["Middleware · oauth-auth"]
        IRO["IngressRoute · /oauth2/*"]
      end
      API["api@internal · Dashboard"]
      O2P["oauth2-proxy · :4180"]
      subgraph ESOG["🟩 External Secrets Operator · optional"]
        direction TB
        SS["SecretStore → Vault"]
        ES["ExternalSecret"]
      end
      subgraph SEC["🩷 Secrets · in-cluster"]
        direction TB
        SECRET["oauth2-proxy-secret"]
        TLS["traefik-dashboard-tls"]
      end
      TRAEFIK --> IRD --> MERR --> MAUTH --> API
      TRAEFIK --> IRO --> O2P
      MAUTH -. forwardAuth .-> O2P
      O2P -. reads .-> SECRET
      TRAEFIK -. TLS .-> TLS
      SS --> ES
      ES -->|creates| SECRET
    end
    KC["Keycloak · OIDC IdP"]
    VAULT[("HashiCorp Vault · KV v2")]
    MIRROR["Internal registry mirror<br>images + vendored chart"]
    BROWSER --> DNS --> LBG
    LBG --> TRAEFIK
    O2P -->|OIDC| KC
    VAULT -->|"Kubernetes auth"| ES
    MIRROR -. "images + chart (no internet)" .-> TRAEFIK
    MIRROR -. image .-> O2P
    APP -. helm .-> TRAEFIK
    EXT ~~~ GIT
    classDef ext fill:#ECEFF1,stroke:#607D8B,color:#111;
    classDef idp fill:#F8D7DA,stroke:#C0392B,color:#111;
    classDef vault fill:#FFF3CD,stroke:#E0A800,color:#111;
    classDef mirror fill:#E0F2F1,stroke:#00695C,color:#111;
    classDef gitops fill:#DDE8FF,stroke:#3B6FD4,color:#111;
    classDef lb fill:#B2DFDB,stroke:#00897B,color:#111;
    classDef app fill:#FFE0B2,stroke:#EF7B4D,color:#111;
    classDef crd fill:#E8E0FF,stroke:#7C4DFF,color:#111;
    classDef eso fill:#D7F5DD,stroke:#2E9E5B,color:#111;
    classDef secret fill:#FCE1F0,stroke:#C2185B,color:#111;
    class BROWSER,DNS ext;
    class KC idp;
    class VAULT vault;
    class MIRROR mirror;
    class REPO,APP,PROJ gitops;
    class METAL,NSX,KVIP,CIL,TRAEFIK lb;
    class IRD,MERR,MAUTH,IRO crd;
    class API,O2P app;
    class SS,ES eso;
    class SECRET,TLS secret;
Loading

Legend

Group What it covers
🟦 Ingress / LB MetalLB · NSX ALB · kube-vip · Cilium · Traefik
🟪 Traefik CRDs IngressRoutes · middlewares (oauth-auth / oauth-errors)
🟧 Apps oauth2-proxy · api@internal dashboard
🟩 External Secrets Operator SecretStore · ExternalSecret (secret.mode=external-secrets)
🩷 Secrets oauth2-proxy-secret · traefik-dashboard-tls
🔁 GitOps git repo · ArgoCD Application · AppProject
🟨 Vault HashiCorp Vault · KV v2 (backend for ESO)
🟥 Keycloak OIDC IdP
🟢 Internal mirror image registry + vendored chart (air-gap)
⬜ Client / DNS browser · DNS record

4. Supported platforms & presets

Platform × LoadBalancer backend

What is typical, merely possible, or best avoided per platform:

Platform ↓ / LB → metallb nsx-alb kube-vip cilium generic
openshift ✅ typical (MetalLB Operator) ⚪ if Avi/AKO present ⚪ possible ⚪ if Cilium CNI ⚪ external/physical LB
rke2 ✅ typical ⚪ possible ⚪ possible ⚪ if Cilium CNI ⚪ possible
k3s ✅ typical ⚪ possible ⚪ possible ⚪ if Cilium CNI ⚪ built-in ServiceLB (klipper)
kubeadm / generic ✅ typical ⚪ possible ⚪ possible ⚪ if Cilium CNI ⚪ possible
Tanzu / TKG (vSphere) ⚠️ L2 often blocked by vSphere port-group security — use BGP ✅ typical (NSX ALB + AKO) ✅ typical (bare-metal/edge) ⚪ if Cilium CNI ⚪ possible

✅ typical · ⚪ possible (supported, less common) · ⚠️ caveat.

cilium fits when Cilium is already the CNI and does the LoadBalancer (LB-IPAM / L2 announcements / BGP), so no MetalLB is needed — request an IP with the lbipam.cilium.io/ips annotation on the Service.

vSphere caveat applies to any L2 mode (MetalLB L2, kube-vip ARP) regardless of platform — including OpenShift-on-vSphere: the port group must allow Forged Transmits, or the VIP is assigned but no traffic arrives. MetalLB BGP mode sidesteps it. This is separate from the cluster's own API/Ingress VIP (keepalived on IPI), which serves the cluster, not this Service.

Preset summary

Each sites/values-<platform>.yaml is a small delta over the chart defaults:

Preset platform LB backend Traefik pod UID caTrust.mode Cluster prerequisite
values-openshift openshift metallb null (SCC injects a UID) openshift-injector MetalLB Operator + pool
values-rke2 rke2 metallb 65532 none Disable bundled ingress-nginx; MetalLB
values-k3s k3s metallb 65532 none Install k3s --disable traefik; MetalLB
values-kubeadm kubeadm metallb 65532 none PodSecurity restricted; MetalLB
values-tanzu-nsx generic nsx-alb 65532 none NSX ALB (Avi) + AKO
values-tanzu-kubevip generic kube-vip 65532 none kube-vip in service mode

Why the pod UID differs

OpenShift's restricted-v2 SCC injects a UID from the namespace range, so the Traefik pod UID must be left null (pinning one is rejected). Every other distribution has no such injector, so the preset sets an explicit non-root UID (65532). The chart deletes the Traefik chart's built-in 65532 with an explicit null and the non-OpenShift presets add it back — see the note in values.yaml. Consequence: install with a preset; a bare helm install with no preset fails validation on purpose.

5. Repository layout

helm/traefik-keycloak/          Umbrella chart
  Chart.yaml                    Depends on the vendored Traefik chart
  values.yaml                   Full catalogue of feature flags (documented)
  charts/traefik-41.0.2.tgz     Vendored official Traefik chart (air-gap)
  templates/
    oauth2-proxy-deployment.yaml  oauth2-proxy (keycloak-oidc provider)
    oauth2-proxy-service.yaml
    middlewares.yaml              oauth-auth (ForwardAuth) + oauth-errors
    ingressroutes.yaml            /oauth2/* and /dashboard routes
    trusted-ca-configmap.yaml     CA trust (openshift-injector / configmap)
    oauth2-proxy-secret.yaml      rendered only when secret.mode=inline
    external-secrets.yaml         ESO SecretStore + ExternalSecret (secret.mode=external-secrets)
    networkpolicy.yaml            NetworkPolicies (networkPolicy.enabled)
    validation.yaml + _validate.tpl   fail-fast combo validation
    _helpers.tpl
  README.md                     Chart-level reference (every flag)
sites/values-<platform>.yaml    Per-platform presets (the delta)
argocd/                         GitOps: AppProject + single Application
  apps/traefik-keycloak.yaml    multi-source (vendored chart + $values preset)
  project.yaml
  README.md
docs/                           tls-secret.md, air-gapped.md,
                                external-secrets.md, network-policies.md
keycloak/keycloak-client-setup.md
metallb/ipaddresspool.example.yaml
secrets/oauth2-proxy-secret.example.yaml
scripts/validate-mermaid.mjs    CI: parse every mermaid diagram
install.sh <platform>           Imperative install (kubectl)
PORTABILITY.md                  Migration notes / what changed vs OpenShift-only

6. Prerequisites

Requirement Check
Target cluster + kubectl/oc session kubectl get nodes
Helm v3 CLI helm version
A LoadBalancer provider with an address pool MetalLB / NSX ALB / kube-vip
Keycloak reachable over HTTPS open its console
Permissions to create namespace + CRDs cluster-admin or equivalent
Manageable DNS for the dashboard host points to the LoadBalancer IP

Platform-specific prerequisites (bundled-component conflicts, etc.) are listed at the top of each sites/values-<platform>.yaml.

7. Configuration

Set the handful of real values — inline with --set, or by editing a copy of the preset:

Value What it is Example
dashboard.host Public FQDN of the dashboard traefik.apps.mycluster.com
dashboard.cookieDomain Parent domain shared by dashboard + Keycloak .apps.mycluster.com
dashboard.tlsSecretName Pre-created TLS Secret (Traefik terminates TLS) traefik-dashboard-tls
keycloak.issuerUrl OIDC issuer (Keycloak 17+: /realms/<realm>) https://keycloak…/realms/myrealm
oauth2Proxy.allowedRoles Role gate (client:role or realm-role) traefik-dashboard:traefik-admin

Out-of-band (see the linked docs):

8. Step-by-step install

Pick one path.

8.A — Imperative (Helm / kubectl)

# 1) Create the namespace + PodSecurity label, then install with your preset:
./install.sh rke2 \
  --set dashboard.host=traefik.apps.mycluster.com \
  --set dashboard.cookieDomain=.apps.mycluster.com \
  --set keycloak.issuerUrl=https://keycloak.apps.mycluster.com/realms/myrealm

# (install.sh wraps: namespace + PSA label, then `helm upgrade --install` with
#  sites/values-<platform>.yaml. The Traefik chart is vendored — no internet pull.)

# 2) Read the LoadBalancer IP and create the DNS A record:
kubectl get svc -n traefik -l app.kubernetes.io/name=traefik -o wide

Platforms: openshift, rke2, k3s, kubeadm, tanzu-nsx, tanzu-kubevip.

8.B — GitOps with ArgoCD (recommended)

See argocd/README.md. In short: pick your preset in argocd/apps/traefik-keycloak.yaml, then:

kubectl apply -n openshift-gitops -f argocd/project.yaml          # or -n argocd
kubectl apply -n openshift-gitops -f argocd/apps/traefik-keycloak.yaml

Secrets are created out-of-band first (§7) — ArgoCD does not manage them unless you use Sealed/External Secrets or secret.mode=inline.

9. Feature-flag reference

Every flag lives in helm/traefik-keycloak/values.yaml (fully commented). Summary:

Flag Values What it does
platform generic openshift rke2 k3s kubeadm Distribution profile; drives pod-security expectations. Validated.
loadBalancer.backend metallb nsx-alb kube-vip cilium generic Documents/validates the LB. Annotations go in traefik.service.annotations.
image.registry "" or host Air-gap registry prefix for the oauth2-proxy image (traefik.image.registry for Traefik).
caTrust.mode none openshift-injector configmap insecure How oauth2-proxy trusts the Keycloak TLS cert.
secret.mode external inline external-secrets Reference a pre-created Secret (default), render one from values (dev), or pull it from a backend via ESO (§10).
networkPolicy.enabled false true Render minimal NetworkPolicies for default-deny clusters (§11).
dashboard.* keycloak.* oauth2Proxy.* — Hostnames, OIDC issuer, role gate, replicas, resources.

Fail-fast validation

helm install/helm template runs the validation first and aborts on an impossible combination:

  • Enum checks on platform, loadBalancer.backend, caTrust.mode, secret.mode.
  • caTrust.mode=openshift-injector ⇒ requires platform=openshift.
  • platform=openshift ⇒ runAsUser must be null; platform≠openshift ⇒ a non-root runAsUser must be set.
  • secret.mode=inline ⇒ clientSecret and cookieSecret must be set.
  • secret.mode=external-secrets ⇒ a SecretStore ref (and, when the chart creates the store, a Vault server) must be set.
  • dashboard.host and keycloak.issuerUrl must be non-empty.

10. Secrets management

oauth2-proxy needs three values — OAUTH2_PROXY_CLIENT_ID, _CLIENT_SECRET, _COOKIE_SECRET — in a Secret named by secret.name. How that Secret comes to exist is the secret.mode axis:

secret.mode Who creates the Secret Use when
external (default) You, out-of-band (manually, or Sealed Secrets) GitOps and you seal secrets, or you apply them by hand
inline The chart, from secret.clientSecret / secret.cookieSecret Dev / demo only — keep the values out of public git
external-secrets The External Secrets Operator, pulled from a backend (e.g. HashiCorp Vault) GitOps with a real secrets backend — nothing sensitive in git

external-secrets mode renders a SecretStore (→ Vault, Kubernetes auth) and an ExternalSecret that fills oauth2-proxy-secret; the oauth2-proxy Deployment consumes it unchanged. Full setup (Vault KV, policy, role, self-signed CA) is in docs/external-secrets.md.

Diagram — secret modes and the ESO/Vault flow (click to expand)
flowchart LR
    subgraph MODES["secret.mode"]
      direction TB
      EXT["external<br/>you create it (Sealed Secrets / manual)"]
      INL["inline<br/>chart renders from values (dev)"]
      ES["external-secrets<br/>ESO pulls from a backend"]
    end
    VAULT[("HashiCorp Vault<br/>KV v2")]
    ESO["External Secrets Operator<br/>SecretStore + ExternalSecret"]
    SECRET["Secret: oauth2-proxy-secret<br/>CLIENT_ID · CLIENT_SECRET · COOKIE_SECRET"]
    O2P["oauth2-proxy"]
    EXT --> SECRET
    INL --> SECRET
    ES --> ESO
    VAULT -->|"Kubernetes auth"| ESO
    ESO -->|"creates / refreshes"| SECRET
    SECRET --> O2P
    classDef mode fill:#DDE8FF,stroke:#3B6FD4,color:#111;
    classDef vault fill:#FFF3CD,stroke:#E0A800,color:#111;
    classDef eso fill:#D7F5DD,stroke:#2E9E5B,color:#111;
    class EXT,INL,ES mode;
    class VAULT vault;
    class ESO eso;
Loading

11. CNI & CSI

Two questions come up for on-prem clusters: does the CNI (Calico, Cilium, OVN-Kubernetes, Antrea, flannel…) or the CSI (storage) choice affect this deployment? Short answer: CSI not at all; CNI only through NetworkPolicy.

CSI / storage — not applicable

The workload is stateless: no PersistentVolumeClaims, no StorageClass dependency. Traefik runs with readOnlyRootFilesystem and an emptyDir; TLS comes from a pre-created Secret (no ACME/acme.json to persist); oauth2-proxy sessions are cookie-based. So the CSI driver and storage classes are irrelevant here. (Storage only re-enters if you self-host Keycloak or Vault in-cluster — those are external dependencies, out of scope for this chart.)

CNI — the workload is agnostic, except NetworkPolicy

Pods are standard and run on any CNI. The one place the CNI matters is NetworkPolicy enforcement: on a default-deny cluster (common with Calico or Cilium in secure / air-gapped environments) the required flows would be blocked — Traefik→oauth2-proxy (ForwardAuth), oauth2-proxy→Keycloak (OIDC), and Traefik→API server (the kubernetesCRD provider watch). Set networkPolicy.enabled=true to render a minimal, working policy set. Details and tightening options in docs/network-policies.md.

Cilium can also be the load balancer (LB-IPAM) — that is the loadBalancer.backend=cilium value (§4), a different axis from NetworkPolicy.

Diagram — allowed flows with networkPolicy.enabled (click to expand)
flowchart TD
    LB["LoadBalancer"] -->|":80 / :443 (8000/8443)"| TR["Traefik"]
    TR -->|":4180 ForwardAuth"| OP["oauth2-proxy"]
    TR -->|"API-server port (CRD watch)"| API["kube-apiserver"]
    OP -->|":443 OIDC"| KC["Keycloak"]
    TR -.->|"DNS :53"| DNS["kube-dns"]
    OP -.->|"DNS :53"| DNS
    classDef inpol fill:#E8E0FF,stroke:#7C4DFF,color:#111;
    classDef ext fill:#ECEFF1,stroke:#607D8B,color:#111;
    class TR,OP inpol;
    class LB,API,KC,DNS ext;
Loading

12. Air-gapped / disconnected

Two things must resolve without internet; both are handled — details in docs/air-gapped.md:

  1. The Traefik chart is vendored in charts/traefik-41.0.2.tgz, so neither helm install nor an ArgoCD sync pulls from traefik.github.io.
  2. Image registries are parameterised. Point them at your internal mirror:
    --set image.registry=harbor.internal.example.com \
    --set traefik.image.registry=harbor.internal.example.com
    Mirror oauth2-proxy and traefik into that registry beforehand.

13. Upgrade

# Bump the vendored Traefik chart:
helm pull traefik/traefik --version <X.Y.Z> -d helm/traefik-keycloak/charts/
# update dependencies.version in helm/traefik-keycloak/Chart.yaml, remove the old .tgz

# Re-render/lint before shipping:
helm lint ./helm/traefik-keycloak -f sites/values-<platform>.yaml
helm template t ./helm/traefik-keycloak -f sites/values-<platform>.yaml >/dev/null

# Imperative apply:
helm upgrade traefik ./helm/traefik-keycloak -n traefik -f sites/values-<platform>.yaml

Keep Traefik ≥ v3.4 (the errors middleware statusRewrites requirement). Under GitOps, commit the change and let ArgoCD reconcile — do not helm/kubectl by hand.

14. Decommission

# Imperative:
helm uninstall traefik -n traefik
kubectl delete namespace traefik

# GitOps: delete the Application (finalizer cascades to its resources):
kubectl delete -n openshift-gitops application/traefik-keycloak

External cleanup: DNS record, Keycloak client + traefik-admin role, MetalLB pool (if dedicated), and the Traefik CRDs if you want them gone (kubectl get crd | grep traefik.io).

15. Operations & troubleshooting

kubectl get pods,svc -n traefik
kubectl logs -n traefik deploy/oauth2-proxy -f      # OIDC, issuer, CA, roles
kubectl logs -n traefik deploy/traefik | grep -i error
Symptom Likely cause Fix
Blank 401 (no redirect) Traefik < v3.4, or wrong middleware order Traefik ≥ v3.4; order [oauth-errors, oauth-auth]
403 after login User lacks the traefik-admin role Assign the role in Keycloak
Redirect loop dashboard.cookieDomain doesn't cover the host Set it to the shared parent domain
Invalid redirect_uri Keycloak redirect URI mismatch Must be https://<dashboard.host>/oauth2/callback
x509: unknown authority oauth2-proxy doesn't trust the Keycloak cert Set caTrust.mode (openshift-injector / configmap), or insecure for tests
invalid issuer issuer with/without /auth wrong Check <issuer>/.well-known/openid-configuration
Service has no EXTERNAL-IP No LB provider/pool, or blocked L2 on vSphere Install MetalLB/NSX ALB/kube-vip; allow Forged Transmits, or use BGP
Traefik pod CreateContainerConfigError (OpenShift) A pinned UID conflicts with the SCC Use platform=openshift (leaves UID unset) — the chart validates this
helm aborts: "requires an explicit non-root runAsUser" Installed with no preset Install with a sites/ preset
Login times out / ForwardAuth 502 after enabling policies networkPolicy.enabled=true but the cluster/CNI needs different ports (e.g. API server on 443) Set networkPolicy.apiServerPort; check keycloakEgressCIDR; see docs/network-policies.md
oauth2-proxy-secret never appears (ESO) Wrong Vault role/path, or ESO can't auth Check the ExternalSecret status and ESO logs; verify the Vault role binds the SA + namespace (docs/external-secrets.md)

16. GitOps with ArgoCD

Diagram — GitOps reconciliation (click to expand)
flowchart LR
    DEV["Edit sites/values-*.yaml<br/>commit + push"] --> REPO[("Git repo")]
    REPO --> APP["ArgoCD Application<br/>traefik-keycloak (multi-source)"]
    APP -->|"$values preset + vendored chart"| RENDER["helm template<br/>(chart 41.0.2)"]
    RENDER --> SYNC["ArgoCD sync<br/>auto-sync · self-heal · prune"]
    SYNC --> CLUSTER["namespace: traefik"]
    PROJ["AppProject: traefik<br/>bounds repos / dest / resources"] -. scopes .-> APP
    classDef git fill:#DDE8FF,stroke:#3B6FD4,color:#111;
    classDef argo fill:#FFE0B2,stroke:#EF7B4D,color:#111;
    class REPO,DEV git;
    class APP,SYNC,PROJ argo;
Loading

Design points (full detail in argocd/README.md):

  • Single Application, multi-source: the vendored chart is fed the platform preset via the $values ref (a -f path outside the chart dir is rejected by ArgoCD, so the ref pattern is used).
  • Namespace created and PSA-labelled via managedNamespaceMetadata.
  • ServerSideApply=true avoids the "metadata.annotations: Too long" error on Traefik's large CRDs.
  • Dedicated AppProject bounds repos (just this one — the chart is vendored), destination (traefik namespace) and cluster-scoped resources.
  • Secrets stay out of git (Sealed/External Secrets), or use secret.mode=inline.

17. Contributing & license

CI (.github/workflows/ci.yml) runs helm lint + helm template/kubeconform for every preset, the validation-rule checks, yamllint, shellcheck, and parses every mermaid diagram (scripts/validate-mermaid.mjs). Please keep it green.

Licensed under the MIT License.

Status: untested on a live cluster — all validation is helm lint / helm template / kubeconform / mermaid parsing, not runtime.


18. Video Walkthroughs & Architecture References (YouTube)

Architectural deep dives, video walkthroughs, and technical shorts for traefik-keycloak-portable, multi-distribution Kubernetes deployments, Traefik Ingress, and Keycloak SSO are hosted on the Nubenetes YouTube Channel (@nubenetes).

📂 Full-Length Technical Deep Dives (Architecture Masterclasses)
1. Portable Traefik & Keycloak Helm Chart: Multi-Distro Kubernetes Architecture
  • 🔗 Direct Link: https://www.youtube.com/watch?v=3OQhS25KbIk
  • 🌐 Origin Language: English 🇺🇸 (Subtitles in 20+ languages)
  • ⏱️ Duration: 9:26
  • 🏷️ Engineering Domain: Multi-Distribution Architecture, Orthogonal Feature Flags & Helm Design
  • 📝 Technical Overview: Detailed exploration of a single, highly portable umbrella Helm chart deploying Traefik v3 and a Keycloak-protected dashboard across OpenShift, RKE2, k3s, kubeadm, and VMware Tanzu. Explains why a single core with orthogonal feature flags outperforms per-distribution forks, how _validate.tpl catches invalid configurations during helm template rendering, and how vendored subcharts enable true air-gapped deployments.
  • 🛠️ Direct Links: Watch on YouTube | Edit in YouTube Studio
2. Multi-Distro Kubernetes Portability: OpenShift, RKE2, K3s, Kubeadm & Tanzu
  • 🔗 Direct Link: https://www.youtube.com/watch?v=8w0-SJw0jLo
  • 🌐 Origin Language: English 🇺🇸 (Subtitles in 20+ languages)
  • ⏱️ Duration: 9:26
  • 🏷️ Engineering Domain: Platform Engineering, Distribution Quirks & Security Hardening
  • 📝 Technical Overview: Architectural analysis of multi-distribution Kubernetes portability. Explores how platform-specific security models—specifically Red Hat OpenShift's restricted-v2 SecurityContextConstraints rejecting hardcoded UIDs like 65532—differ from standard Kubernetes PodSecurityStandards. Demonstrates how the portable chart adapts UID allocation dynamically, configures CNI NetworkPolicies for Calico and Cilium, and eliminates environment drift.
  • 🛠️ Direct Links: Watch on YouTube | Edit in YouTube Studio
3. Traefik & Keycloak OIDC Authentication: ForwardAuth & Role RBAC Masterclass
  • 🔗 Direct Link: https://www.youtube.com/watch?v=X4GdtkJrbZo
  • 🌐 Origin Language: English 🇺🇸 (Subtitles in 20+ languages)
  • ⏱️ Duration: 7:04
  • 🏷️ Engineering Domain: Zero-Trust Ingress, Keycloak OIDC, oauth2-proxy & ForwardAuth Middleware
  • 📝 Technical Overview: Comprehensive guide to securing the internal Traefik dashboard with OpenID Connect when Traefik OSS lacks native OIDC. Details the exact mechanics of oauth2-proxy, Traefik's ForwardAuth middleware (/oauth2/auth), the Traefik v3.4+ errors middleware status rewrite (statusRewrites: "401": 302), and enforcing strict role-based access control with traefik-dashboard:traefik-admin.
  • 🛠️ Direct Links: Watch on YouTube | Edit in YouTube Studio
4. Portable Kubernetes Networking: MetalLB, NSX ALB, Kube-Vip & Cilium LB-IPAM
  • 🔗 Direct Link: https://www.youtube.com/watch?v=Hb4K81jiGrA
  • 🌐 Origin Language: English 🇺🇸 (Subtitles in 20+ languages)
  • ⏱️ Duration: 7:52
  • 🏷️ Engineering Domain: On-Premises Load Balancing, Ingress Networking & CNI Isolation
  • 📝 Technical Overview: Deep dive into decoupling Kubernetes ingress networking from specific cloud providers. Compares on-premises LoadBalancer backends supported by the chart: MetalLB (Layer 2 ARP and BGP pools), VMware NSX Advanced Load Balancer (Avi Vantage with AKO), kube-vip virtual IP failover, and modern eBPF-native Cilium LB-IPAM. Also reviews ingress NetworkPolicy hardening for zero-trust datacenters.
  • 🛠️ Direct Links: Watch on YouTube | Edit in YouTube Studio
5. End-to-End Portable Traefik & Keycloak: Air-Gapped GitOps Masterclass
  • 🔗 Direct Link: https://www.youtube.com/watch?v=1AfsoppwT_w
  • 🌐 Origin Language: English 🇺🇸 (Subtitles in 20+ languages)
  • ⏱️ Duration: 9:46
  • 🏷️ Engineering Domain: End-to-End Production Lifecycle, Air-Gapped GitOps & Secrets Management
  • 📝 Technical Overview: The complete production walkthrough covering Day 0 setup through Day 2 operations. Explains disconnected air-gapped image mirroring and chart vendoring, synchronizing secrets and private CA certificates from HashiCorp Vault via External Secrets Operator, declarative multi-source GitOps delivery with ArgoCD, and zero-downtime Helm upgrades.
  • 🛠️ Direct Links: Watch on YouTube | Edit in YouTube Studio
📂 Technical Shorts Matrix & Architecture Breakdowns

⚡ Video Shorts Matrix

# Short Title Architectural Domain & Focus Audio Duration Action
01 The Ultimate Portable Traefik & Keycloak Helm Chart Unified Ingress & OIDC
Zero forks, ForwardAuth middleware, status 401 to 302 rewrite & role-gating
🇺🇸 EN 1:30 ▶️ Watch
02 How Air-Gapped Kubernetes Clusters Install Ingress Air-Gapped Architecture
Vendored Helm dependencies & internal OCI mirrors for disconnected datacenters
🇺🇸 EN 1:03 ▶️ Watch
03 How External Secrets Operator Connects Vault Zero-Trust Secret Sync
Bridging HashiCorp Vault into Kubernetes secrets without committing passwords
🇺🇸 EN 1:02 ▶️ Watch
04 Why OpenShift SCC Breaks Standard Pods OpenShift Hardening
restricted-v2 dynamic UID ranges vs hardcoded non-root UIDs (65532)
🇺🇸 EN 1:20 ▶️ Watch

1. The Ultimate Portable Traefik & Keycloak Helm Chart Explained
  • 🔗 Direct Link: https://www.youtube.com/shorts/murHHpH8WHE
  • 🌐 Origin Language: English 🇺🇸 (Subtitles in 20+ languages)
  • ⏱️ Duration: 1:30
  • 🏷️ Engineering Domain: Unified Ingress, OIDC Authentication & Helm Architecture
  • 📝 Technical Overview: Why managing separate Helm chart forks per distribution is an anti-pattern. Demonstrates how a single portable chart handles Traefik ingress and Keycloak dashboard protection across any Kubernetes cluster, using oauth2-proxy, ForwardAuth, status 401 to 302 redirect rewriting, and role-based gating.
  • 🛠️ Direct Links: Watch Short | Edit in YouTube Studio
2. How Air-Gapped Kubernetes Clusters Install Ingress & Helm Charts
  • 🔗 Direct Link: https://www.youtube.com/shorts/LGdVhOyaVaI
  • 🌐 Origin Language: English 🇺🇸 (Subtitles in 20+ languages)
  • ⏱️ Duration: 1:03
  • 🏷️ Engineering Domain: Air-Gapped Kubernetes, Disconnected Mirroring & Offline Charts
  • 📝 Technical Overview: How to deploy ingress controllers and complex microservices in strictly isolated datacenters with zero internet egress. Explains storing vendored Helm charts in Git and redirecting image repositories to internal OCI registries.
  • 🛠️ Direct Links: Watch Short | Edit in YouTube Studio
3. How External Secrets Operator Connects HashiCorp Vault to Kubernetes
  • 🔗 Direct Link: https://www.youtube.com/shorts/BUwUb6vpbqs
  • 🌐 Origin Language: English 🇺🇸 (Subtitles in 20+ languages)
  • ⏱️ Duration: 1:02
  • 🏷️ Engineering Domain: Zero-Trust Secret Management, External Secrets Operator & Vault
  • 📝 Technical Overview: Eliminating plaintext secrets from Git repositories. Shows how External Secrets Operator continuously polls HashiCorp Vault to synchronize client secrets, cookie encryption keys, and internal TLS certs into standard Kubernetes Secrets.
  • 🛠️ Direct Links: Watch Short | Edit in YouTube Studio
4. Why OpenShift Security Context Constraints Break Standard Kubernetes Pods
  • 🔗 Direct Link: https://www.youtube.com/shorts/Cv5jakkIseE
  • 🌐 Origin Language: English 🇺🇸 (Subtitles in 20+ languages)
  • ⏱️ Duration: 1:20
  • 🏷️ Engineering Domain: OpenShift Security, restricted-v2 SCC & Dynamic UID Ranges
  • 📝 Technical Overview: Why pods that run fine on k3s or kubeadm get blocked on Red Hat OpenShift. Explains how OpenShift's restricted-v2 SCC allocates dynamic UIDs and why hardcoding runAsUser: 65532 causes admission failures, plus how the portable chart adapts seamlessly.
  • 🛠️ Direct Links: Watch Short | Edit in YouTube Studio

About

Portable Helm chart: Traefik v3.7.6 + Keycloak-protected dashboard (oauth2-proxy/ForwardAuth) for OpenShift, RKE2, k3s, kubeadm & Tanzu. Feature-flag driven — MetalLB/NSX ALB/kube-vip/Cilium LB, External Secrets+Vault, optional NetworkPolicies, air-gap. Fail-fast validation. AI-generated, not yet tested on a live cluster.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages