I'm sure that some of you have dealt with this same problem. I'm hoping that someone has a better answer than what I'm doing now.

So, you've got some users in an LDAP directory, and one day you say "hey! I can authenticate against this thing for SSH!" And it is good.

Then one day you realize you want only certain users to be able to get into a machine. Say, developers should only be able to get into the dev boxes, not prod. You do some googling around and find pam_groupdn which goes in your LDAP config (/etc/ldap.conf) like so:

pam_groupdn CN=developers,OU=groups,DC=yourcompany

And, again, it is good. You make another group for prod, another for QA, etc. Maybe one day you have a second product dev group, so they get their own group. Whatever.

Then one day you've got a server that both developers and QA need to be able to log in to. Uh... It turns out that pam_groupdn doesn't take multiple values. What do you do? Well, if you're not thinking much, you say "oh, I'll just make a developers-and-QA group!".

pam_groupdn CN=developers-and-QA,OU=groups,DC=yourcompany

That's... not good, but it's ok, right?

Then one day you get another developer, and you realize you need to add them to 15 groups because there's developers-and-QA, product1-and-product2, developers-who-have-access-to-prod and so on. Crap.

There's got to be a better way to do this, right? I emailed the developers of pam_ldap, and they said that unfortunately there's no way to make pam_groupdn take multiple values (without hacking around in the code).

Anyone want to share how they manage groups-of-groups in LDAP without resorting to copy/paste?


If you're using NSS you can set the AllowGroups parameter in /etc/ssh/sshd_config. If not, you should look into dynamic lists:

dn: cn=devandqa,ou=groups,dc=company,dc=com
cn: devandqa
objectClass: groupOfNames
labeledURI: ldap:///ou=groups,dc=company,dc=com?memberUid?one?(|(cn=developers)(cn=qa))

Something like that. You also need to include the schema and include the overlay, etc.... So it's a start.

| improve this answer | |
  • ... Oh! Why didn't I think of AllowGroups? That's pretty clean. I'll wait for better answers (where "better" == "what I was thinking of"), but I think you've got it. – Bill Weiss Sep 14 '10 at 18:00

A working solution is to apply a pam_filter instead of pam_groupdn in /etc/ldap.conf:

# Filter to AND with uid=%s
pam_filter |(member=CN=developers,OU=groups,DC=yourcompany)(member=CN=QA,OU=groups,DC=yourcompany)

"Filter to AND with uid=%s" means the Filter sent to the LDAP-Server will be like this: &(uid=<your username>)(<your pam_filter>)

The user has to be member of developers or QA. If he is not member of one of these groups, there is an error message in the response from the LDAP server and the password will not even be sent to the LDAP-Server.

Works for my RHEL 5.8 Environment.

For different filter variations, check google for "ldap filter syntax"...

| improve this answer | |
  • That's a great answer! It would have done what I was looking for. – Bill Weiss Jun 11 '14 at 16:05

My solution here is pretty ugly, but slightly less ugly than what you describes above: the group I specify in pam_groupdn is a "machine-class" group, e.g. cn=database,ou=NY,dc=mydomain,dc=com

The groups qualify who should have access to a specific class of systems at a specific site (in the above example, database servers in NY).

This only really works by virtue of the fact that in my environment granting a user access to one machine in a particular class is pretty much pointless (because they're all effectively identical), so if I'm letting someone log into db01 there's no reason they shouldn't also be allowed to log in to db02. (The "site" qualification is born of the logic that I may hire someone to manage California, but I may not want them logging in to NY's machines.)

This design does involve some duplication in the groups which makes globally revoking access more difficult (e.g. my account is listed in every group at every site: To lock my user out you would have to edit every group and revoke my membership), but it gives the groups a logical/manageable structure that only grows when new sites/machine classes are added.

| improve this answer | |
  • I think that's functionally what I'm doing, even though your naming scheme is prettier :) – Bill Weiss Sep 14 '10 at 17:59
  • Functionally equivalent, but with a deterministic bound for the number of groups :) – voretaq7 Sep 14 '10 at 18:12

Assuming you have some configuration management system like puppet you can devise a PAM-based solution (so it is more general than AllowGroups in sshd_config) by using pam_listfile. Have puppet manage something like /etc/allowed_groups for each server/server group and add to your PAM config:

required pam_listfile.so onerr=fail item=group sense=allow file=/etc/allowed_groups

Like the SSH solution it assumes you have LDAP wired into NSS.

| improve this answer | |
  • Not a bad idea. I think I'd prefer to push my sshd_config changes through Puppet instead of that file, but that's personal choice. – Bill Weiss Sep 14 '10 at 18:26

If you are using RHEL6+ the conf file is located at /etc/pam_ldap.conf and not /etc/openldap/ldap.conf. If you put the pam_groupdn entry in there then it will work as designed. The best way to go about it would be to copy your ldap.conf to pam_ldap.conf or create a symbolic link.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.