L
LLDAPโ€ข12mo ago
Trick789

Trick789 - Hi ๐Ÿ™‚ I'm running homeassistant (HA)...

Hi ๐Ÿ™‚ I'm running homeassistant (HA), lldap and the cisco duo authentication proxy (DUO) on k8s. I can't use the example config as I'm using HA > LDAP > DUO > LDAP > LLDAP. It's working beautifully using a a custom auth provider for HA (python script using ldap3 library: https://gist.github.com/yumenohikari/8440144023cf33ab3ef0d68084a1b42f) , but the only thing I can't get right is the filter so that only members of a group cn=ha_rw,ou=groups,dc=example,dc=com can authenticate. I've tried a bunch of flavors for the filter, but the lldap log continues to throw [warn]: Ignoring unknown group attribute ""memberof"" in filter messages. I thought memberof was a person attribute so I loaded up an LDAP browser (Apache DS) but can't find the attribute on either groups or people. But it must work because DUO is also pulling a memberof query to allow certain LLDAP users through and that's not generating a log entry on the LLDAP server. This is the original filter in the script (basically for AD):
FILTER = """ (& (objectClass=person) (| (sAMAccountName={}) (userPrincipalName={}) ) (memberOf=CN=Home Assistant,OU=Security Groups,OU=Accounts,DC=ad,DC=example,DC=com) )"""
and that's what I've attempted to change it to..
FILTER = """ (& (objectClass=person) (| (uid={}) (mail={}) ) (memberOf=CN=ha_hw,ou=groups,dc=example,dc=com) )"""
Any idea what the filter should look like? (running lldap 0.5.1-alpha). Thank you.
Solution:
@nitnelave - hey, just wanted to let you know that with the right filter in place I got it to work.. filter in ldap-auth.py
safe_username = escape_filter_chars(os.environ['username']) FILTER = f"(&(uid={safe_username})(memberOf=cn=ha_rw,ou=groups,dc=example,dc=com))"...
Jump to solution
64 Replies
nitnelave
nitnelaveโ€ข12mo ago
You're right that memberOf is a person attribute. It's also not technically an attribute, which is why the LDAP browser didn't show it (it's also possible that the browser doesn't work well with LLDAP). The reverse direction, for groups, is "member".
Trick789
Trick789OPโ€ข12mo ago
@nitnelave - thanks for the excellent work. love the project! Enabled verbosity and am seeing the following entries..
[debug]: | request.base: "DC=example,DC=com" | scope: Global get_user_list [ 607ยตs | 0.03% / 0.34% ] [debug]: | filters: And([And([]), MemberOf("ha_rw")])
so I take it the filter is working and I can just ignore those warnings
nitnelave
nitnelaveโ€ข12mo ago
The warning might be a false alarm: since your query probably has a base DN of just the DC (without the OU), LLDAP searches both groups and users (although it's not going to find any group with the object class condition)
Trick789
Trick789OPโ€ข12mo ago
oh right, I could finetune the base DN in the script to avoid that.
nitnelave
nitnelaveโ€ข12mo ago
If you can configure the base DN of the request, adding "ou=people" at the beginning should make the warnings go away
Trick789
Trick789OPโ€ข12mo ago
just noticed, after having added a couple of users to the group, that no matter which user I enter in HA, it's trying to login with the first user (alphabetically) in the group. Bummer but that has nothing to do with LLDAP I suppose ๐Ÿ™‚
nitnelave
nitnelaveโ€ข12mo ago
Look at the query sent to LLDAP and make sure it has the login name (you can see it in the verbose logs) When I see:
get_user_list [ 607ยตs | 0.03% / 0.34% ] [debug]: | filters: And([And([]), MemberOf("ha_rw")])
It seems that there's no login name in there
Trick789
Trick789OPโ€ข12mo ago
oh yeh I'm monitoring the logs of all 3 apps while trying to sign in.. HA: authentication failed for user B (the one I'm trying to login with). DUO: passes user A (first user of group hw_rw) on to LLDAP. LLDAP: errors out on user A.
nitnelave
nitnelaveโ€ข12mo ago
I don't see the whole "or" block from your filter The second "And([])" is just "true", that's the object class= person filter, it's always true for users I guess the issue is in the duo configuration
Trick789
Trick789OPโ€ข12mo ago
This is what I'm seeing when DUO checks if a user is allowed to use the service (DUO)
[debug]: | filters: And([And([Or([MemberOf("duo_access")]), Or([And([Not(And([])), Not(And([]))]), And([]), Not(And([]))])]), UserId(UserId("userB"))])
I didn't define the filter, that's all DUO.
nitnelave
nitnelaveโ€ข12mo ago
It's getting hard to read ๐Ÿ˜… can you post the LDAP just above? It has the LDAP filter before processing
Trick789
Trick789OPโ€ข12mo ago
[debug]: | filters: And([And([]), MemberOf("ha_rw")]) list_users [ 509ยตs | 0.26% ] filters: Some(And([And([]), MemberOf("ha_rw")])) | _get_groups: false [debug]: | return: [UserAndGroups { user: User { user_id: UserId("userB"), ..
nitnelave
nitnelaveโ€ข12mo ago
No, I mean a couple of lines above the filter, for the same query, you have the full request And the filter looks different
Trick789
Trick789OPโ€ข12mo ago
gotcha
[debug]: | msg: LdapMsg { msgid: 183, op: SearchRequest(LdapSearchRequest { base: "CN=duo_access,OU=groups,DC=example,DC=com", scope: Base, aliases: Never, sizelimit: 1, timelimit: 0, typesonly: false, filter: Present("objectClass"), attrs: ["objectsid"] }), ctrl: [] }
followed by
[debug]: | request.base: "CN=duo_access,OU=groups,DC=example,DC=com" | scope: Group(Equality("cn", "duo_access"))
nitnelave
nitnelaveโ€ข12mo ago
That's the request for the duo_access group (no idea about the objectsid attribute though) (maybe it's important?)
Trick789
Trick789OPโ€ข12mo ago
I do see that that attr gets resolved?
[debug]: | resolved_attributes: ["objectsid"]
nitnelave
nitnelaveโ€ข12mo ago
That request should return exactly one entry (if the group exists) You don't get a warning for unknown group attribute objectsid?
Trick789
Trick789OPโ€ข12mo ago
Yes I do. Right below that entry objectsid, objectcategory and memberof are all unknown group attributes
nitnelave
nitnelaveโ€ข12mo ago
Right. That's because we don't have those attributes (disregard memberOf) If they're important, you'll have to look into custom attributes
Trick789
Trick789OPโ€ข12mo ago
but uhm, its all working though :p
nitnelave
nitnelaveโ€ข12mo ago
Which we support, we just don't have a UI for it Great, so they're probably not important!
Trick789
Trick789OPโ€ข12mo ago
both the check on group member of the duo_access and ha_ra groups is ๐Ÿ‘ membership* I just think I'm stuck with the limitation of only having a single user in the ha_rw group
nitnelave
nitnelaveโ€ข12mo ago
Okay, so where does it get the names of the members of the group? Since it's sending a query as the first user of the group, it means that it got the list somewhere
Trick789
Trick789OPโ€ข12mo ago
let me have a look
nitnelave
nitnelaveโ€ข12mo ago
(user A and B in your example)
Trick789
Trick789OPโ€ข12mo ago
I'm adding them back in
Trick789
Trick789OPโ€ข12mo ago
Trick789
Trick789OPโ€ข12mo ago
Does a bindrequest with userA while I'm trying to login with userB I'm assuming it's because the request isn't passing on a uid and simply going with a memberOf filter so it just grabs the first user it finds?
nitnelave
nitnelaveโ€ข12mo ago
So, it requests all the members of the group, but gets the display name, that's a bit unusual You'd expect the uid Can I see your duo config?
Trick789
Trick789OPโ€ข12mo ago
ofc but duo's config doesn't query display name the HA script does.. search = conn.search(BASEDN, FILTER, attributes='displayName') if len(conn.entries) > 0: # search is True on success regardless of result size eprint("search success: username {}, result {}".format(os.environ['username'], conn.entries)) user_dn = conn.entries[0].entry_dn user_displayName = conn.entries[0].displayName I guess just to display in the app DUO config:
[ad_client] host=lldap.lldap.svc.cluster.local port=3890 bind_dn=CN=svcduo,OU=people,DC=example,DC=com service_account_username=CN=svcduo,OU=people,DC=example,DC=com service_account_password=### search_dn=OU=people,DC=example,DC=com security_group_dn=CN=duo_access,OU=groups,DC=example,DC=com transport=clear auth_type=plain username_attribute=uid at_attribute=mail
[ldap_server_auto] ikey=### skey=### api_host=###.duosecurity.com port=389 factors=auto failmode=safe
nitnelave
nitnelaveโ€ข12mo ago
Huh, that looks good Maybe add uid and mail to the attributes?
Trick789
Trick789OPโ€ข12mo ago
wouldn't I want to pass the user I'm trying to login with with filter uid=$username? instead*
nitnelave
nitnelaveโ€ข12mo ago
Oh yeah, you can try that
Trick789
Trick789OPโ€ข12mo ago
I'll give that a go tomorrow. Thanks for your help! ๐Ÿ™‚
nitnelave
nitnelaveโ€ข12mo ago
My pleasure!
Solution
Trick789
Trick789โ€ข12mo ago
@nitnelave - hey, just wanted to let you know that with the right filter in place I got it to work.. filter in ldap-auth.py
safe_username = escape_filter_chars(os.environ['username']) FILTER = f"(&(uid={safe_username})(memberOf=cn=ha_rw,ou=groups,dc=example,dc=com))"
DUO
LDAPMessage(id=3, value=LDAPSearchRequest(baseObject=b'ou=people,dc=example,dc=com', scope=2, derefAliases=3, sizeLimit=0, timeLimit=0, typesOnly=0, filter=LDAPFilter_and(value=[LDAPFilter_equalityMatch(attributeDesc=BEROctetString(value=b'uid'), assertionValue=BEROctetString(value=b'userB')), LDAPFilter_equalityMatch(attributeDesc=BEROctetString(value=b'memberOf'), assertionValue=BEROctetString(value=b'cn=ha_rw,ou=groups,dc=example,dc=com'))]), attributes=[b'displayName']), controls=None)
LLDAP
[debug]: | filters: And([UserId(UserId("userB")), MemberOf("ha_rw")]) [debug]: | resolved_attributes: ["displayName"]
Also set BaseDN to "ou=people,dc=example,dc=com" and the event Ignoring unknown group attribute ""memberof"" in filter stops showing up in the logs. Does still show Ignoring unknown user attribute "objectcategory" in filter and Ignoring unrecognized group attribute: objectsid, but whatever ๐Ÿ™‚ I know that in your homeassistant example you have people targeting the graphql API (and that may be the prefered way). Would you think it's also worth adding a section for those who need to use LDAP for certain edge cases? If you do, I wouldn't mind writing something up and sending it to you.
nitnelave
nitnelaveโ€ข12mo ago
So, objectsid and category are good candidates for adding them to the list of ignored attributes: you know that they don't work and it doesn't matter That's kinda the point of that list ๐Ÿ˜„ Oh, but it's in filters, sorry
Trick789
Trick789OPโ€ข12mo ago
Yeah, will add them
nitnelave
nitnelaveโ€ข12mo ago
Does that work with filters? I don't remember And yeah, more documentation is always welcome, there's bound to be someone who's going to try the same thing as you
Trick789
Trick789OPโ€ข12mo ago
the thing is, those events show up while lldap is still dealing with whatever duo is requesting
nitnelave
nitnelaveโ€ข12mo ago
So feel free to add to the existing HA docs
Trick789
Trick789OPโ€ข12mo ago
and I have no control over that
nitnelave
nitnelaveโ€ข12mo ago
Right, I was just talking about silencing the warnings In LLDAP there's a mechanism for saying "don't warn me about this unknown attribute, it's a known unknown" But I don't remember if it works for filters
Trick789
Trick789OPโ€ข12mo ago
I'll add it to the ignore list and will let you know if it does Works for filters ๐Ÿ™‚ added to conf file: ignored_user_attributes = [ "objectcategory" ] ignored_group_attributes = [ "objectsid" ]
nitnelave
nitnelaveโ€ข12mo ago
Great! To be honest, you're the first person who I know uses that feature ๐Ÿคฃ
Trick789
Trick789OPโ€ข12mo ago
the authentication & authorization aspect of my homelab is my biggest annoyance with it ๐Ÿ˜„
nitnelave
nitnelaveโ€ข12mo ago
I hope LLDAP was useful! I remember spending weeks setting up openLdap (it took me several tries, and I probably found the worst tutorials in terms of explaining concepts)
Trick789
Trick789OPโ€ข12mo ago
You have services who offer no user management at all (longhorn offers write access! without authentication to the block storage), services who only offer local users (no external identity providers), then those who do but no ldap, then those who offer OIDC I can count on one hand, etc.
nitnelave
nitnelaveโ€ข12mo ago
Yeah... Gating stuff behind authelia sometimes works You can even sometimes get trusted header support for SSO
Trick789
Trick789OPโ€ข12mo ago
so now I have my reverse proxy forwardAuth to Authelia + LLDAP for those who offer no direct integration with external identity providers and for those with LDAP support (Nextcloud, etc.), I'll skip the Authelia step and offer MFA through DUO+LLDAP (Authelia also goes through DUO before reaching LLDAP for requests coming from non-trusted networks)
nitnelave
nitnelaveโ€ข12mo ago
How does it look like with duo? What's the user flow? For the second factuur Factor Oh, you get a push notification And it just takes super long for duo to respond to the bind request
Trick789
Trick789OPโ€ข12mo ago
It's amazing. DUO offers up to 10 users for free. That plan allows me to create an application for Authelia for that integration and also an LDAP proxy application. The DUO authentication proxy simply runs on my k8s cluster and whenever a request to authenticate a user comes in, it reaches out to the "Cisco managed control plane" API to check 1) if the user is enrolled, 2) which policy it needs to follow, 3) which devices the user has, etc. You can set timeouts at practically every step it's either push, or sms, etc. whatever you define for earch user on the control plane
nitnelave
nitnelaveโ€ข12mo ago
Right. But if the client service expects a bind response in 3s, it'll time out I guess (although no one sets timeouts...)
Trick789
Trick789OPโ€ข12mo ago
Yeah, I set the timeout value in the HA script to 30s
nitnelave
nitnelaveโ€ข12mo ago
A bit tight for sms ๐Ÿ™‚ But push should work Good to know!
Trick789
Trick789OPโ€ข12mo ago
true ๐Ÿ™‚
nitnelave
nitnelaveโ€ข12mo ago
I'll recommend that in the MFA issue (although it'd be nice to have a free option baked in, with just totp )
Trick789
Trick789OPโ€ข12mo ago
The important bit for me is that I can just have an ldap proxy deal with it as long as the app supports the protocol, I'm golden
nitnelave
nitnelaveโ€ข12mo ago
There's one place you can't put MFA: in LLDAP itself As in, your admin login to LLDAP can't be protected Which I why I wanted to have at least support for totp
Trick789
Trick789OPโ€ข12mo ago
How do you usually deal with SSO?
nitnelave
nitnelaveโ€ข12mo ago
Currently, you don't
Trick789
Trick789OPโ€ข12mo ago
I've kind of given up on it seeing how many apps lack support for OIDC
nitnelave
nitnelaveโ€ข12mo ago
There's a request for authelia trusted headers support It "just" needs to be implemented
Trick789
Trick789OPโ€ข12mo ago
ok, I'll keep an eye on that ๐Ÿ™‚ Thanks again for your time! I'll write-up a piece for HA w/ LDAP this week.
Want results from more Discord servers?
Add your server