Support for LDAP (Active Directory) Security Groups

Just starting to use Percona MongoDB - I have AD LDAP set up and can authenticate users.  What I haven’t found is information relating to using LDAP/AD Security Groups to grant authorization to the database.  Can this be done, or do I need to manually add each user to the database?  Not a big deal in this case as it’s only a couple of software developers and one user, but this is definitely a security nightmare if we can’t do group based authorization.

Thanks!

Hi @mdiorio
Just wanted to check you had seen these blog posts and webinars, and that the answer isn’t held within for you:

I admit that after a quick look I didn’t find anything that explicitly referred to AD Security Groups, so I have also posted the question to our product team, hopefully they’ll update. 

I just had a reply from the team and the good news is that this is supported in the latest release of Percona Server for MongoDB https://www.percona.com/doc/percona-server-for-mongodb/LATEST/release_notes/4.2.5-5.html
There are some limitations for that release as mentioned in the release notes, but otherwise our software maps to that MongoDB® implementation, which includes support for LDAP/AD Security Groups. 
Hope this helps ::smile:

Does this hold true for PSMDB too?  I’d really love to see a KB article around this and it’s a bit of a headache trying to determine which MDB documents match with features in PSMDB.  

With LDAP authorization, user creation and management occurs on the LDAP server. MongoDB requires creation of roles on the admin database, with the name of each role exactly matching a LDAP group Distinguished Name (DN). This is in contrast to MongoDB managed authorization, which requires creating users on the $externaldatabase.

Hi @mdiorio,
Yes, when you use LDAP authorization you don’t need to create users in the $external db. This is true for PSMDB too. You still use $external as authentication db but no need to create users there.
General description of LDAP authorization is here: https://docs.mongodb.com/manual/core/security-ldap-external/
Example of using AD for LDAP authorization is here: https://docs.mongodb.com/manual/tutorial/authenticate-nativeldap-activedirectory/
LDAP authorization is supported in PSMDB versions 4.2.5-5 , 4.0.18-11 , and will be supported in upcoming version based on 3.6.18

Hi @"Igor Solodovnikov"

So I think I have things configured correctly, but I can't get this to work. I think it may be due to the comma in the DN that needs to be escaped but it may not be happening.

``` security: authorization: enabled ldap: transportSecurity: none servers: ldap.internal.domain.com bind: queryUser: "CN=SVC_LDAP_RO,OU=General,OU=Service,OU=Accounts,DC=internal,DC=domain,DC=com" queryPassword: "password" userToDNMapping: >- [ { match: "(.+)", ldapQuery: "dc=internal,dc=domain,dc=com??sub?(&(objectClass=person)(sAMAccountName={0}))" } ] authz: # queryTemplate: "dc=internal,dc=domain,dc=com??sub?(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={USER}))" queryTemplate: "{USER},memberOf,base"
<p>No matter what I do, it doesn't seem to work.</p><p>With either queryTemplate, I get: "QUERY [js] Error: LDAP search failed with error: Operations error".</p><p>When I query from the console using ldapsearch, both of these return proper groups:</p><p>ldapsearch -LLL -h ldap.internal.domain.com -x -D "CN=SVC_LDAP_RO,OU=General,OU=Service,OU=Accounts,DC=internal,DC=domain,DC=com" -w password -b "DC=internal,DC=domain,DC=com" "(&amp;(objectClass=person)(distinguishedName=CN=LName\5C, FName,OU=HQ,OU=Users,OU=Accounts,DC=internal,DC=domain,DC=com))" memberOf</p><p><br></p><p>ldapsearch -LLL -h ldap.internal.domain.com -x -D "CN=SVC_LDAP_RO,OU=General,OU=Service,OU=Accounts,DC=internal,DC=domain,DC=com" -w password -b "DC=internal,DC=domain,DC=com" "(&amp;(objectClass=Group)(member:1.2.840.113556.1.4.1941:=CN=LName\\\\, FName,OU=HQ,OU=Users,OU=Accounts,DC=internal,DC=domain,DC=com))"</p><p>In both these queries, you have to escape using either 4 backslashes or \5C before the comma in the CN. I have a feeling that it's passing LName\, FName into the group query and not escaping it properly.</p><p>Is there a way to actually get this to work that you've used?</p>

Hi @mdiorio,
Thank you for this detailed question. I tried to reproduce this issue. Here are my steps:
1. I created a user with comma in its DN, common name of this user is “ln, fn”. DN name looks like this in “ADSI edit” utility: "CN=ln, fn,CN=Users,DC=engineering,DC=example,DC=com"
2. I also assigned userPrincipalName attribute to “lnfn@engineering.example.com”.
3. My config file is here:

security:
authorization: enabled
ldap:
userToDNMapping: &gt;-
[
{
match: "(.+)",
ldapQuery: "dc=example,dc=com??sub?(&amp;(objectClass=organizationalPerson)(userPrincipalName={0}))"
}
]
bind:
queryUser: "CN=alice,CN=Users,DC=engineering,DC=example,DC=com"
queryPassword: "alice"
authz:
queryTemplate: "{USER}?memberOf?base"
setParameter:
authenticationMechanisms: PLAIN,SCRAM-SHA-256,SCRAM-SHA-1

  1. Now I start mongo shell with these parameters:
./mongo -u "lnfn@engineering.example.com" -p --authenticationDatabase '$external' --authenticationMechanism 'PLAIN'

and it succesfully authenticates to the server
5. Here is what I see in the server log:

2020-05-14T14:54:07.012+0300 D1 ACCESS [conn1] Getting user lnfn@engineering.example.com@$external from disk
2020-05-14T14:54:07.012+0300 D1 ACCESS [conn1] Parsing LDAP URL: ldap://192.168.79.154:389/dc=example,dc=com??sub?(&amp;(objectClass=organizationalPerson)(userPrincipalName=lnfn@engineering.example.com)); dn: dc=example,dc=com; scope: 2; filter: (&amp;(objectClass=organizationalPerson)(userPrincipalName=lnfn@engineering.example.com))
2020-05-14T14:54:07.013+0300 D1 ACCESS [conn1] Parsing LDAP URL: ldap://192.168.79.154:389/CN=ln\, fn,CN=Users,DC=engineering,DC=example,DC=com?memberOf?base; dn: CN=ln\, fn,CN=Users,DC=engineering,DC=example,DC=com; scope: 0; filter: nullptr
2020-05-14T14:54:07.014+0300 I COMMAND [conn1] command admin.$cmd appName: "MongoDB Shell" command: isMaster { isMaster: 1, saslSupportedMechs: "$external.lnfn@engineering.example.com", hostInfo: "u64vm:27017", client: { application: { name: "MongoDB Shell" }, driver: { name: "MongoDB Internal Client", version: "4.2.3" }, os: { type: "Linux", name: "Ubuntu", architecture: "x86_64", version: "16.04" } }, $db: "admin" } numYields:0 reslen:282 locks:{ ReplicationStateTransition: { acquireCount: { w: 1 } }, Global: { acquireCount: { r: 1 } }, Database: { acquireCount: { r: 1 } }, Collection: { acquireCount: { r: 1 } }, Mutex: { acquireCount: { r: 1 } } } storage:{ data: { bytesRead: 2708, timeReadingMicros: 3 } } protocol:op_query 2ms
2020-05-14T14:54:07.015+0300 D1 ACCESS [conn1] Parsing LDAP URL: ldap://192.168.79.154:389/dc=example,dc=com??sub?(&amp;(objectClass=organizationalPerson)(userPrincipalName=lnfn@engineering.example.com)); dn: dc=example,dc=com; scope: 2; filter: (&amp;(objectClass=organizationalPerson)(userPrincipalName=lnfn@engineering.example.com))
2020-05-14T14:54:07.019+0300 D1 ACCESS [conn1] Returning user lnfn@engineering.example.com@$external from cache
2020-05-14T14:54:07.019+0300 I ACCESS [conn1] Successfully authenticated as principal lnfn@engineering.example.com on $external from client 127.0.0.1:41518

So it looks like comma in the DN is not source of this “Operations error” message.I am not sure why you observe this error message, but one suspicious thing I noticed in your config is that you use sAMAccountName attribute in the ldapQuery but not in your examples with ldapsearch utility. Please try to execute ldapsearch with query based on sAMAccountName attribute.
Another issue in your config file is this line:

queryTemplate: "{USER},memberOf,base"

correct syntax of this query is with question marks:

queryTemplate: "{USER}?memberOf?base"