all passwords are accepted if user exists
m0se opened this issue · 14 comments
What could cause a situation like this? I successfully set up nginx-ladp-auth on a kubernetes cluster and everything seemed to work, random madeup usernames and passwords got rejected and existing users can auth and get to the backend-site.
But now I discovered, that any random password is accepted as long as the user exists.
How can I best debug this? Where/How are passwords checked against ldap?
You can debug using the command-line client:
ldapsearch -H ${servers[*]} -D ${auth.bindDN} -w ${auth.bindPW} -b ${user.baseDN|group.baseDN} ${user.filter|group.filter}
To validate the user password instead of the admin you place the user's DN after -D
and the password after -w
arguments.
Ok, I checked with ldapsearch
and there seems to be no issue with ldap itself:
with correct pw:
kubeadm@ClusterAdmin:~$ ldapsearch -H ldap://ldap.<domain>.intra:389 -D uid=wtesch,ou=Others,ou=People,dc=<domain>,dc=com -w abc123 -b ou=People,dc=<domain>,dc=com "(uid={0})"
# extended LDIF
#
# LDAPv3
# base <ou=People,dc=<domain>,dc=ch> with scope subtree
# filter: (uid={0})
# requesting: ALL
#
# search result
search: 2
result: 0 Success
# numResponses: 1
with wrong pw:
kubeadm@ClusterAdmin:~$ ldapsearch -H ldap://ldap.<domain>.intra:389 -D uid=wtesch,ou=Others,ou=People,dc=<domain>,dc=com -w wRongPw -b ou=People,dc=<domain>,dc=com "(uid={0})"
ldap_bind: Invalid credentials (49)
but when I call the nginx-ldap-auth instance directly with curl, I get the following results:
no credentials supplied (as expected)
kubeworker1 ~ # curl -v 10.233.9.49:5555
* Rebuilt URL to: 10.233.9.49:5555/
* Trying 10.233.9.49...
* TCP_NODELAY set
* Connected to 10.233.9.49 (10.233.9.49) port 5555 (#0)
> GET / HTTP/1.1
> Host: 10.233.9.49:5555
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Www-Authenticate: Basic realm="LDAP Login"
< Date: Sat, 23 Mar 2019 10:05:54 GMT
< Content-Length: 0
<
* Connection #0 to host 10.233.9.49 left intact
not existing user supplied (as expected):
kubeworker1 ~ # curl -v 10.233.9.49:5555 -u "idontexist:qwert"
* Rebuilt URL to: 10.233.9.49:5555/
* Trying 10.233.9.49...
* TCP_NODELAY set
* Connected to 10.233.9.49 (10.233.9.49) port 5555 (#0)
* Server auth using Basic with user 'idontexist'
> GET / HTTP/1.1
> Host: 10.233.9.49:5555
> Authorization: Basic aWRvbnRleGlzdDpxd2VydA==
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
* Authentication problem. Ignoring this.
< Www-Authenticate: Basic realm="LDAP Login"
< Date: Sat, 23 Mar 2019 10:09:07 GMT
< Content-Length: 0
<
* Connection #0 to host 10.233.9.49 left intact
user with correct pw (as expected):
kubeworker1 ~ # curl -v 10.233.9.49:5555 -u "wtesch:abc123"
* Rebuilt URL to: 10.233.9.49:5555/
* Trying 10.233.9.49...
* TCP_NODELAY set
* Connected to 10.233.9.49 (10.233.9.49) port 5555 (#0)
* Server auth using Basic with user 'wtesch'
> GET / HTTP/1.1
> Host: 10.233.9.49:5555
> Authorization: Basic d3Rlc2NoOmFiYzEyMw==
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sat, 23 Mar 2019 10:11:11 GMT
< Content-Length: 0
<
* Connection #0 to host 10.233.9.49 left intact
now the same user with a wrong pw (unexpected!!):
kubeworker1 ~ # curl -v 10.233.9.49:5555 -u "wtesch:wRongPW"
* Rebuilt URL to: 10.233.9.49:5555/
* Trying 10.233.9.49...
* TCP_NODELAY set
* Connected to 10.233.9.49 (10.233.9.49) port 5555 (#0)
* Server auth using Basic with user 'wtesch'
> GET / HTTP/1.1
> Host: 10.233.9.49:5555
> Authorization: Basic d3Rlc2NoOndSb25nUFc=
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sat, 23 Mar 2019 10:12:30 GMT
< Content-Length: 0
<
* Connection #0 to host 10.233.9.49 left intact
The only thing I could think of that could cause this might be that my LDAP also allows anonymous read, but I assume the nginx-ldap-auth tries to explicitly bind or auth with the given user and does not fall back to anonymous and succeeds when it's just able to find the user.
Additionally I can confirm, that group membership works, when the user is not in the required group I get also a 401. But when the user is in the required group I always get 200 no matter what pw I supply.
I have not tested over an anonymous permissive LDAP, if you don't mind could you try conn.Bind(username, password)
over correct and incorrect credentials in order to see whether or not it returns an error
? You can create your own code based on files ldap/login.go and ldap/pool.go.
Sorry, I am not exactly sure how you mean, I'm not really fluent in go. I'm able to modify, build and run the code and have added some log output in ldap/login.go :
func (p *Pool) Validate(username, password string) (bool, error) {
log.Printf(username)
log.Printf(password)
that one prints the user DN & pw
In rule/service.go I added below line 55:
if ok && err == nil {
log.Printf("ok, but err == nil")
}
if ok && err != nil {
log.Printf("ok, but err pressent")
log.Printf(err.Error())
}
And this is what I got when testing:
With wrong pw:
2019/03/23 15:47:43 uid=wtesch,ou=Others,ou=People,dc=<domain>,dc=ch
2019/03/23 15:47:43 yxcvb
2019/03/23 15:47:43 ok, but err pressent
2019/03/23 15:47:43 LDAP Result Code 49 "Invalid Credentials":
with correct pw:
2019/03/23 15:47:58 uid=wtesch,ou=Others,ou=People,dc=<domain>,dc=ch
2019/03/23 15:47:58 abc123
2019/03/23 15:47:58 ok, but err == nil
So it seems with the correct pw err
is nil, but with the wrong pw err
contains a error message about invalid credentials. From what I can tell from the control structure the case ok==true && err != nil
is not handeld and this would be the user-exist-but-pw-wrong case.
By changing &&
to ||
in rule/service.go line 53 I get the expected behavior. 401 for existing User with wrong pw and 200 from existing User with correct.
Could please you have a look if this is actually a bug? I don't have to much of a idea what I'm doing and if changing this AND to OR would break something else. Thanks!
Hi @tiagoapimenta,
I submitted the PR, please review, hopefully this simple fix should address this issue.
Thanks !
Sorry @tlvenn , the first bool
represents whether or not the operation was successfully executed, the error
represents if the login and password is correct or not depending on being null or not. I cannot accept your MR.
When I have a time off I will check why I put an &&
instead of an ||
in rule/service.go
line 53 as @m0se quoted and if it should be the correct fix or not.
I guess is the return is valid as far as your api is designed then yes the issue need to be fixed at the location pointed out by @m0se. Imho, from a design point of view, it's weird that a validate credentials function return true when the credentials are actually wrong.
The boolean being false indicates an internal error, some network issue or wrong admin validation (required to query users), it's a go pattern to indicates on a boolean whether or not the other argument is valid, in that case the argument is an error
itself, but could be two booleans for instance.
@m0se can you answer me if is there a message Network problem, trying to reconnect once:
being printed on console by any chance?
Could you test version 1.0.5
please? It could be a issue where there is no group.
Thanks for fixing! As far as I can tell I now get the expected behavior. I cloned the 1.0.5 repo, built the docker image without modifying anything and now existing user with wrong passwords get rejected with my permissiv LDAP setup.
As for the console output I get, the mentioned network error does not appear. That's all I get after a few failed and successful login tests:
kubeworker3 ~ # docker logs -f 0c9e33637dc9
Loaded config "/etc/nginx-ldap-auth/config.yaml".
2019/03/26 07:51:10 It was not possble to start TLS, falling back to plain: LDAP Result Code 200 "Network Error": TLS handshake failed (EOF).
Serving...
I assume the TLS error is to be expected since I use plain LDAP
..or did you want me to produce a situation where the LDAP actually is not reachable, too see if the mentioned Network Error is printed?
No need to deeper research, I believe that is enough, thank you for your support
Still experiencing this issue (passwordless login with existing users) in v1.0.5 and v1.0.6
I am correctly rejected when using an explicitly bad password with an existing user, but an empty string for password lets me in regardless. Going to try to dig in and see where this is coming from.