webmeshproj / webmesh-vdi

A Kubernetes-native Virtual Desktop Infrastructure

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

LDAP Authentication Issues

caduceus4 opened this issue · comments

I like kvdi, thanks!

It appears that when using LDAP authentication, you are using a filter to search for attributes cn,dn,uid, memberOf, and accountStatus. That appears to be ok for OpenLDAP, but not for FreeIPA (my LDAP server) and probably not for ActiveDirectory either. (I could be wrong, as I only have direct access to FreeIPA).

In ./kvdi-main/pkg/auth/providers/ldap/authenticate.go, you do:

if strings.ToLower(user.GetAttributeValue("accountStatus")) != "active" {
return nil, fmt.Errorf("User account %s is disabled", user.GetAttributeValue("uid"))
}
This is the error I am getting in the logs for the app, and I verified that you are querying for accountStatus via wireshark.

As accountStatus does not exist in freeIPA, I cannot use kvdi with FreeIPA.

Perhaps just binding as the user in FreeIPA, is enough. Disabled users cannot bind as themselves. An entry in values.yaml to skip the accountStatus check may be good enough

More generally, it would be good to have the ability to specify the attributes you will query, perhaps as an entry in values.yaml. For example, with Active Directory the user attribute is (or was) sAMAccountName, not uid.

More generally, it would be good to have the ability to specify the attributes you will query, perhaps as an entry in values.yaml. For example, with Active Directory the user attribute is (or was) sAMAccountName, not uid.

This seems like a good idea to do to ensure compatibility. I'll take a swing at it over the weekend.

Perhaps just binding as the user in FreeIPA, is enough. Disabled users cannot bind as themselves. An entry in values.yaml to skip the accountStatus check may be good enough

It seems a good idea to check if the user is disabled. I can see maybe offering an insecure option to skip the check entirely, but just out of curiosity does FreeIPA not have any way to determine if an account is disabled?

Actually in looking over things briefly I think all-in it's a pretty low effort and can probably do it today. I'm realizing I don't have a good way for people to test PRs (specifically in cases like this, when the CRD needs to change), so I'll probably end up just doing a quick PR to myself and then tagging a release for you to try out.

@caduceus4 When this build finishes you'll be able to try out the latest version (v0.1.2) to see if it addresses these issues. Let me know if it works okay for you and I'll close the issue. See the PR or the docs for the new fields.

I'd be happy to test freeIPA for you. A little googling on my part shows that there is an operational attribute called nsAccountLock for freeIPA that would give you what you need. It can be TRUE, FALSE, or non-existent. But playing with my server, it appears the attribute doesn't exist at all until the user is disabled. Once disabled, the value is TRUE. Then upon re-enable, the value is FALSE. But if the user has never been disabled, the attribute does not exist. So the only thing you can rely on is existence and TRUE means that the account is disabled.

As such for freeIPA, your query would be something like:

if nsAccountLock exists in your initial query response, AND it is true, report disabled error. If it does not exist, assume nothing about the user, and move to the bind.

Here is the info on nsAccountLock: https://auth.docs.cern.ch/freeipa/freeipa-attribute-tables/

Hrm. To be fair it's been years since I had to manage any sort of LDAP environment myself. That by itself is very different than how OpenLDAP (and I believe AD) does it, and makes me wonder if I should take out that check entirely. If not allowing disabled users to bind is something I can rely on every LDAP server to do then kvdi shouldn't be bothered with it. My mindset at the time was me not being sure if that's the case.

Now that I'm researching it myself it seems AD will also not allow the user to bind, but there were instances in the past of bugs that made it possible unintentionally.

After the build passes on that one I'll merge it and do another tag v0.1.3 without the check entirely.

Keeping the disabled check does reduce the exposure of the password in the clear when not doing TLS/SSL. But that's minimal to doing the queries in the clear when things work anyway. So either way (doing the check with nsAccountLock, or not at all), is probably ok as long as all three systems (OpenLDAP, freeIPA, and AD) behave appropriately when bind()ing to a user that is disabled\

i'll check back later today for the 0.1.3 tag

At the very least 0.1.2 with insecureSkipStatusCheck set to true (and the other fields you need to override) has the meat of what was causing you issues.

I actually just did some tests on disabled users with glauth (what I use locally for testing LDAP stuff) against that other branch, and it actually lets the disabled user bind...which is now making me wonder if this is why I had the check there in the first place. It would suck if I can't verify that there, but if actual production LDAP servers do their job correctly it may still be okay.

I'm thinking what I might do on this other branch is flip it around. By default the check doesn't happen, but in the event you have an additional arbitrary field (e.g. accountStatus) that you want to verify before binding, you can opt-in and provide it.

Sounds like you get to file a bugreport for glauth ;-). I like the opt-in approach

Lol I wasn't sure if I should bother. It's pretty concise in their documentation:

disabled
    Specify if account is active.
    Set to 'true' (without quotes) to make the LDAP entry add 'AccountStatus = inactive'

Which makes me think they are well aware of the fact that the only thing they do with that field is set the LDAP attribute. My bigger question is why...

Should be good to go. Could probably even just override your app image if you want, but otherwise a helm upgrade or reinstall will do.

ok, it works! I am able to login using ldap creds. Thanks! the docs say tlsCACert is the base64 of the CA cert for the ldap server. So I have the pem encoded base64 cert there for the CA? I tried that and can't get it to verify. Or do you mean that tlsCACert is the name of the key in the secret that has the base64 as the value?

Nah that should just be the base64 encoded PEM cert. In looking at the code real quick I'm realizing I may not be handling chains well (for LDAP or Oauth). If it's an intermediate AND a CA you are needing to provide, I might need to make another fix for that.

EDIT: I take that back, it might be handling those fine. Are you able to share the error it is giving you? Perhaps it's upset because of a DNS name mismatch or something?

I just did a test using TLS on glauth and everything checked out. I used a leaf signed by an intermediate and I did need to include both the intermediate and the CA that signed it in tlsCACert (which makes me wonder if it should be renamed tlsCACertChain). That being said, I'm still happy to try to help figure out what might be causing the issue on your end.

"LDAP Result Code 200 "Network Error": x509: certificate signed by unknown authority" This what I am getting in the mgr log when I specify my tlsCACert as a single string of the base64 of my cert (from the pem file, joined as 1 string, but without the -----BEGIN and -----END CERTIFICATE-----

If I alter the base64 string by a single character, the error is invalid base64. my CA is a private one and I have verified that the string I am using is correct.

You need the BEGIN and END of the PEM block as well. If it's getting it all onto one line that is the problem, yaml has the "|" syntax for strings that works here, or you can do something similar to cat ca.crt | base64 --wrap=0 which will squash new lines. The fact that it let you get that far means it should probably check the validity of that sooner, or I'm not handling an error condition correctly.