rossoctl / rossoctl/operator

client-registration uses external KEYCLOAK_URL for server-to-server admin calls → unreachable in-cluster on Kind (client-credentials Secret never created)

Open
#484 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

stale
Dominant language
Go
Stars
18
Forks
50
Avg merge
3d 3h
Merged PRs (30d)
8

Description

Summary

The client-registration controller uses the external KEYCLOAK_URL (the workload issuer host) for its server-to-server Keycloak Admin API calls. On Kind that host (keycloak.localtest.me) resolves to 127.0.0.1 inside a pod, so the admin-token request fails, no client is registered, and the kagenti-keycloak-client-credentials-<hash> Secret is never created — leaving the injected agent pod stuck FailedMount/Init:0/1, never Ready.

Code trace (internal/controller/clientregistration_controller.go)

  • readAuthbridgeConfigMapKeycloakURL = cm.Data["KEYCLOAK_URL"] (~L434) — this is the external issuer host.
  • reconcileOne builds the admin client with that same value (~L261):
    kc := keycloak.Admin{BaseURL: ab.KeycloakURL, HTTPClient: keycloak.DefaultHTTPClient()}
    // → admin token (password grant) + RegisterOrFetchClientWithToken
    
  • The same KEYCLOAK_URL is also consumed by the webhook injector (internal/webhook/injector/namespace_config.go:73, container_builder.go:319) for the workload's JWT validation, where the external issuer is correct.

So one config key serves two incompatible purposes: server-to-server admin (needs in-cluster reachability) vs workload token validation (needs the external issuer).

Evidence

  • Live dev cluster (Kind): operator log:
    Keycloak admin token failed ... Post "https://keycloak.localtest.me/realms/master/protocol/openid-connect/token":
    dial tcp 127.0.0.1:443: connect: connection refused
    
    kubectl get secrets -n tx-e2e | grep -c kagenti-keycloak-client-credentials0; injected pod Init:0/1, FailedMount: secret "kagenti-keycloak-client-credentials-..." not found.
  • CI (release-validation Kind): the token-exchange wait logged Waiting for credentials (0/2) for the full loop; pod never Ready. (Confirm from the operator pod logs in the failure artifact.)

Why it looks "flaky"

It's platform-dependent, not random: on Kind, keycloak.localtest.me127.0.0.1 (unreachable in-cluster) → always fails; on OCP, the external route may be reachable from inside the cluster → happens to work. Matches the e2e-kind.yaml "client registration ... flaky/incomplete on Kind" note.

Proposed fix — decouple the admin endpoint from the issuer URL

The admin/registration calls must use an in-cluster-reachable URL, distinct from the external issuer KEYCLOAK_URL used for workload validation:

  1. Add KEYCLOAK_INTERNAL_URL to authbridgeConfig + readAuthbridgeConfigMap.
  2. In reconcileOne, choose the admin base URL:
    adminBase := ab.KeycloakAdminURL              // KEYCLOAK_INTERNAL_URL
    if adminBase == "" { adminBase = deriveInClusterURL(kcNamespace) }  // http://keycloak-service.<kc-ns>.svc:8080
    if adminBase == "" { adminBase = ab.KeycloakURL }                   // last resort (OCP where route is in-cluster reachable)
    kc := keycloak.Admin{BaseURL: adminBase, HTTPClient: keycloak.DefaultHTTPClient()}
    
    The operator already reads admin creds from the Keycloak namespace (resolveKeycloakAdminCredentials), so deriving the in-cluster service URL needs no new lookup.
  3. Populate KEYCLOAK_INTERNAL_URL in authbridge-config from the platform install + the token-exchange setup (kagenti-system already carries http://keycloak-service.keycloak.svc:8080; the admin path just doesn't use it). Keep KEYCLOAK_URL = external issuer for the injector.

Secondary: surface a clear condition/event on the AgentRuntime when admin-token/registration fails (e.g. ClientRegistrationFailed: keycloak unreachable at <url>) — this was invisible until pod events + operator logs were read manually.

Impact / linkage

Blocks the two exec-based token-exchange E2E tests in kagenti/kagenti#2129 (they stay xfail until the Secret is reliably created). Related: this repo #417 (Keycloak client contract), #433 (per-namespace KEYCLOAK_REALM). Prior client-registration fix: kagenti/kagenti#2098 (namespaces:patch RBAC → client_not_found) — a different failure in the same path.

Assisted-By: Claude Code

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in internal/controller/clientregistration_controller.go at readAuthbridgeConfigMap and reconcileOne, then inspect internal/webhook/injector/namespace_config.go and container_builder.go to preserve the external issuer path. Check e2e-kind.yaml and the release-validation failure artifacts while tracing the token-exchange setup. Done means admin registration reaches Keycloak in Kind, the client-credentials Secret is created, and the injected pod becomes Ready.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, kubernetes
Domain
devops, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.