feat(webhook): operator-managed Keycloak client credentials#262
feat(webhook): operator-managed Keycloak client credentials#262akram wants to merge 4 commits intokagenti:mainfrom
Conversation
c510134 to
019144b
Compare
cwiklik
left a comment
There was a problem hiding this comment.
Review Summary
Clean webhook-side implementation of the operator-managed Keycloak client credentials contract. The code correctly mounts operator-provisioned Secrets into shared-data containers, handles reinvocation for late annotations, and logs credential delivery paths. The annotation constant matches the operator PR (kagenti/kagenti-operator#247) exactly. Idempotency is well-handled throughout.
Must-fix: Made-with: Cursor trailer in 2 of 3 commits — should be Assisted-By: per kagenti conventions. Same issue flagged on the paired operator PR.
Areas reviewed: Go, Tests
Commits: 3 commits, all signed-off: yes
CI status: DCO passing
Cross-repo consistency: Annotation kagenti.io/keycloak-client-credentials-secret-name matches operator PR #247 ✓
kagenti-webhook/internal/webhook/injector/keycloak_client_credentials.go
Show resolved
Hide resolved
kagenti-webhook/internal/webhook/v1alpha1/authbridge_webhook.go
Outdated
Show resolved
Hide resolved
kagenti-webhook/internal/webhook/injector/keycloak_client_credentials.go
Show resolved
Hide resolved
…gistration When workloads opt out of webhook-injected client-registration, the kagenti-operator reconciler registers OAuth clients and annotates the pod template with the secret name. The mutating webhook now skips injecting the client-registration container in that case while still injecting AuthBridge sidecars, and wires the operator-provided secret for mounts. Signed-off-by: Akram <akram.benaissi@gmail.com>
- Replace operator_clientreg with keycloak_client_credentials (annotation, volume naming) - Add keycloak-client-registration logger; align deliveryPaths labels with Keycloak terminology Assisted-By: Cursor Signed-off-by: Akram <akram.benaissi@gmail.com>
Use AnnotationKeycloakClientSecretName, NeedsKeycloakClientCredentialsVolumePatch, ApplyKeycloakClientCredentialsSecretVolumes, and updated log field names. Assisted-By: Cursor Signed-off-by: Akram <akram.benaissi@gmail.com>
Signed-off-by: Akram <akram.benaissi@gmail.com>
019144b to
8a25d04
Compare
| // keycloakClientCredentialsVolumeName is the in-pod Volume name for the Secret that holds Keycloak | ||
| // client credentials. It matches the Secret name (kagenti-keycloak-client-credentials-<uniq-id>) so the | ||
| // volume is unique and stable for that Secret. | ||
| func keycloakClientCredentialsVolumeName(secretName string) string { | ||
| return secretName | ||
| } |
There was a problem hiding this comment.
What is the purpose of this function?
Alan-Cha
left a comment
There was a problem hiding this comment.
This PR successfully implements the webhook-side coordination for operator-managed Keycloak client registration. The code quality is excellent: idempotent, well-tested, secure (ReadOnly SubPath mounts), and provides good observability. The prior review issue (Made-with: Cursor → Assisted-By: Cursor) has been fixed ✅.
Remaining issues:
- All 4 commits and PR title are missing emoji prefixes (Kagenti convention)
- Final commit message is too generic
Prior review issues:
✅ Assisted-By: Cursor trailer fixed (was Made-with: Cursor)
Areas reviewed: Go, Tests, Security, Cross-repo consistency
Commits: 4 commits, all signed-off ✅, all missing emoji prefixes ❌
CI status: DCO passing ✅
Code quality: Excellent - idempotent, well-tested, secure mounts, good observability
| // keys client-id.txt and client-secret.txt. The webhook mounts them for every container that uses | ||
| // the shared-data volume (same paths as the client-registration sidecar: /shared/client-id.txt, | ||
| // /shared/client-secret.txt). | ||
| const AnnotationKeycloakClientSecretName = "kagenti.io/keycloak-client-credentials-secret-name" |
There was a problem hiding this comment.
✅ PRAISE: Good use of a constant for the annotation name - ensures consistency with operator PR #247 and prevents typos across the codebase.
|
|
||
| // NeedsKeycloakClientCredentialsVolumePatch reports whether the pod still needs Secret volume mounts for | ||
| // operator-managed Keycloak client credentials (e.g. after webhook reinvocation when sidecars were injected first). | ||
| func NeedsKeycloakClientCredentialsVolumePatch(podSpec *corev1.PodSpec, annotations map[string]string) bool { |
There was a problem hiding this comment.
✅ PRAISE: Excellent idempotency check! NeedsKeycloakClientCredentialsVolumePatch correctly handles reinvocation scenarios where the annotation appears after initial sidecar injection.
| return false | ||
| } | ||
|
|
||
| func appendSubPathMount(c *corev1.Container, vol, subPath, mountPath string) { |
There was a problem hiding this comment.
✅ PRAISE: appendSubPathMount correctly implements idempotent mounting - checks for existing mounts before appending. This prevents duplicate volume mounts on reinvocation.
| t.Fatalf("mounts: %#v", c.VolumeMounts) | ||
| } | ||
|
|
||
| ApplyKeycloakClientCredentialsSecretVolumes(spec, ann) // idempotent |
There was a problem hiding this comment.
✅ PRAISE: Testing idempotency explicitly (calling ApplyKeycloakClientCredentialsSecretVolumes twice and verifying volume count stays at 1) is excellent practice.
| if alreadyInjected { | ||
| // Reinvocation / upgrade: sidecars exist but operator-managed registration Secret mounts may | ||
| // still be missing (annotation added after first injection pass). | ||
| if injector.NeedsKeycloakClientCredentialsVolumePatch(&pod.Spec, pod.Annotations) { |
There was a problem hiding this comment.
✅ PRAISE: The reinvocation logic is well-implemented - handles the case where the operator annotation appears after initial injection. The verbose logging (V(1).Info) will help debugging without cluttering normal logs.
| // logClientRegistrationPaths records how Keycloak client credentials are delivered for this Pod: | ||
| // Secret mounted from annotation (kagenti-operator), legacy kagenti-client-registration sidecar, or combined authbridge. | ||
| // deliveryPaths is comma-separated short tokens: operator-secret, combined-authbridge, sidecar, skip. | ||
| func logClientRegistrationPaths(namespace, crName string, labels map[string]string, combinedSidecar bool, decision InjectionDecision, annotations map[string]string) { |
There was a problem hiding this comment.
✅ PRAISE: logClientRegistrationPaths provides excellent observability into how credentials are delivered (operator-secret vs sidecar vs combined vs skip). The comma-separated deliveryPaths field makes it easy to track multiple concurrent delivery methods.
|
The PR looks good to me. I will approve for now but I will do some more testing in the mean time |
|
I asked Claude to help me test a number of scenarios |
Summary
Coordinates with kagenti-operator for workloads that opt out of webhook-injected client registration (
kagenti.io/client-registration-inject: "false").requires:
requires: kagenti/kagenti-operator#247
Changes
kagenti.io/keycloak-client-credentials-secret-name(volume name matches Secret:kagenti-keycloak-client-credentials-<id>).operator_clientregwithkeycloak_client_credentialshelpers:ApplyKeycloakClientCredentialsSecretVolumes,NeedsKeycloakClientCredentialsVolumePatch,AnnotationKeycloakClientSecretName.keycloak-client-registration.Related
Testing
go test ./internal/webhook/injector/...(unit); deploy webhook + operator together for integration.fixes kagenti/kagenti#397