Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[INTEGRATION] Trying to use LLDAP with etherpad (plus ep_ldapauth_ng plugin) #863

Open
esp13 opened this issue Mar 11, 2024 · 6 comments
Open
Labels
integration Connection between LLDAP and a service question Further information is requested

Comments

@esp13
Copy link

esp13 commented Mar 11, 2024

Description of the service
I'm a beginner so I have difficulties to configure correctly etherpad + ep_ldapauth_ng plugin (https://github.com/daschr/ep_ldapauth_ng#readme) to use LLDAP authentication.
Adding a working configuration for Etherpad in https://github.com/lldap/lldap/tree/main/example_configs could be usefull for other beginners :)

What you've tried

I tried this without success:

"users": {
    "ldapauth": {
        "url": "ldap://lldapserveraddress:lldapserverport",
        "accountBase": "ou=people,dc=mysubdomain,dc=mydomain,dc=org",
        "accountPattern": "(&(objectClass=*)(uid={{username}}))",
        "displayNameAttribute": "cn",
        "searchDN": "uid=mylldapuserthathaspermissionstoreadhtedirectory,ou=people,dc=mysubdomain,dc=mydomain,dc=org",
        "searchPWD": "thepasswordofmylldapuserthathaspermissionstoreadhtedirectory",
        "groupSearchBase": "ou=groups,dc=mysubdomain,dc=mydomain,dc=org",
        "groupAttribute": "member",
        "groupAttributeIsDN": false,
        "searchScope": "sub",
        "groupSearch": "(&(cn=mylldapgroupforwitchuserscanhaveadminrights)(objectClass=groupOfUniqueNames))",
        "anonymousReadonly": false
    }
},

I'm not sure but this seems to be a valid command to test if an user is member of a group : (&(objectClass=person)(memberof=cn=mylldapgroupforwitchuserscanhaveadminrights,ou=groups,dc=mysubdomain,dc=mydomain,dc=org)) bug no idea how to put this into the configuration

What's not working

In Etherpad logs I get:
Failed authentication from IP myIP
When the password is right (more error messages when the password is wrong)

Working Bonus

I have an other etherpad plugin (ep_mypads: https://www.npmjs.com/package/ep_mypads) that is working great with this configuration:

{
    "url": "ldap://lldapserveraddress:lldapserverport",
    "bindDN": "uid=mylldapuserthathaspermissionstoreadhtedirectory,ou=people,dc=mysubdomain,dc=mydomain,dc=org",
    "bindCredentials": "thepasswordofmylldapuserthathaspermissionstoreadhtedirectory",
    "searchBase": "ou=people,dc=mysubdomain,dc=mydomain,dc=org",
    "searchFilter": "(uid={{username}})",
    "tlsOptions": {
        "rejectUnauthorized": true
    },
    "properties": {
        "login": "uid",
        "email": "mail",
        "firstname": "givenName",
        "lastname": "sn"
    },
    "defaultLang": "fr"
}
@esp13 esp13 added the integration Connection between LLDAP and a service label Mar 11, 2024
@nitnelave
Copy link
Member

I think for the admin group you need to set groupSearch to (&(objectClass=groupOfUniqueNames)(uid=mylldapgroupforwitchuserscanhaveadminrights)).

If you want to have another group to control access to the directory, you can plug that in the accountPattern: (&(objectClass=person)(uid={{username}})(memberOf=uid=mylldapgroupforwitchuserscanaccess,ou=groups,dc=example,dc=com))

I think you also want groupAttributeIsDN to be true.

I don't know why you'd get an authentication error, though. Can you post the verbose LLDAP logs from trying to log in?

@esp13
Copy link
Author

esp13 commented Mar 12, 2024

Thank you for your answer.

I think for the admin group you need to set groupSearch to (&(objectClass=groupOfUniqueNames)(uid=mylldapgroupforwitchuserscanhaveadminrights)).

If you want to have another group to control access to the directory, you can plug that in the accountPattern: (&(objectClass=person)(uid={{username}})(memberOf=uid=mylldapgroupforwitchuserscanaccess,ou=groups,dc=example,dc=com))

I think you also want groupAttributeIsDN to be true.

I don't know why you'd get an authentication error, though. Can you post the verbose LLDAP logs from trying to log in?

With this configuration: (I didn't add the other group to control access for now (step by step :) )so each user should be able to get standard access)

"users": {
    "ldapauth": {
        "url": "ldap://lldapserveraddress:lldapserverport",
        "accountBase": "ou=people,dc=mysubdomain,dc=mydomain,dc=org",
        "accountPattern": "(&(objectClass=*)(uid={{username}}))",
        "displayNameAttribute": "cn",
        "searchDN": "uid=mylldapuserthathaspermissionstoreadhtedirectory,ou=people,dc=mysubdomain,dc=mydomain,dc=org",
        "searchPWD": "thepasswordofmylldapuserthathaspermissionstoreadhtedirectory",
        "groupSearchBase": "ou=groups,dc=mysubdomain,dc=mydomain,dc=org",
        "groupAttribute": "member",
        "groupAttributeIsDN": true,
        "searchScope": "sub",
        "groupSearch": "(&(objectClass=groupOfUniqueNames)(uid=mylldapgroupforwitchuserscanhaveadminrights))",
        "anonymousReadonly": false
    }
},

With an user test that is not part of the group mylldapgroupforwitchuserscanhaveadminrights I get this in LLDAP logs:


LDAP session [ 148ms | 0.06% / 100.00% ]
    ┝━ LDAP request [ 148ms | 0.06% / 99.93% ]
    │  ┝━ 🐛 [debug]:  | msg: LdapMsg { msgid: 1, op: BindRequest(LdapBindRequest { dn: "uid=test,ou=people,dc=mysubdomain,dc=mydomain,dc=org", cred: LdapBindCred::Simple }), ctrl: [] }
    │  ┝━ do_bind [ 148ms | 0.03% / 99.87% ] dn: uid=test,ou=people,dc=mysubdomain,dc=mydomain,dc=org
    │  │  ┝━ bind [ 148ms | 0.01% / 99.68% ]
    │  │  │  ┝━ get_password_file_for_user [ 90.8µs | 0.06% ] user_id: UserId("test")
    │  │  │  ┕━ passwords_match [ 148ms | 99.61% ] username: test
    │  │  ┝━ get_user_groups [ 249µs | 0.17% ] user_id: "test"
    │  │  │  ┕━ 🐛 [debug]:  | return: {GroupDetails { group_id: GroupId(3), display_name: "lldap_strict_readonly", creation_date: 2024-02-11T16:08:43.151252775, uuid: Uuid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"), attributes: [] }}
    │  │  ┕━ 🐛 [debug]: Success!
    │  ┕━ 🐛 [debug]:  | response: BindResponse(LdapBindResponse { res: LdapResult { code: Success, matcheddn: "", message: "", referral: [] }, saslcreds: None })
    ┕━ LDAP request [ 10.3µs | 0.01% ]
       ┕━ 🐛 [debug]:  | msg: LdapMsg { msgid: 2, op: UnbindRequest, ctrl: [] }
    LDAP session [ 138ms | 0.09% / 100.00% ]
    ┝━ LDAP request [ 137ms | 0.07% / 99.38% ]
    │  ┝━ 🐛 [debug]:  | msg: LdapMsg { msgid: 1, op: BindRequest(LdapBindRequest { dn: "uid=mylldapuserthathaspermissionstoreadhtedirectory,ou=people,dc=mysubdomain,dc=mydomain,dc=org", cred: LdapBindCred::Simple }), ctrl: [] }
    │  ┝━ do_bind [ 137ms | 0.03% / 99.31% ] dn: uid=mylldapuserthathaspermissionstoreadhtedirectory,ou=people,dc=mysubdomain,dc=mydomain,dc=org
    │  │  ┝━ bind [ 137ms | 0.01% / 99.07% ]
    │  │  │  ┝━ get_password_file_for_user [ 147µs | 0.11% ] user_id: UserId("mylldapuserthathaspermissionstoreadhtedirectory")
    │  │  │  ┕━ passwords_match [ 137ms | 98.95% ] username: mylldapuserthathaspermissionstoreadhtedirectory
    │  │  ┝━ get_user_groups [ 296µs | 0.21% ] user_id: "mylldapuserthathaspermissionstoreadhtedirectory"
    │  │  │  ┕━ 🐛 [debug]:  | return: {GroupDetails { group_id: GroupId(3), display_name: "lldap_strict_readonly", creation_date: 2024-02-11T16:08:43.151252775, uuid: Uuid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"), attributes: [] }}
    │  │  ┕━ 🐛 [debug]: Success!
    │  ┕━ 🐛 [debug]:  | response: BindResponse(LdapBindResponse { res: LdapResult { code: Success, matcheddn: "", message: "", referral: [] }, saslcreds: None })
    ┝━ LDAP request [ 720µs | 0.09% / 0.52% ]
    │  ┝━ 🐛 [debug]:  | msg: LdapMsg { msgid: 2, op: SearchRequest(LdapSearchRequest { base: "ou=people, dc=mysubdomain, dc=mydomain, dc=org", scope: Subtree, aliases: Never, sizelimit: 0, timelimit: 10, typesonly: false, filter: And([Present("objectClass"), Equality("uid", "test")]), attrs: [] }), ctrl: [] }
    │  ┝━ do_search [ 596µs | 0.14% / 0.43% ]
    │  │  ┝━ 🐛 [debug]:  | request.base: "ou=people, dc=mysubdomain, dc=mydomain, dc=org" | scope: Users
    │  │  ┝━ get_user_list [ 388µs | 0.02% / 0.28% ]
    │  │  │  ┝━ 🐛 [debug]:  | filters: And([And([]), UserId(UserId("test"))])
    │  │  │  ┕━ list_users [ 363µs | 0.26% ] filters: Some(And([And([]), UserId(UserId("test"))])) | _get_groups: false
    │  │  │     ┕━ 🐛 [debug]:  | return: [UserAndGroups { user: User { user_id: UserId("test"), email: "[email protected]", display_name: Some("testDN"), creation_date: 2024-02-11T16:16:19.264620212, uuid: Uuid("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"), attributes: [AttributeValue { name: "first_name", value: Serialized("testFN") }, AttributeValue { name: "last_name", value: Serialized("testLN") }] }, groups: Some([GroupDetails { group_id: GroupId(3), display_name: "lldap_strict_readonly", creation_date: 2024-02-11T16:08:43.151252775, uuid: Uuid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"), attributes: [] }]) }]
    │  │  ┕━ expand_attribute_wildcards [ 12.1µs | 0.01% ] ldap_attributes: []
    │  │     ┕━ 🐛 [debug]:  | resolved_attributes: ["objectclass", "uid", "mail", "givenname", "sn", "cn", "jpegPhoto", "createtimestamp", "entryuuid"]
    │  ┝━ 🐛 [debug]:  | response: SearchResultEntry(LdapSearchResultEntry { dn: "uid=test,ou=people,dc=mysubdomain,dc=mydomain,dc=org", attributes: [LdapPartialAttribute { atype: "objectclass", vals: ["inetOrgPerson", "posixAccount", "mailAccount", "person"] }, LdapPartialAttribute { atype: "uid", vals: ["test"] }, LdapPartialAttribute { atype: "mail", vals: ["[email protected]"] }, LdapPartialAttribute { atype: "givenname", vals: ["testFN"] }, LdapPartialAttribute { atype: "sn", vals: ["testLN"] }, LdapPartialAttribute { atype: "cn", vals: ["testDN"] }, LdapPartialAttribute { atype: "createtimestamp", vals: ["2024-02-11T16:16:19.264620212+00:00"] }, LdapPartialAttribute { atype: "entryuuid", vals: ["zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"] }] })
    │  ┕━ 🐛 [debug]:  | response: SearchResultDone(LdapResult { code: Success, matcheddn: "", message: "", referral: [] })
    ┕━ LDAP request [ 8.43µs | 0.01% ]
       ┕━ 🐛 [debug]:  | msg: LdapMsg { msgid: 3, op: UnbindRequest, ctrl: [] }
    LDAP session [ 138ms | 0.09% / 100.00% ]
    ┝━ LDAP request [ 137ms | 0.06% / 99.47% ]
    │  ┝━ 🐛 [debug]:  | msg: LdapMsg { msgid: 1, op: BindRequest(LdapBindRequest { dn: "uid=mylldapuserthathaspermissionstoreadhtedirectory,ou=people,dc=mysubdomain,dc=mydomain,dc=org", cred: LdapBindCred::Simple }), ctrl: [] }
    │  ┝━ do_bind [ 137ms | 0.03% / 99.41% ] dn: uid=mylldapuserthathaspermissionstoreadhtedirectory,ou=people,dc=mysubdomain,dc=mydomain,dc=org
    │  │  ┝━ bind [ 136ms | 0.01% / 99.24% ]
    │  │  │  ┝━ get_password_file_for_user [ 74.2µs | 0.05% ] user_id: UserId("mylldapuserthathaspermissionstoreadhtedirectory")
    │  │  │  ┕━ passwords_match [ 136ms | 99.18% ] username: mylldapuserthathaspermissionstoreadhtedirectory
    │  │  ┝━ get_user_groups [ 200µs | 0.15% ] user_id: "mylldapuserthathaspermissionstoreadhtedirectory"
    │  │  │  ┕━ 🐛 [debug]:  | return: {GroupDetails { group_id: GroupId(3), display_name: "lldap_strict_readonly", creation_date: 2024-02-11T16:08:43.151252775, uuid: Uuid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"), attributes: [] }}
    │  │  ┕━ 🐛 [debug]: Success!
    │  ┕━ 🐛 [debug]:  | response: BindResponse(LdapBindResponse { res: LdapResult { code: Success, matcheddn: "", message: "", referral: [] }, saslcreds: None })
    ┝━ LDAP request [ 581µs | 0.08% / 0.42% ]
    │  ┝━ 🐛 [debug]:  | msg: LdapMsg { msgid: 2, op: SearchRequest(LdapSearchRequest { base: "ou=groups, dc=mysubdomain, dc=mydomain, dc=org", scope: Subtree, aliases: Never, sizelimit: 0, timelimit: 10, typesonly: false, filter: And([Equality("objectClass", "groupOfUniqueNames"), Equality("uid", "mylldapgroupforwitchuserscanhaveadminrights")]), attrs: [] }), ctrl: [] }
    │  ┝━ do_search [ 467µs | 0.11% / 0.34% ]
    │  │  ┝━ 🐛 [debug]:  | request.base: "ou=groups, dc=mysubdomain, dc=mydomain, dc=org" | scope: Groups
    │  │  ┝━ get_groups_list [ 308µs | 0.01% / 0.22% ]
    │  │  │  ┝━ 🐛 [debug]:  | filters: And([And([]), DisplayName("mylldapgroupforwitchuserscanhaveadminrights")])
    │  │  │  ┕━ list_groups [ 289µs | 0.21% ] filters: Some(And([And([]), DisplayName("mylldapgroupforwitchuserscanhaveadminrights")]))
    │  │  │     ┕━ 🐛 [debug]:  | return: [Group { id: GroupId(4), display_name: "mylldapgroupforwitchuserscanhaveadminrights", creation_date: 2024-03-11T12:41:49.627889976, uuid: Uuid("yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"), users: [UserId("mylldapuserthatisinthegroupthatcanhaveadminrights")], attributes: [] }]
    │  │  ┕━ expand_attribute_wildcards [ 9.27µs | 0.01% ] ldap_attributes: []
    │  │     ┕━ 🐛 [debug]:  | resolved_attributes: ["objectclass", "uid", "cn", "member", "uniquemember", "entryuuid"]
    │  ┝━ 🐛 [debug]:  | response: SearchResultEntry(LdapSearchResultEntry { dn: "cn=mylldapgroupforwitchuserscanhaveadminrights,ou=groups,dc=mysubdomain,dc=mydomain,dc=org", attributes: [LdapPartialAttribute { atype: "objectclass", vals: ["groupOfUniqueNames"] }, LdapPartialAttribute { atype: "uid", vals: ["mylldapgroupforwitchuserscanhaveadminrights"] }, LdapPartialAttribute { atype: "cn", vals: ["mylldapgroupforwitchuserscanhaveadminrights"] }, LdapPartialAttribute { atype: "member", vals: ["uid=mylldapuserthatisinthegroupthatcanhaveadminrights,ou=people,dc=mysubdomain,dc=mydomain,dc=org"] }, LdapPartialAttribute { atype: "uniquemember", vals: ["uid=mylldapuserthatisinthegroupthatcanhaveadminrights,ou=people,dc=mysubdomain,dc=mydomain,dc=org"] }, LdapPartialAttribute { atype: "entryuuid", vals: ["yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"] }] })
    │  ┕━ 🐛 [debug]:  | response: SearchResultDone(LdapResult { code: Success, matcheddn: "", message: "", referral: [] })
    ┕━ LDAP request [ 12.3µs | 0.01% ]
       ┕━ 🐛 [debug]:  | msg: LdapMsg { msgid: 3, op: UnbindRequest, ctrl: [] }

@nitnelave
Copy link
Member

That all looks correct: it first tries to bind as "test", to check the password, and that works. Then it connects as your read-only bind user to get the "test" user details, and the admin group details (which does not contain test).

What issues are you facing?

@esp13
Copy link
Author

esp13 commented Mar 13, 2024

That all looks correct: it first tries to bind as "test", to check the password, and that works. Then it connects as your read-only bind user to get the "test" user details, and the admin group details (which does not contain test).

What issues are you facing?

Etherpad doesn't grant access (it ask for login one more time) and in Etherpad logs I get:
Failed authentication from IP myIP

Edit: I'm asking myself if I am wasting a lot of time for a plugin issue or if I misunderstood the doc. https://github.com/daschr/ep_ldapauth_ng/ I thought I could connect through ldap directly OR through ldap + OIDC but maybe I'm wrong.

@nitnelave
Copy link
Member

Hmm, I can help you debug the LDAP part, but it seems to be working well. At this point, you'll be better served by asking the etherpad community (or the plugin author) for help. Maybe also looking at the etherpad logs.

@nitnelave nitnelave added the question Further information is requested label Mar 18, 2024
@esp13
Copy link
Author

esp13 commented Mar 29, 2024

Hmm, I can help you debug the LDAP part, but it seems to be working well. At this point, you'll be better served by asking the etherpad community (or the plugin author) for help. Maybe also looking at the etherpad logs.

I think the main problem is that this ep_ldapauth_ng plugin (https://github.com/daschr/ep_ldapauth_ng#readme) isn't working with last etherpad version anymore.
Curious, I tried the older one ep_ldapauth (non _ng) plugin (https://github.com/tykeal/ep_ldapauth#readme) with this configuration :

"users": {
    "ldapauth": {
        "url": "ldap://lldapserveraddress:lldapserverport",
        "accountBase": "ou=people,dc=mysubdomain,dc=mydomain,dc=org",
        "accountPattern": "(&(objectClass=*)(uid={{username}}))",
        "displayNameAttribute": "cn",
        "searchDN": "uid=mylldapuserthathaspermissionstoreadhtedirectory,ou=people,dc=mysubdomain,dc=mydomain,dc=org",
        "searchPWD": "thepasswordofmylldapuserthathaspermissionstoreadhtedirectory",
        "groupSearchBase": "ou=groups,dc=mysubdomain,dc=mydomain,dc=org",
        "groupAttribute": "member",
        "groupAttributeIsDN": true,
        "searchScope": "sub",
        "groupSearch": "(&(objectClass=groupOfUniqueNames)(cn=mylldapgroupforwitchuserscanhaveadminrights))",
        "anonymousReadonly": false
    }
},

It worked for regular users. But doesn't work for users from the mylldapgroupforwitchuserscanhaveadminrights group that don't get etherpad admin permission.
Maybe the etherpad admin login mechanism has been modified since last plugin update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integration Connection between LLDAP and a service question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants