nginxinc/nginx-ldap-auth

Fails to auth if BaseDN isn't maximally narrow OU

Drugoy opened this issue · 7 comments

I have a problem that this module seems to generally work, however I can't make it work for all of our users:
Our users (CNs in AD) are located in different OUs and there's no way to specify as BaseDN some common ancestor: if I do so - all users fail to log in.

Reduced case:
In AD:
CN=UserA,OU=X,OU=Users,OU=BranchA,DC=my,DC=corp
CN=UserB,OU=Y,OU=Users,OU=BranchB,DC=my,DC=corp
both are members of the group CN=access-group,OU=special_groups,OU=technical_stuff,DC=my,DC=corp
tech account, used for binding with AD:
CN=ldap_gatekeeper,OU=Service Accounts,DC=my,DC=corp

In nginx vhost config:

  proxy_set_header X-Ldap-URL "ldap://my.corp:389";
  proxy_set_header X-Ldap-BaseDN "DC=my,DC=corp"; # fails to auth both UserA and UserB
  proxy_set_header X-Ldap-BindDN "CN=ldap_gatekeeper,OU=Service Accounts,DC=my,DC=corp";
  proxy_set_header X-Ldap-BindPass "passwordhere";
  proxy_set_header X-Ldap-Template "(&(objectClass=user)(sAMAccountName=%(username)s)(memberOf=CN=access-group,OU=special_groups,OU=technical_stuff,DC=my,DC=corp))"";

It appears as if nginx-ldap-auth doesn't search for users in specified BaseDN recursively.

Log looks like this:

- using username/password from authorization header
UserA searching on server "ldap:/my.corp:389" with base dn "DC=my,DC=corp" with filter "(&(objectClass=user)(sAMAccountName=UserA)(memberOf=CN=access-group,OU=special_groups,OU=technical_stuff,DC=my,DC=corp))"
UserA Error while running search query: {'info': '000004DC: LdapErr: DSID-0C090A7D, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, V3839', 'desc': 'Operations error'}, server="ldap://my.corp:389", login="UserA"
UserA "GET /auth HTTP/1.0" 401 -

When nginx vhost is configured so:

  proxy_set_header X-Ldap-BaseDN "OU=Y,OU=Users,OU=BranchB,DC=my,DC=corp"; # successfully auths UserA, but fails to auth UserB

the log looks like this:

- using username/password from authorization header
UserA searching on server "ldap:/my.corp:389" with base dn "OU=Y,OU=Users,OU=BranchB,DC=my,DC=corp" with filter "(&(objectClass=user)(sAMAccountName=UserA)(memberOf=CN=access-group,OU=special_groups,OU=technical_stuff,DC=my,DC=corp))"
UserA attempting to bind using dn "CN=John Smith,OU=Y,OU=Users,OU=BranchB,DC=my,DC=corp"
UserA Auth OK for user "UserA"
UserA "Get /auth HTTP/1.0" 200 -

Judging by the error's wording it looks like it fails even to bind to LDAP, but mind you, that my nginx vhost conf contains X-Ldap-BindDN and X-Ldap-BindPass headers configured to use a service account.

oxpa commented

It looks like the first time there was no attempt to bind to the server while the second time the connection is authenticated with "CN=John Smith" username.
Could you please make sure that your configuration has correct username and password to bind to AD?

Here is the line where the search is performed: https://github.com/nginxinc/nginx-ldap-auth/blob/master/nginx-ldap-auth-daemon.py#L238 as you can see it's done with 'scope=subtree'

Yeah, I've grepped the code and found that line as well.
I am sure that the configuration has correct username and password, as the only line in config that was changed between 2 checks is BaseDN.

Besides, the log entry of successful auth basically tells that nginx-ldap-auth used user's input to bind.
If that works - why would I even bother using a special bind account?

oxpa commented

It would help a lot if you could show log entries, ldap records and corresponding configuration with minor (automatically done) substitutions if needed.

The daemon uses bind credentials to find the DN to use with password provided. Most of directories won't allow searching without binding. So you have to bind with preconfigured user, find the user which attempts authentication and then try binding with password provided for the found user.

Already done, see initial post, all the necessary info is there.

Hi, I confirm this issue exists. I have two users, one is part of the OU=STUDENT, and the other is part of OU=STAFF.

If I setup the following:
proxy_set_header X-Ldap-BaseDN "OU=STUDENT,DC=my,DC=corp"; then the student will be found by the query but not the employee.

If I setup the following:
proxy_set_header X-Ldap-BaseDN "OU=STAFF,DC=my,DC=corp"; then the employee will be found by the query but not the student.

if I setup the following:
proxy_set_header X-Ldap-BaseDN "DC=my,DC=corp"; then the query will fail with "Error while running search query: {'desc': 'Operations error', 'info': '000004DC: LdapErr: DSID-0C0907E1, comment: In order to perform this operation a successful bind must be completed on the connection."

If I use ldapsearch via cli the BaseDN "DC=my,DC=corp" does work. This happens only with the nginx-ldap-auth daemon.

In all cases only the X-Ldap-BaseDN line was modified. So I can confirm that the issue exists as reported: "Fails to auth if BaseDN isn't maximally narrow OU"

I have some good news for you Drugoy. To solve the issue simply add the following line to your nginx configuration parameters:

proxy_set_header X-Ldap-DisableReferrals "true";

So your config should look as:

proxy_set_header X-Ldap-URL "ldap://my.corp:389";
proxy_set_header X-Ldap-DisableReferrals "true";
proxy_set_header X-Ldap-BaseDN "DC=my,DC=corp"; # fails to auth both UserA and UserB
proxy_set_header X-Ldap-BindDN "CN=ldap_gatekeeper,OU=Service Accounts,DC=my,DC=corp";
proxy_set_header X-Ldap-BindPass "passwordhere";
proxy_set_header X-Ldap-Template "(&(objectClass=user)(sAMAccountName=%(username)s)(memberOf=CN=access-group,OU=special_groups,OU=technical_stuff,DC=my,DC=corp))"";

This info should be added to the documentation!!

Also check:
https://github.com/nginxinc/nginx-ldap-auth/blob/master/nginx-ldap-auth-daemon.py#L224

@admintechblog oh, man, thank you so much for this solution! It worked!