Back openDesk Edu for a sovereign, open-source education â every vote counts.
Vote nowSave products you love by clicking the heart icon.
What Flux v2.8 and Helm v4 change for GitOps users â server-side apply, kstatus health checks, CEL expressions, .status.inventory, Cosign v3, and the post-renderer breaking change.

Nachdem ein Benutzer, der via Keycloak OIDC automatisch provisioniert wurde, erfolgreich in LDAP erstellt wurde, kann der Proxy von OpenCloud kein Session-Token ausstellen. Der Benutzer sieht âSie werden eingeloggtâ, gefolgt von âNicht angemeldetâ. Jedes. Einzelne. Mal.
Dies ist die Geschichte darĂŒber, wie drei unabhĂ€ngige Bugs aufeinandertrafen, um diesen Fehler zu verursachen â und was es brauchte, um sie zu finden.
OpenCloud 4.0.3, bereitgestellt via Helm auf Kubernetes, konfiguriert mit einem externen UMS LDAP (OpenLDAP) als Identity-Backend. Benutzer authentifizieren sich ĂŒber Keycloak/Shibboleth via OIDC. Auto-Provisioning ist aktiviert: Wenn sich ein Benutzer zum ersten Mal anmeldet, erstellt OpenCloud dessen LDAP-Eintrag on-the-fly.
Der Fehler war konsistent und reproduzierbar:
https://opencloud...Die OpenCloud-Logs zeigten ein klares Muster:
graph: failed to add user â LDAP Result Code 68 "Entry Already Exists"
graph: could not create user: backend error â nameAlreadyExists
proxy: Error Response â OData Error: a user with that name already exists
proxy: Error getting token for autoprovisioned user â user not found
Der Auto-Provision-Flow sah so aus:
Jeder Login-Versuch stieĂ an eine Wand. Und der Benutzer existierte TATSĂCHLICH in LDAP. Drei unabhĂ€ngige Bugs waren dafĂŒr verantwortlich.
EQUALITY auf openCloudUUIDDer Auto-Provision-Code durchsucht LDAP nach einem existierenden Benutzer per UUID, bevor ein neuer erstellt wird. Der Benutzer existierte â aber die Suche lieferte null Ergebnisse.
Test der LDAP-Suche direkt:
$ ldapsearch ... "(openCloudUUID=*)"
â returns the entry (presence check works)
$ ldapsearch ... "(openCloudUUID=b7ada882-...)"
â returns 0 entries (equality check FAILS)
Das Attribut existierte. Der Wert war korrekt. Aber der Equality-Matching-Vergleich funktionierte nicht.
Der Attributtyp openCloudUUID im OpenLDAP-Schema wurde ohne eine EQUALITY-Regel geladen:
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.1
NAME 'openCloudUUID'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
# Configmap definition (correct):
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.1
NAME 'openCloudUUID'
EQUALITY caseIgnoreMatch â MISSING from loaded schema!
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
Ohne EQUALITY caseIgnoreMatch kann OpenLDAP keinen Equality-Matching-Vergleich auf dem Attribut durchfĂŒhren. Der LDAP-Schema-Job prĂŒfte lediglich auf das Vorhandensein neuer Attribut-OIDs â er hat nie verifiziert, ob bestehende Attribute die korrekten Matching-Rules hatten. Wenn also ein altes Schema geladen wurde (aus einer frĂŒheren Chart-Version, der EQUALITY fehlte), haben nachfolgende Upgrades dies nie korrigiert.
EQUALITY-Regel zum laufenden OpenLDAP via ldapmodify:ldapmodify -Y EXTERNAL -H ldapi:/// <<'EOF'
dn: cn={53}opencloud,cn=schema,cn=config
changetype: modify
replace: olcAttributeTypes
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.1 NAME 'openCloudUUID'
DESC 'OpenCloud user UUID'
EQUALITY caseIgnoreMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.2 ... )
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.3 ... )
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.4 ... )
EOF
EQUALITY bei openCloudUUID zu prĂŒfen, nicht nur auf das Vorhandensein von Attribut-OIDs.Nachdem die UUID-Suche behoben war, lieferte der LDAP-Lookup immer noch null Ergebnisse â aber diesmal lag der Grund tief im Suchfilter vergraben.
Der reva LDAP-User-Provider baut einen Suchfilter fĂŒr GetUserByClaim("userid", uuid) auf. Durch das Tracing im OpenCloud-Quellcode ergab sich folgender Filteraufbau:
filter = fmt.Sprintf("(&%s(objectclass=%s)(%s=%s)%s%s)",
i.User.Filter,
i.User.Objectclass, // "openCloudUser"
attribute, // "openCloudUUID"
value, // "b7ada882-..."
i.tenantFilter(tenantID), // ""
i.disabledFilter(), // "(!(openCloudUserEnabled=FALSE))" )
The resulting filter:
(&(objectclass=openCloudUser)(openCloudUUID=b7ada882-...)(!(openCloudUserEnabled=FALSE)))
Testing it directly:
$ ldapsearch ... "(&(objectclass=openCloudUser)(openCloudUUID=b7ada882-...))" â 1 Eintrag gefunden
$ ldapsearch ... "(&(objectclass=openCloudUser)(openCloudUUID=b7ada882-...)(!(openCloudUserEnabled=FALSE)))" â 0 EintrĂ€ge gefunden
### The Root Cause
LDAP uses three-valued logic: TRUE, FALSE, and **UNDEFINED**. When an attribute doesn't exist on an entry:
- `(attr=FALSE)` â UNDEFINED (the attribute isn't present, so the comparison can't be evaluated)
- `(!(attr=FALSE))` â NOT(UNDEFINED) â **UNDEFINED**
- `(TRUE AND TRUE AND UNDEFINED)` â **UNDEFINED** â entry is **not returned**
The user entry in the external UMS LDAP didn't have an `openCloudUserEnabled` attribute. This is an OpenCloud-internal attribute that doesn't exist in the external LDAP. The `disabledFilter()` was designed for OpenCloud's internal IDM LDAP (which has this attribute), but when pointed at an external LDAP, it silently filtered out **every user**.
The `DisableUserMechanism` was set to `"attribute"` by default, which adds the `(!(openCloudUserEnabled=FALSE))` filter. In OpenCloud's internal IDM, every user has this attribute set to `TRUE`. In an external LDAP, nobody does.
### The Fix
```yaml
# values.yaml
oidc:
roleAssignmentDriver: "default"
# â setzt Umgebungsvariable OC_LDAP_DISABLE_USER_MECHANISM=none
Setting OC_LDAP_DISABLE_USER_MECHANISM=none tells the users service to skip the disabled filter entirely. This is the correct setting when using an external LDAP that doesn't manage OpenCloud-specific attributes.
After fixing the LDAP search, the login flow progressed further â but failed with a new error:
proxy: no roles in user claims
proxy: Error mapping role names to role ids â oidcroles.go:84
proxy: Could not get user roles â account_resolver.go:192
The proxy was configured with PROXY_ROLE_ASSIGNMENT_DRIVER=oidc, which reads role information from OIDC claims and maps them to OpenCloud roles. Our Keycloak instance doesn't send roles in the OIDC token â it's a simple authentication-only setup.
The OIDC role mapper iterates over the claims, looks for roles, finds none, and returns an error. This error propagates up through the account resolver, which aborts the login.
I initially tried GRAPH_ASSIGN_DEFAULT_USER_ROLE=true, which controls whether the Graph API assigns a default role when creating users. But the error was coming from the Proxy after user creation, during token issuance. Two different code paths, two different env vars.
The PROXY_ROLE_ASSIGNMENT_DRIVER supports two values:
| Driver | Behaviour |
|---|---|
oidc | Reads roles from OIDC claims. Fails if claims have no roles. |
default | Assigns the role "user" to any user without a role at login time. |
The oidc driver is designed for setups where Keycloak sends roles via an OIDC claim (e.g., roles, groups, or a custom mapper). When used with a Keycloak that doesn't send roles, it's a hard blocker.
# values.yaml
oidc:
roleAssignmentDriver: "default"
# â setzt Umgebungsvariable PROXY_ROLE_ASSIGNMENT_DRIVER=default
The default driver checks if the user already has a role assigned. If not, it assigns the built-in "user" role. This is the correct choice for most simple OIDC setups.
Here's how the three bugs interacted:
Benutzer authentifiziert sich via OIDC
â
Proxy ruft GetUserByClaims("userid", uuid) auf
â
Gateway delegiert an users service (LDAP backend)
â
Bug #1: LDAP-Schema â UUID-Equality-Suche liefert 0 EintrĂ€ge
Bug #2: disabledFilter â bestehender Benutzer wird stillschweigend aus den Ergebnissen ausgeschlossen
â
GetUserByClaims â ErrAccountNotFound
â
Proxy ruft CreateUserFromClaims auf â Graph API â LDAP add â "Entry Already Exists"
â
Cloud gibt nameAlreadyExists zurĂŒck â CreateUserFromClaims liest Benutzer erneut aus â gibt Benutzer zurĂŒck
â
Proxy ruft GetUserByClaims erneut auf â immer noch ErrAccountNotFound (Bugs 1 & 2 erneut)
â
Proxy lĂ€uft weiter â versucht Rollenzuweisung
Bug #3: OIDC-Driver â keine Rollen in den Claims â Fehler
â
"No roles in user claims" â 401 â "Nicht angemeldet"
Jeder dieser Bugs wÀre in einer anderen Konfiguration einzeln verkraftbar gewesen:
Aber zusammen bildeten sie eine absolut undurchdringliche Mauer.
LDAP-Schema-Gleichheitsregeln verifizieren. PrĂ€senzprĂŒfungen (attr=*) können einwandfrei funktionieren, wĂ€hrend GleichheitsprĂŒfungen (attr=value) stillschweigend fehlschlagen. Testen Sie immer beides.
Die dreiwertige Logik von LDAP ist nicht intuitiv. (!(attr=FALSE)) ist KEIN No-Op fĂŒr fehlende Attribute â es ist UNDEFINED, was EintrĂ€ge aus den Suchergebnissen ausschlieĂt. Wenn Sie einen âdisabledâ-Filter hinzufĂŒgen, stellen Sie sicher, dass jeder Benutzer das Attribut tatsĂ€chlich besitzt.
Wissen, welcher Dienst welche Umgebungsvariable besitzt. GRAPH_ASSIGN_DEFAULT_USER_ROLE (Graph API, wÀhrend der Benutzererstellung) und PROXY_ROLE_ASSIGNMENT_DRIVER (Proxy, wÀhrend des Logins/der Token-Ausstellung) steuern unterschiedliche Phasen desselben Flows. Die Behebung der falschen Variable bewirkt nichts.
Immer nach jedem Fix erneut testen. Das Debugging von drei gestapelten Bugs ist nur machbar, wenn jeder Fix unabhĂ€ngig verifiziert wird, bevor man zum nĂ€chsten ĂŒbergeht. Die Fehlermeldungen Ă€nderten sich bei jedem Schritt â was die unabhĂ€ngige BestĂ€tigung jedes Fixes ermöglichte.
Alle drei Fixes wurden in die OpenCloud-Revision 50 eingespielt, wobei die Chart-Templates aktualisiert wurden, um ein erneutes Auftreten bei zukĂŒnftigen Deployments zu verhindern.