Apache 'Require ldap-group' Limitation

The problem, briefly: Apache configured to authenticate via LDAP and authorize access only to members of a certain group, would not authorize a new user account that was clearly a member of that group.

The solution, briefly: The new user account had its primary group identifier (GID) set to the authorized group, while all other users were auxiliary members. The new user account had to be given an explicit memberUid entry within the group’s LDAP definition.

The longer story

I and the rest of our team use our regular accounts for both normal and administrative work on our Unix machines, resorting to separate admin accounts on Windows only. In the Unix environment, we’re members of the group admins, that grants us the rights to log into infrastructure machines and invoke sudo when necessary.

Our documentation wiki contains some sensitive information, so we require that wiki visitors also be a member of the admins group. We use Apache’s mod_authnz_ldap:

<Directory />
  # everyone authenticates
  AuthName "Documentation"
  AuthType Basic
  AuthBasicProvider ldap
  AuthLDAPUrl "ldap://directory.work.com:389/ou=users,dc=work,dc=com?uid?sub"
  AuthLDAPGroupAttribute memberUid
  AuthLDAPGroupAttributeIsDN off
  Require ldap-group cn=admins,ou=groups,dc=work,dc=com

An admin-only account

A colleague within our system administration group—let’s call him “Skip”—wanted to create a new, separate user account for himself. He planned to use his regular account for non-administrative functions only, switching to the new account only when he wanted to invoke sudo or do other administrator-level work.

Skip didn’t want his regular account listed in that administrative group, which meant that he’d have to use his new administrative account to access our wiki.

He created the account: skipadm. He tested login access to machines that require users to be part of the admins group. He encountered no troubles. Unix tools like id reported that the skipadm user was part of the admins group using

Apache: denied

Despite success with system-level access, Apache wouldn’t authorize him. We increased Apache’s LogLevel setting up to trace6 and watched the error log. We could see that the password was accepted just fine, but Apache couldn’t map the account to the admins group.

[Thu Mar 17 10:01:33.809963 2016] [authnz_ldap:debug] [pid 18098] mod_authnz_ldap.c(593): [client] AH01697: auth_ldap authenticate: accepting skipadm

[Thu Mar 17 10:01:33.809969 2016] [authnz_ldap:debug] [pid 18098] mod_authnz_ldap.c(879): [client] AH01714: auth_ldap authorize: require group: testing for memberUid: skipadm (cn=admins,ou=groups,dc=work,dc=com)

[Thu Mar 17 10:01:33.810001 2016] [authnz_ldap:debug] [pid 18098] mod_authnz_ldap.c(945): [client] AH01720: auth_ldap authorize group: authorization denied for user skipadm to /wiki/

Various troubleshooting steps followed, to no avail.

First light

Then Skip sent this to me via chat:

actually, maybe I have a theory
I think I set it as my primary GID

And that freed up the brain cells for me to get to the heart of the problem.

Normal account creation

Here’s our normal procedure for creating new user accounts:

  1. Create the new user, making allusers the primary group
  2. Create a new group with the same name as the new user account
  3. Add user to the new per-user group
  4. Create home directory owned by user and per-user group
  5. Add user to any other pertinent auxiliary groups.

For example, for Skip’s normal account we

  1. Created user skip with GID allusers.
  2. Created group skip and added user skip to it.
  3. Made a home directory owned by user skip and group skip.
  4. Added user skip to the admins group.

A slight abnormality

When Skip created his admin account, however, he followed a different procedure:

  1. Created user skipadm with GID admins
  2. Created group skipadm and added user skipadm to it.
  3. Made a home directory owned by user skipadm and group skipadm.

Most user accounts for system administrators have admins added as an auxiliary group, but for this account it was the primary group.

The LDAP difference

That difference showed up in our LDAP directory in an unexpected way.

In a normal case, the user’s directory entry has the accounts user ID (UID) listed via uidNumber and the group ID via gidNumber. Auxiliary group membership isn’t listed in the user’s entry but by querying memberUid in a group entry.

In our directory scheme, however, the default group doesn’t get memberUid entry, just the aforementioned gidNumber in the user’s entry.

It can show up via getent. If we assume that the allusers group (our default group) has GID 1234, we can see a huge difference in the number of group members seen via getent group allusers as opposed to via getent passwd and looking for GID 1234:

[~]$ getent group allusers | cut -d: -f4 | sed 's/,/ /g' | wc
       1      61     484
[~]$ getent passwd | grep :1234:  | wc
     916     917   50790

The group map reports 61 members, while the passwd map reports 916.

When queried on a per-user basis, the system has one view of group membership. That’s what PAM does, and that’s why Skip was able to use his new account to log into systems.

Back to Apache

When viewed on a per-group basis, there’s a different view. That’s what Apache does, and that’s why it didn’t view Skip’s admin account as a group member.

Short of asking Apache to test group membership via PAM by way of a mechanism like the id utility, I’m not sure there’s an easy long-term solution to this problem when the user and group databases are stored in LDAP.

The fix

The fix was easy, if not really scalable: I manually edited the LDAP directory, adding a memberUid for skipadm in the admins group entry. Once that was in place, Apache was fine.

Still, it was a weird problem.