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
</Directory>
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 10.11.12.13:46107] 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 10.11.12.13:46107] 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 10.11.12.13:46107] 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:
- Create the new user, making
allusersthe primary group - Create a new group with the same name as the new user account
- Add user to the new per-user group
- Create home directory owned by user and per-user group
- Add user to any other pertinent auxiliary groups.
For example, for Skip’s normal account we
- Created user
skipwith GIDallusers. - Created group
skipand added userskipto it. - Made a home directory owned by user
skipand groupskip. - Added user
skipto theadminsgroup.
A slight abnormality
When Skip created his admin account, however, he followed a different procedure:
- Created user
skipadmwith GIDadmins - Created group
skipadmand added userskipadmto it. - Made a home directory owned by user
skipadmand groupskipadm.
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.