Active Directory Nested Groups

active_directory

(Brad Baker) #1

I am trying to get active directory authentication working with nested groups. What I mean by that is that I have the following groups setup:

Sonar Users
  |- Developers
      |- User1
      |- User2
  |- SysAdmins
     |- User3
     |- User4

If I place User1/2/3/4 directly in the “Sonar Users” group everything works as expected. However If I try to place the “Developers” and “SysAdmins” group inside the “Sonar Users” group authentication won’t work.

How can I get nested ad groups working? Here’s what I have in my config file:

# Distinguished Name (DN) of the root node in LDAP from which to search for users (mandatory)
ldap.user.baseDn=DC=mydomain,DC=local

# LDAP user request. (default: (&(objectClass=inetOrgPerson)(uid={login})) )
ldap.user.request=(sAMAccountName={0})

# Attribute in LDAP defining the user’s real name. (default: cn)
ldap.user.realNameAttribute=cn

# Attribute in LDAP defining the user’s email. (default: mail)
ldap.user.emailAttribute=mail

# GROUP MAPPING

# Distinguished Name (DN) of the root node in LDAP from which to search for groups. (optional, default: empty)
ldap.group.baseDn=CN=SonarQube Users,OU=Groups,DC=domain,DC=local

# LDAP group request (default: (&(objectClass=groupOfUniqueNames)(uniqueMember={dn})) )
ldap.group.request=(&(objectClass=group)(member={dn}))

ldap.group.idAttribute=sAMAccountName

# Support Active Directory Nested Groups
ldap.windows.compatibilityMode = true

What I see on the web console is: Authentication failed.

What I see in the logs is:

2019.01.02 20:10:14 TRACE web[AWgQII7LjH452GK4AAA6][sql] time=0ms | sql=SELECT u.id as id, u.uuid as uuid, u.login as login, u.name as name, u.email as email, u.active as "active", u.scm_accounts as "scmAccounts", u.salt as "salt", u.crypted_password as "cryptedPassword", u.hash_method as "hashMethod", u.external_id as "externalId", u.external_login as "externalLogin", u.external_identity_provider as "externalIdentityProvider", u.user_local as "local", u.is_root as "root", u.onboarded as "onboarded", u.homepage_type as "homepageType", u.homepage_parameter as "homepageParameter", u.organization_uuid as organizationUuid, u.created_at as "createdAt", u.updated_at as "updatedAt" FROM users u WHERE u.login=? AND u.active=true | params=myusername
2019.01.02 20:10:14 DEBUG web[AWgQII7LjH452GK4AAA6][o.s.p.l.LdapUsersProvider] Requesting details for user myusername
2019.01.02 20:10:14 DEBUG web[AWgQII7LjH452GK4AAA6][o.s.p.l.LdapSearch] Search: LdapSearch{baseDn=DC=domain,DC=local, scope=subtree, request=(sAMAccountName={0}), parameters=[myusername], attributes=[mail, cn]}
2019.01.02 20:10:14 DEBUG web[AWgQII7LjH452GK4AAA6][o.s.p.l.LdapContextFactory] Initializing LDAP context {java.naming.provider.url=ldap://domain-ad1.domain.local:389, java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory, java.naming.security.principal=CN=sonarqube,OU=Special Accounts,OU=AWS,DC=domain,DC=local, com.sun.jndi.ldap.connect.pool=true, java.naming.security.authentication=simple, java.naming.referral=follow}
2019.01.02 20:10:25 TRACE web[][sql] time=0ms | sql=select id, data from notifications order by id asc limit ? | params=1
2019.01.02 20:11:25 TRACE web[][sql] time=1ms | sql=select id, data from notifications order by id asc limit ? | params=1
2019.01.02 20:12:25 TRACE web[][sql] time=1ms | sql=select id, data from notifications order by id asc limit ? | params=1
2019.01.02 20:12:32 TRACE web[][sql] time=1ms | sql=select uuid, doc_type as docType, doc_id as docId, doc_id_type as docIdType, doc_routing as docRouting, created_at as createdAt from es_queue where created_at <= ? order by created_at desc limit ? | params=1546459652929, 10000
2019.01.02 20:13:25 TRACE web[][sql] time=0ms | sql=select id, data from notifications order by id asc limit ? | params=1
2019.01.02 20:14:25 TRACE web[][sql] time=0ms | sql=select id, data from notifications order by id asc limit ? | params=1

Can anyone assist me?

Thanks!
Brad


(Nicolas Bontoux) #2

Hi Brad,

Don’t forget to mention which version of SonarQube you’re using and (if manually installed) relevant plugin version.

This property is deprecated since quite a while, and as far as I can remember was unrelated to nested groups behaviour. Don’t know where you got it from, but I believe you can (and should) remove it.

More generally regarding the support of nested groups, I had to dig this topic a year ago and here were the conclusions (long form so that you have all the context):

I confirm that SonarQube does not support nested (hierarchical) groups. All in all you cannot define (in AD) users in a group A that is part of another group B and expect that (in SonarQube) users, on top of joining group A, will also join group B. (To this respect, group B definition is of no use in SonarQube, unless users are directly attached to that group in AD).

Generally speaking the Group Mapping feature of the LDAP Plugin all boils down to this one request that is issued by the Plugin when user logs in: ldap.group.request . There’s no extra logic from an LDAP Plugin perspective, the rest of it is pure LDAP considerations. As far as I know simple LDAP requests search only within direct attributes (hence why ‘parent’ groups are not processed), however do note that this is a pure LDAP/AD consideration (independent from SonarQube), and I don’t have the LDAP/AD mastery to conclude on whether further search capabilities exists.

So all in all:

  • no specific support of nested group in LDAP Plugin
  • to know which groups a user belongs to, the LDAP Plugin looks into results of the ldap.group.request search query.
  • whether such query can return inherited information (e.g. nested groups) is an LDAP/AD consideration, and you would have to consult AD experts (independently of SonarQube) to see if there are specific capabilities there (also depending on your setup/configuration)

Hope that helps!


(Brad Baker) #4

I got this working with nested groups.

For future reference this is what worked for me. I’m using SonarCube 7.5.

(If you’re using an older or newer version it should go without saying your mileage may vary. Obviously also update these values to suit your particular environment)

#--------------------------------------------------------------------------------------------------
# LDAP CONFIGURATION

# Enable the LDAP feature
sonar.security.realm=LDAP

# Set to true when connecting to a LDAP server using a case-insensitive setup.
sonar.authenticator.downcase=true

# URL of the LDAP server. Note that if you are using ldaps, then you should install the server certificate into the Java truststore.
ldap.url=ldap://yourdc.yourdomain.local:389

# Bind DN is the username of an LDAP user to connect (or bind) with. Leave this blank for anonymous access to the LDAP directory (optional)
ldap.bindDn=CN=sonarqube,OU=Users,DC=yourdomain,DC=local

# Bind Password is the password of the user to connect with. Leave this blank for anonymous access to the LDAP directory (optional)
ldap.bindPassword=your-super-secret-password

# Possible values: simple | CRAM-MD5 | DIGEST-MD5 | GSSAPI See http://java.sun.com/products/jndi/tutorial/ldap/security/auth.html (default: simple)
ldap.authentication=simple

# Distinguished Name (DN) of the root node in LDAP from which to search for users (mandatory)
ldap.user.baseDn=DC=yourdomain,DC=local

# LDAP user request. (default: (&(objectClass=inetOrgPerson)(uid={login})) )
ldap.user.request=(sAMAccountName={0})

# Attribute in LDAP defining the user’s real name. (default: cn)
ldap.user.realNameAttribute=cn

# Attribute in LDAP defining the user’s email. (default: mail)
ldap.user.emailAttribute=mail

# GROUP MAPPING

# Distinguished Name (DN) of the root node in LDAP from which to search for groups. (optional, default: empty)
ldap.group.baseDn=CN=SonarQube Users,OU=Groups,DC=yourdomain,DC=local

# LDAP group request (default: (&(objectClass=groupOfUniqueNames)(uniqueMember={dn})) )
ldap.group.request=(&(objectClass=group)(member={dn}))

ldap.group.idAttribute=sAMAccountName

Hope this helps someone else!

Thanks
Brad


(Nicolas Bontoux) #5

Brad, thanks for the update. I might be missing something here, but I can’t see the actual difference between the config you pasted in your initial post, and the config from your latest post where you say it’s resolved. Specifically ldap.user.baseDn, ldap.user.request, ldap.group.baseDn, ldap.group.request all seem to be identical.

Can you shed some more lights on what specific change got it to work with nested groups on your end?

Thanks for the clarifications, especially as this may help other AD users indeed.


(Brad Baker) #6

To be entirely honest I’m not sure what fixed it. It may have been upgrading to 7.5, it may have been removing ldap.windows.compatibilityMode = true.

Or possibly another setting I adjusted which I didn’t include in my original post. I simply “fiddled” with it until I got it to work. Thus the reason for posting the entire config versus a snippit for others to reference.