AuthBridge provides secure, transparent token management for Kubernetes workloads. It combines automatic client registration with token exchange capabilities, enabling zero-trust authentication flows with SPIFFE/SPIRE integration.
📘 Looking to run the demo? See the Single-Target Demo or Multi-Target Demo for step-by-step instructions.
The cmd/authbridge/ directory contains a unified binary that supports three deployment modes in a single codebase. Two container images are published:
| Image | Size | Contents |
|---|---|---|
authbridge-envoy |
140 MB | Go binary + Envoy (UBI9-micro base) |
authbridge-light |
29 MB | Go binary only (distroless base) |
| Mode | Image | Use Case | How It Works |
|---|---|---|---|
envoy-sidecar (default) |
authbridge-envoy |
Transparent interception via iptables | Envoy intercepts all traffic, delegates auth to authbridge via ext_proc gRPC |
proxy-sidecar |
authbridge-light |
Lightweight proxy via HTTP_PROXY env | Agent routes outbound traffic through forward proxy; reverse proxy validates inbound JWTs |
waypoint |
authbridge-light |
Shared proxy in Istio ambient mesh | Standalone deployment (not injected as sidecar) |
The operator selects the mode via the kagenti.io/authbridge-mode annotation on the workload. Default is envoy-sidecar. See kagenti-operator PR #295 for the implementation.
| Name | Status |
|---|---|
authbridge-envoy |
Canonical name for the Envoy variant |
authbridge-light |
Lightweight variant (no Envoy) |
authbridge-unified |
Deprecated alias for authbridge-envoy |
The shared auth library at authlib/ contains the building blocks (JWT validation, token exchange, caching, routing) with no protocol dependencies. See authlib/README.md for package reference.
The following describes the operator-injected sidecar deployment. The authbridge-envoy image replaces the legacy envoy-with-processor in envoy-sidecar mode. The authbridge-light image is used for proxy-sidecar mode.
AuthBridge solves the challenge of secure service-to-service authentication in Kubernetes:
-
Automatic Identity - Workloads automatically obtain their identity from SPIFFE/SPIRE and register as Keycloak clients using their SPIFFE ID (e.g.,
spiffe://example.com/ns/default/sa/myapp) -
Token-Based Authorization - Callers obtain JWT tokens from Keycloak with the workload's identity as the audience, authorizing them to invoke specific services
-
Transparent Token Exchange - A sidecar intercepts outgoing requests, validates incoming tokens, and exchanges them for tokens with the appropriate target audience—all without application code changes
-
Target Service Validation - Target services validate the exchanged token, ensuring it has the correct audience before authorizing requests
Incoming request (with JWT)
│
▼
┌───────────────────────────────────────────────────────────────────────┐
│ WORKLOAD POD │
│ (with AuthBridge sidecars) │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Init Container: proxy-init (iptables intercepts pod traffic, │ │
│ │ excluding Keycloak port) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ AuthProxy Sidecar │ │
│ │ (Envoy + Ext Proc) │ │
│ │ │ │
│ │ INBOUND: Validates JWT (signature + issuer via JWKS) │ │
│ │ Returns 401 Unauthorized if invalid │ │
│ │ OUTBOUND: Exchanges token → target-service audience │ │
│ │ (using Workload's credentials) │ │
│ └──────────────────────┬──────────────────────────────────────────┘ │
│ ▲ outbound │ inbound │
│ │ request │ (validated) │
│ │ ▼ │
│ ┌─────────┴────────────────────┐ ┌───────────────────────────────┐ │
│ │ Your App │ │ SPIFFE Helper │ │
│ │ │ │ (provides SPIFFE creds) │ │
│ └──────────────────────────────┘ └───────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ client-registration (registers Workload with Keycloak) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────────┘
│
│ Exchanged token (aud: target-service)
▼
┌─────────────────────┐
│ TARGET SERVICE POD │
│ │
│ Validates token │
│ with audience │
│ "target-service" │
└─────────────────────┘
📊 Mermaid Architecture Diagram (click to expand)
flowchart TB
subgraph WorkloadPod["WORKLOAD POD (with AuthBridge sidecars)"]
subgraph Init["Init Container"]
ProxyInit["proxy-init<br/>(iptables setup)"]
end
subgraph Containers["Containers"]
App["Your Application"]
SpiffeHelper["SPIFFE Helper<br/>(provides SVID)"]
ClientReg["client-registration<br/>(registers with Keycloak)"]
subgraph Sidecar["AuthProxy Sidecar"]
AuthProxy["auth-proxy"]
Envoy["envoy-proxy"]
ExtProc["ext-proc"]
end
end
end
subgraph TargetPod["TARGET SERVICE POD"]
Target["Target Service<br/>(validates tokens)"]
end
subgraph External["External Services"]
SPIRE["SPIRE Agent"]
Keycloak["Keycloak"]
end
Caller["Caller<br/>(external)"]
SPIRE --> SpiffeHelper
SpiffeHelper --> ClientReg
ClientReg --> Keycloak
Caller -->|"1. Get token"| Keycloak
Caller -->|"2. Pass token"| Envoy
Envoy -->|"3. Validate JWT (JWKS)"| ExtProc
ExtProc -->|"3a. Validation result"| Envoy
Envoy -->|"4. 401 if invalid"| Caller
Envoy -->|"5. Forward if valid"| App
App -->|"6. Request + Token"| Envoy
Envoy -->|"7. Token Exchange"| ExtProc
ExtProc -->|"8. Exchange with Keycloak"| Keycloak
Envoy -->|"9. Request + Exchanged Token"| Target
Target -->|"10. Response"| App
App -->|"11. Response"| Caller
style WorkloadPod fill:#e1f5fe
style TargetPod fill:#e8f5e9
style Sidecar fill:#fff3e0
style External fill:#fce4ec
style Caller fill:#fff9c4
| Component | Type | Purpose |
|---|---|---|
proxy-init |
init | Sets up iptables to intercept inbound and outbound traffic (excludes Keycloak port) |
client-registration |
container | Registers workload with Keycloak using SPIFFE ID, saves credentials to /shared/ |
spiffe-helper |
container | Provides SPIFFE credentials (SVID) |
Your App |
container | Your application; the demo uses a pass-through proxy as an example |
AuthProxy Sidecar |
container | Composed of Envoy + external processing (Ext Proc) components (shown as separate nodes in diagrams): validates inbound JWTs (signature + issuer via JWKS, returns 401 if invalid) and exchanges outbound tokens (HTTP: token exchange via Ext Proc; HTTPS: TLS passthrough) |
Any downstream service that validates incoming tokens have the expected audience.
Initialization (Workload Pod Startup):
SPIRE Agent Workload Pod Keycloak
│ │ │
│ 0. SVID │ │
│───────────────────────►│ SPIFFE Helper │
│ (SPIFFE ID) │ │
│ │ │
│ │ 1. Register client │
│ │ (client_id = SPIFFE ID) │
│ │───────────────────────────────►│
│ │ Client Registration │
│ │ │
│ │◄───────────────────────────────│
│ │ client_secret │
│ │ (saved to /shared/) │
Runtime Flow:
Caller Workload Pod Keycloak Target Service
│ │ │ │
│ 2. Get token │ │ │
│ (aud: Workload's SPIFFE ID) │ │
│─────────────────────────────────────────────►│ │
│◄─────────────────────────────────────────────│ │
│ Token (aud: Workload) │ │
│ │ │ │
│ 3. Pass token │ │ │
│ to Workload │ │ │
│────────────────────►│ │ │
│ │──────────┐ │ │
│ │ Envoy intercepts │ │
│ │ inbound request │ │
│ │ │ │ │
│ │ Ext Proc validates │ │
│ │ JWT (signature + │ │
│ │ issuer via JWKS) │ │
│ │ │ │ │
│ │ 401 if invalid ──────►│ (rejected) │
│ │ │ │ │
│ │ 4. Forward to App │ │
│ │ if valid │ │
│ │◄─────────┘ │ │
│ │ │ │
│ │ 5. Workload calls │ │
│ │ Target Service with │ │
│ │ Caller's token │ │
│ │──────────┐ │ │
│ │ │ │ │
│ │ Envoy intercepts │ │
│ │ outbound request │ │
│ │ │ │ │
│ │ 6. Token Exchange │ │
│ │ (using Workload creds)│ │
│ │───────────────────────►│ │
│ │◄───────────────────────│ │
│ │ New token (aud: target-service) │
│ │ │ │ │
│ │ 7. Forward request │ │
│ │ with exchanged token │ │
│ │───────────────────────────────────────►│
│ │ │ │
│ │◄───────────────────────────────────────│
│ │ "authorized" │ │
│◄────────────────────│ │ │
│ Response │ │ │
| Step | Component | Verification |
|---|---|---|
| 0 | SPIFFE Helper | SVID obtained from SPIRE Agent |
| 1 | Client Registration | Workload registered with Keycloak (client_id = SPIFFE ID) |
| 2 | Caller | Token obtained with aud: Workload's SPIFFE ID |
| 3 | Envoy + Ext Proc (inbound) | Inbound JWT validated: signature verified via JWKS, issuer checked, optional audience check. Returns 401 if invalid. |
| 4 | Workload | Validated request forwarded to application |
| 5 | Envoy + Ext Proc (outbound) | Outbound request intercepted; token exchanged using Workload's credentials → aud: target-service |
| 6 | Target Service | Token validated (aud: target-service), returns "authorized" |
📊 Mermaid Diagram (click to expand)
sequenceDiagram
autonumber
participant SPIRE as SPIRE Agent
participant Helper as SPIFFE Helper
participant Reg as Client Registration
participant Caller as Caller
participant App as Workload
participant Envoy as AuthProxy (Envoy + Ext Proc)
participant KC as Keycloak
participant Target as Target Service
Note over Helper,SPIRE: Workload Pod Initialization
SPIRE->>Helper: SVID (SPIFFE credentials)
Helper->>Reg: JWT with SPIFFE ID
Reg->>KC: Register client (client_id = SPIFFE ID)
KC-->>Reg: Client credentials (saved to /shared/)
Note over Caller,Target: Runtime Flow
Caller->>KC: Get token (aud: Workload's SPIFFE ID)
KC-->>Caller: Token with workload-aud scope
Note over Caller,Envoy: Inbound Path (JWT Validation)
Caller->>Envoy: Request with Bearer token
Note over Envoy: Ext Proc validates JWT:<br/>signature (JWKS), issuer,<br/>optional audience check
alt Invalid token
Envoy-->>Caller: 401 Unauthorized
end
Envoy->>App: Forward validated request
Note over App,Envoy: Outbound Path (Token Exchange)
App->>Envoy: Call Target Service with Caller's token
Note over Envoy: Ext Proc intercepts outbound<br/>Uses Workload's credentials
Envoy->>KC: Token Exchange (Workload's creds)
KC-->>Envoy: New Token (aud: target-service)
Envoy->>Target: Request + Exchanged Token
Target->>Target: Validate token (aud: target-service)
Target-->>App: "authorized"
App-->>Caller: Response
| Step | From → To | Action |
|---|---|---|
| Initialization Phase | ||
| 1 | SPIRE → SPIFFE Helper | Issue SVID (SPIFFE credentials) |
| 2 | SPIFFE Helper → Client Registration | Pass JWT with SPIFFE ID |
| 3 | Client Registration → Keycloak | Register client (client_id = SPIFFE ID) |
| 4 | Keycloak → Client Registration | Return client credentials (saved to /shared/) |
| Runtime Phase — Inbound (JWT Validation) | ||
| 5 | Caller → Keycloak | Request token (aud: Workload's SPIFFE ID) |
| 6 | Keycloak → Caller | Return token with workload-aud scope |
| 7 | Caller → Envoy (inbound) | Request intercepted by iptables, routed to Envoy inbound listener |
| 8 | Envoy → Ext Proc | Validate JWT: signature (JWKS), issuer, optional audience. Returns 401 if invalid. |
| 9 | Envoy → Workload | Forward validated request to application |
| Runtime Phase — Outbound (Token Exchange) | ||
| 10 | Workload → Envoy (outbound) | Outbound request intercepted by iptables, routed to Envoy outbound listener |
| 11 | Envoy → Ext Proc → Keycloak | Token Exchange (using Workload's credentials) |
| 12 | Keycloak → Envoy | Return new token (aud: target-service) |
| 13 | Envoy → Target Service | Forward request with exchanged token |
| 14 | Target Service | Validate token (aud: target-service) |
| 15 | Target Service → Workload | Return "authorized" |
| 16 | Workload → Caller | Return response |
- No Static Secrets - Credentials are dynamically generated during registration
- Short-Lived Tokens - JWT tokens expire and must be refreshed
- Inbound JWT Validation - Incoming requests are validated at the sidecar (signature via JWKS, issuer, optional audience) before reaching the application
- Self-Audience Scoping - Tokens include the Workload's own identity as audience, enabling token exchange
- Same Identity for Exchange - AuthProxy uses the Workload's credentials (same SPIFFE ID), matching the token's audience
- Transparent to Application - Both inbound validation and outbound token exchange are handled by the sidecar; applications don't need to implement either
- Configurable Targets - Route-based configuration maps destination hosts to target audiences
- Kubernetes cluster (Kind recommended for local development)
- SPIRE installed and running (server + agent) - for SPIFFE version
- Keycloak deployed
- Docker/Podman for building images
The easiest way to get all prerequisites is to use the Kagenti Ansible installer.
- Webhook Demo - Shows how the kagenti-operator webhook automatically injects AuthBridge sidecars into your deployments (recommended starting point)
- GitHub Issue Agent Demo - End-to-end demo with the real GitHub Issue Agent and GitHub MCP Tool, showing transparent token exchange via AuthBridge
- Manual deployment — deploy everything via
kubectland YAML manifests - UI deployment — import agent and tool via the Kagenti dashboard
- Manual deployment — deploy everything via
- Single-Target Demo - Basic token exchange to one target service (manual deployment)
- Multi-Target Demo - Route-based token exchange to multiple targets
All demos cover configuring Keycloak, deploying, and testing.
AuthBridge supports per-host token exchange configuration via routes.yaml:
# Exchange tokens for target-alpha audience when calling this host
- host: "target-alpha-service.authbridge.svc.cluster.local"
target_audience: "target-alpha"
token_scopes: "openid target-alpha-aud"
# Glob patterns supported
- host: "*.internal.svc.cluster.local"
passthrough: true # Skip token exchangeUse keycloak_sync.py to reconcile routes.yaml with Keycloak configuration:
python keycloak_sync.py --config routes.yaml --agent-client "spiffe://..." --yesThis creates target clients, audience scopes, and assigns scopes to the agent.
- Unified AuthBridge Binary - Single binary, three modes (recommended)
- authlib - Shared auth building blocks (Go library)
- AuthProxy - Proxy-init, combined sidecar, demo app, and quickstart
- Client Registration - Automatic Keycloak client registration with SPIFFE