keycloakopenldap

Keycloak: Users are not imported from OpenLDAP


In the Keycloak Users view I cannot see the LDAP users, although they should have been imported:

2023-08-17 08:01:27,607 INFO  [org.keycloak.storage.ldap.LDAPIdentityStoreRegistry] (Timer-0) Creating new LDAP Store for the LDAP storage provider: 'ldap', LDAP Configuration: {pagination=[false], fullSyncPeriod=[3600], startTls=[false], usersDn=[ou=users,dc=vlch,dc=local], connectionPooling=[false], cachePolicy=[DEFAULT], useKerberosForPasswordAuthentication=[false], importEnabled=[true], enabled=[true], usernameLDAPAttribute=[uid], bindDn=[cn=admin,dc=vlch,dc=local], changedSyncPeriod=[600], vendor=[other], uuidLDAPAttribute=[entryUUID], allowKerberosAuthentication=[false], connectionUrl=[ldap://x.x.x.x:389], syncRegistrations=[true], authType=[simple], useTruststoreSpi=[always], usePasswordModifyExtendedOp=[false], trustEmail=[false], userObjectClasses=[*], rdnLDAPAttribute=[uid], editMode=[READ_ONLY], validatePasswordPolicy=[false]}, binaryAttributes: []
2023-08-17 08:01:27,619 INFO  [org.keycloak.storage.ldap.LDAPStorageProviderFactory] (Timer-0) Sync changed users from LDAP to local store: realm: 644c2eae-8186-4cbb-96bc-03a146c4b62a, federation provider: ldap, last sync time: Thu Jan 01 00:00:00 GMT 1970
2023-08-17 08:01:27,980 INFO  [org.keycloak.storage.ldap.LDAPStorageProviderFactory] (Timer-0) Sync changed users finished: 3 imported users, 0 updated users

When accessing the users via the REST API {{KEYCLOAK_HOST}}/admin/realms/{{realm}}/users I can see them:

[
    {
        "id": "2807a4c6-b947-48b1-97f2-111f25f3eff3",
        "createdTimestamp": 1692259287960,
        "username": "user01",
        "enabled": true,
        "totp": false,
        "emailVerified": false,
        "firstName": "User1",
        "lastName": "Bar1",
        "federationLink": "39637f5c-cf75-4f37-ba6b-8bb634190cfc",
        "attributes": {
            "LDAP_ENTRY_DN": [
                "cn=user01,ou=users,dc=vlch,dc=local"
            ],
            "modifyTimestamp": [
                "20230817073107Z"
            ],
            "createTimestamp": [
                "20230817073107Z"
            ],
            "LDAP_ID": [
                "c638e942-d11b-103d-87db-67ab5a774fb1"
            ]
        },
        "disableableCredentialTypes": [],
        "requiredActions": [],
        "notBefore": 0,
        "access": {
            "manageGroupMembership": true,
            "view": true,
            "mapRoles": true,
            "impersonate": true,
            "manage": true
        }
    },
    {
        "id": "f60bf9db-460a-41a0-841a-b181211592d4",
        "createdTimestamp": 1692259287968,
        "username": "user02",
        "enabled": true,
        "totp": false,
        "emailVerified": false,
        "firstName": "User2",
        "lastName": "Bar2",
        "federationLink": "39637f5c-cf75-4f37-ba6b-8bb634190cfc",
        "attributes": {
            "LDAP_ENTRY_DN": [
                "cn=user02,ou=users,dc=vlch,dc=local"
            ],
            "modifyTimestamp": [
                "20230817073107Z"
            ],
            "createTimestamp": [
                "20230817073107Z"
            ],
            "LDAP_ID": [
                "c639126e-d11b-103d-87dc-67ab5a774fb1"
            ]
        },
        "disableableCredentialTypes": [],
        "requiredActions": [],
        "notBefore": 0,
        "access": {
            "manageGroupMembership": true,
            "view": true,
            "mapRoles": true,
            "impersonate": true,
            "manage": true
        }
    },
    {
        "id": "1f6eb0cb-a54c-41dc-944c-ab840f308a66",
        "createdTimestamp": 1692259287972,
        "username": "user03",
        "enabled": true,
        "totp": false,
        "emailVerified": false,
        "firstName": "User3",
        "lastName": "Bar3",
        "federationLink": "39637f5c-cf75-4f37-ba6b-8bb634190cfc",
        "attributes": {
            "LDAP_ENTRY_DN": [
                "cn=user03,ou=users,dc=vlch,dc=local"
            ],
            "modifyTimestamp": [
                "20230817073107Z"
            ],
            "createTimestamp": [
                "20230817073107Z"
            ],
            "LDAP_ID": [
                "c6397fba-d11b-103d-87dd-67ab5a774fb1"
            ]
        },
        "disableableCredentialTypes": [],
        "requiredActions": [],
        "notBefore": 0,
        "access": {
            "manageGroupMembership": true,
            "view": true,
            "mapRoles": true,
            "impersonate": true,
            "manage": true
        }
    }
]

This is my LDAP user federation configuration:

{
  "pagination":[
    false
  ],
  "fullSyncPeriod":[
    3600
  ],
  "startTls":[
    false
  ],
  "usersDn":[
    "ou=users,dc=vlch,dc=local"
  ],
  "connectionPooling":[
    false
  ],
  "cachePolicy":[
    "DEFAULT"
  ],
  "useKerberosForPasswordAuthentication":[
    false
  ],
  "importEnabled":[
    true
  ],
  "enabled":[
    true
  ],
  "usernameLDAPAttribute":[
    "uid"
  ],
  "bindDn":[
    "cn=admin,dc=vlch,dc=local"
  ],
  "changedSyncPeriod":[
    600
  ],
  "vendor":[
    "other"
  ],
  "uuidLDAPAttribute":[
    "entryUUID"
  ],
  "allowKerberosAuthentication":[
    false
  ],
  "connectionUrl":[
    "ldap://x.x.x.x:389"
  ],
  "syncRegistrations":[
    true
  ],
  "authType":[
    "simple"
  ],
  "useTruststoreSpi":[
    "always"
  ],
  "usePasswordModifyExtendedOp":[
    false
  ],
  "trustEmail":[
    false
  ],
  "userObjectClasses":[
    "*"
  ],
  "rdnLDAPAttribute":[
    "uid"
  ],
  "editMode":[
    "READ_ONLY"
  ],
  "validatePasswordPolicy":[
    false
  ]
}
"binaryAttributes":[]

This is the ldapsearch output (normal):

# ldapsearch -v -H ldap://x.x.x.x:389 -x -b'dc=vlch,dc=local'
ldap_initialize( ldap://x.x.x.x:389/??base )
filter: (objectclass=*)
requesting: All userApplication attributes
# extended LDIF
#
# LDAPv3
# base <dc=vlch,dc=local> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# vlch.local
dn: dc=vlch,dc=local
objectClass: dcObject
objectClass: organization
dc: vlch
o: example

# users, vlch.local
dn: ou=users,dc=vlch,dc=local
objectClass: organizationalUnit
ou: users

# user01, users, vlch.local
dn: cn=user01,ou=users,dc=vlch,dc=local
cn: User1
cn: user01
sn: Bar1
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
userPassword:: cGFzc3dvcmQx
uid: user01
uidNumber: 1000
gidNumber: 1000
homeDirectory: /home/user01

# user02, users, vlch.local
dn: cn=user02,ou=users,dc=vlch,dc=local
cn: User2
cn: user02
sn: Bar2
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
userPassword:: cGFzc3dvcmQy
uid: user02
uidNumber: 1001
gidNumber: 1001
homeDirectory: /home/user02

# user03, users, vlch.local
dn: cn=user03,ou=users,dc=vlch,dc=local
cn: User3
cn: user03
sn: Bar3
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
userPassword:: U2llbWVucy0x
uid: user03
uidNumber: 1002
gidNumber: 1002
homeDirectory: /home/user03

# readers, groups, vlch.local
dn: cn=readers,ou=groups,dc=vlch,dc=local
objectClass: groupOfNames
member: cn=user02,ou=users,dc=vlch,dc=local
member: cn=user03,ou=users,dc=vlch,dc=local
member: cn=user01,ou=users,dc=vlch,dc=local
cn: readers

# groups, vlch.local
dn: ou=groups,dc=vlch,dc=local
ou: groups
objectClass: organizationalUnit
objectClass: top

# main, groups, vlch.local
dn: cn=main,ou=groups,dc=vlch,dc=local
cn: main
member: cn=user03,ou=users,dc=vlch,dc=local
objectClass: groupOfNames
objectClass: top

# search result
search: 2
result: 0 Success

# numResponses: 9
# numEntries: 8

This is the ldapsearch output (with hidden attributes):

# ldapsearch -v -H ldap://x.x.x.x:389 -x -b'dc=vlch,dc=local' +
ldap_initialize( ldap://x.x.x.x:389/??base )
filter: (objectclass=*)
requesting: + 
# extended LDIF
#
# LDAPv3
# base <dc=vlch,dc=local> with scope subtree
# filter: (objectclass=*)
# requesting: + 
#

# vlch.local
dn: dc=vlch,dc=local
structuralObjectClass: organization
entryUUID: c63893d4-d11b-103d-87d9-67ab5a774fb1
creatorsName: cn=admin,dc=vlch,dc=local
createTimestamp: 20230817073107Z
entryCSN: 20230817073107.426863Z#000000#000#000000
modifiersName: cn=admin,dc=vlch,dc=local
modifyTimestamp: 20230817073107Z
entryDN: dc=vlch,dc=local
subschemaSubentry: cn=Subschema
hasSubordinates: TRUE

# users, vlch.local
dn: ou=users,dc=vlch,dc=local
structuralObjectClass: organizationalUnit
entryUUID: c638bec2-d11b-103d-87da-67ab5a774fb1
creatorsName: cn=admin,dc=vlch,dc=local
createTimestamp: 20230817073107Z
entryCSN: 20230817073107.427978Z#000000#000#000000
modifiersName: cn=admin,dc=vlch,dc=local
modifyTimestamp: 20230817073107Z
entryDN: ou=users,dc=vlch,dc=local
subschemaSubentry: cn=Subschema
hasSubordinates: TRUE

# user01, users, vlch.local
dn: cn=user01,ou=users,dc=vlch,dc=local
structuralObjectClass: inetOrgPerson
entryUUID: c638e942-d11b-103d-87db-67ab5a774fb1
creatorsName: cn=admin,dc=vlch,dc=local
createTimestamp: 20230817073107Z
entryCSN: 20230817090623.828587Z#000000#000#000000
modifiersName: cn=admin,dc=vlch,dc=local
modifyTimestamp: 20230817090623Z
entryDN: cn=user01,ou=users,dc=vlch,dc=local
subschemaSubentry: cn=Subschema
hasSubordinates: FALSE

# user02, users, vlch.local
dn: cn=user02,ou=users,dc=vlch,dc=local
structuralObjectClass: inetOrgPerson
entryUUID: c639126e-d11b-103d-87dc-67ab5a774fb1
creatorsName: cn=admin,dc=vlch,dc=local
createTimestamp: 20230817073107Z
entryCSN: 20230817073107.430120Z#000000#000#000000
modifiersName: cn=admin,dc=vlch,dc=local
modifyTimestamp: 20230817073107Z
entryDN: cn=user02,ou=users,dc=vlch,dc=local
subschemaSubentry: cn=Subschema
hasSubordinates: FALSE

# user03, users, vlch.local
dn: cn=user03,ou=users,dc=vlch,dc=local
structuralObjectClass: inetOrgPerson
entryUUID: c6397fba-d11b-103d-87dd-67ab5a774fb1
creatorsName: cn=admin,dc=vlch,dc=local
createTimestamp: 20230817073107Z
entryCSN: 20230817073107.432918Z#000000#000#000000
modifiersName: cn=admin,dc=vlch,dc=local
modifyTimestamp: 20230817073107Z
entryDN: cn=user03,ou=users,dc=vlch,dc=local
subschemaSubentry: cn=Subschema
hasSubordinates: FALSE

# readers, groups, vlch.local
dn: cn=readers,ou=groups,dc=vlch,dc=local
structuralObjectClass: groupOfNames
entryUUID: c639ab70-d11b-103d-87de-67ab5a774fb1
creatorsName: cn=admin,dc=vlch,dc=local
createTimestamp: 20230817073107Z
entryCSN: 20230817110936.059512Z#000000#000#000000
modifiersName: cn=admin,dc=vlch,dc=local
modifyTimestamp: 20230817110936Z
entryDN: cn=readers,ou=groups,dc=vlch,dc=local
subschemaSubentry: cn=Subschema
hasSubordinates: FALSE

# groups, vlch.local
dn: ou=groups,dc=vlch,dc=local
structuralObjectClass: organizationalUnit
entryUUID: 251d5fc4-d13a-103d-9c67-2d90308da14c
creatorsName: cn=admin,dc=vlch,dc=local
createTimestamp: 20230817110831Z
entryCSN: 20230817110831.533698Z#000000#000#000000
modifiersName: cn=admin,dc=vlch,dc=local
modifyTimestamp: 20230817110831Z
entryDN: ou=groups,dc=vlch,dc=local
subschemaSubentry: cn=Subschema
hasSubordinates: TRUE

# main, groups, vlch.local
dn: cn=main,ou=groups,dc=vlch,dc=local
structuralObjectClass: groupOfNames
entryUUID: 88c5e0a0-d13a-103d-9c68-2d90308da14c
creatorsName: cn=admin,dc=vlch,dc=local
createTimestamp: 20230817111118Z
entryCSN: 20230817111118.732779Z#000000#000#000000
modifiersName: cn=admin,dc=vlch,dc=local
modifyTimestamp: 20230817111118Z
entryDN: cn=main,ou=groups,dc=vlch,dc=local
subschemaSubentry: cn=Subschema
hasSubordinates: FALSE

# search result
search: 2
result: 0 Success

# numResponses: 9
# numEntries: 8

I used the bitnami/openldap:2.6 image for OpenLDAP and the keycloak/keycloak:22.0 image for the Keycloak instance. Since the log message 2023-08-17 08:01:27,980 INFO [org.keycloak.storage.ldap.LDAPStorageProviderFactory] (Timer-0) Sync changed users finished: 3 imported users, 0 updated users was seen, I would expect that I can also see the imported users in the Users overview of the Realm, but they do not appear there.

Also when changing the uuidLDAPAttribute from entryUUID to uid the users are not imported. Same behavior when changing the first name mapper LDAP Attribute from cn to uid.


Solution

  • For the search, once you have an LDAP provider, Keycloak does not seem to show all users by default anymore (for performance reasons, i suppose). You have to type something in the search or "*" for all.