Active Directory group check via winbind + rlm_unix, not LDAP
Hello, I have a pretty common requirement: authenticate wireless users against Active Directory and prevent SSID cross-connections, i.e. users in group A can only connect to SSID A and users in group B can only connect to SSID B. I have seen plenty of messages in the archives about how to accomplish this. The authentication part is easy and has excellent documentation, e.g. http://deployingradius.com/documents/configuration/active_directory.html The group checking part is also well understood and documented (rlm_ldap), and whoever asks apparently gets told to use LDAP. At least I could not find anything on the alternate approach described here (apologies if this is old news). I decided to look into a different approach, which does not involve LDAP -- since the machine already has winbind running (for ntlm_auth), why not to use the Name Service Switch and rlm_unix to check for group membership and avoid usind LDAP altogether? Group membership information is already available if one has added "winbind" to "passwd" and "group" in /etc/nsswitch.conf. I apparently got this to work and wanted to share the solution in the hope that it will be helpful to someone, but also to ask if anyone sees any issues with the approach: First, I configured winbind for ntlm_auth use by FreeRADIUS, as explained elsewhere. Then, I verified that the system can see Active Directory users and groups as if they were Unix users and groups: shell$ id DOMAIN\\username uid=10017(DOMAIN\username) gid=10002(DOMAIN\domain users) groups=10002(DOMAIN\domain users),10024(DOMAIN\computer-lab-monitoring),10008(DOMAIN\domain admins),10034(DOMAIN\vdi teachers),10010(DOMAIN\teachers),10026(DOMAIN\vdi users),10011(DOMAIN\teacher assistants),10074(DOMAIN\schema admins) Then, I put this logic, which is similar to what one would normally use if LDAP and Ldap-Groups were in use, in the post-authentication section of my sites-enabled/default: if (NAS-Port-Type == Wireless-802.11) { if (Called-Station-Id =~ /.*:SSID-A/i) { # Can't do 'if (Group != "xxxxx")' because != # operator doesn't work for group checking. Careful # with the number of backslashes. if (!(Group == "DOMAIN\\\\group A") ) { update reply { Reply-Message = "User not allowed to join this wireless network" } reject } } elsif (Called-Station-Id =~ /.*:SSID-B/i) { if (!(Group == "DOMAIN\\\\group B") ) { update reply { Reply-Message = "User not allowed to join this wireless network" } reject } } } This works if the EAP identity is "DOMAIN\username". However, I don't want to make things unnecessarily complicated for my users so I want them to be able to enter just "username" when they configure their devices. The main issue to address, however, is that if the identity is entered as "username" (not "DOMAIN\username"), the group check will fail because the Unix user ID for users that are known to the system via winbind is "DOMAIN\username", not "username" (see output from the "id" command above). "Not a problem", I thought, "I'll just manipulate User-Name before anything happens and prefix it with "DOMAIN\". Turns out that was a bad idea because that made User-Name different than the EAP identity hidden in the EAP message, which caused the "rlm_eap: identity does not match User-Name, setting from EAP identity" message that has bitten so many people before. The solution I came up with was to still manipulate the User-Name but towards the end, in the post-auth section, instead of at the beginning. This way EAP uses the right identity, but the rlm_unix Group check uses the correct "DOMAIN\username" User-Name. The final configuration looks like this: if (NAS-Port-Type == Wireless-802.11) { # If User-Name doesn't contain our domain then add it. # It's needed for the Group check to use the correct # username. if (User-Name !~ /DOMAIN\\\\/i) { update request { User-Name := "DOMAIN\\\\%{User-Name}" } } if (Called-Station-Id =~ /.*:SSID-A/i) { # Can't do 'if (Group != "xxxxx")' because != # operator doesn't work for group checking. Careful # with the number of backslashes. if (!(Group == "DOMAIN\\\\group A") ) { update reply { Reply-Message = "User not allowed to join this wireless network" } reject } } elsif (Called-Station-Id =~ /.*:SSID-B/i) { if (!(Group == "DOMAIN\\\\group B") ) { update reply { Reply-Message = "User not allowed to join this wireless network" } reject } } } I do not know about the performance impact but this should not require additional network traffic to check for group membership because winbind caches this information. Another possible advantage is redundancy -- I understand the LDAP method does not allow for multiple LDAP servers. Using winbindd (I theorize, I am not sure about this) provides redundancy because the group membership comes from the domain controller, which is found using DNS lookups -- if a controller goes down then another (hopefully) takes its place and winbindd will be able to find it with no configuration changes. This approach seemed simple to me -- no additional configuration other than manipulating the User-Name in the post-authentication phase. Things can also be made to work if the user chooses to configure the supplicant with "DOMAIN\user" as the identity -- in this case one needs to configure "with_ntdomain_hack = yes" in modules/mschap, create an empty "DOMAIN" realm in proxy.conf, and enable the ntdomain realm in the authorize section of sites-enabled/inner-tunnel. Doing this will allow both "DOMAIN\user" and just "user" to work. Any thoughts, gotchas or hidden traps with this approach? I have tested this in pre-deployment and it works. I will start testing this in production to see if users have any issues. Cheers, Eloy Paris.-
Hi,
I apparently got this to work and wanted to share the solution in the hope that it will be helpful to someone, but also to ask if anyone sees any issues with the approach:
interesting solution
if (User-Name !~ /DOMAIN\\\\/i) { update request { User-Name := "DOMAIN\\\\%{User-Name}" } }
you shouldnt play with User-Name - use a temporary/local RADIUS attribute instead
Another possible advantage is redundancy -- I understand the LDAP method does not allow for multiple LDAP servers. Using winbindd (I theorize, I
yes, it does (allow multiple servers)
am not sure about this) provides redundancy because the group membership comes from the domain controller, which is found using DNS lookups -- if a controller goes down then another (hopefully) takes its place and winbindd will be able to find it with no configuration changes.
no. it rarely falls over nicely to the next server. winbindd is rubbish (i know, we use it) alan
Hi Am 01.09.2014 13:42, schrieb A.L.M.Buxey@lboro.ac.uk:
am not sure about this) provides redundancy because the group membership comes from the domain controller, which is found using DNS lookups -- if a controller goes down then another (hopefully) takes its place and winbindd will be able to find it with no configuration changes.
no. it rarely falls over nicely to the next server. winbindd is rubbish (i know, we use it)
Which I unfortunately have to confirm, it doesn't fail over neither quickly nor that nicely, it may take its time to fail over which might take enough time for the user to get a authentication error... Usually a LDAP lookup for the group membership is very quick and can be balanced. (even though AD's LDAP isn't one of the fastest LDAP servers) -- Mathieu
On 01/09/14 12:42, A.L.M.Buxey@lboro.ac.uk wrote:
no. it rarely falls over nicely to the next server. winbindd is rubbish (i know, we use it)
We find it does fail over, but it's slow to detect a dead server - tens of seconds, which is too slow to be useful in a wireless environment with high client and reauth counts.
Hello, On Mon, Sep 01, 2014 at 11:42:33AM +0000, A.L.M.Buxey@lboro.ac.uk wrote: [...]
if (User-Name !~ /DOMAIN\\\\/i) { update request { User-Name := "DOMAIN\\\\%{User-Name}" } }
you shouldnt play with User-Name - use a temporary/local RADIUS attribute instead
I'm all for that, but then how can the "Group == 'xxxxx'" check be done against this temporary/local attribute? The group check is always done against the User-Name attribute, isn't it?
Another possible advantage is redundancy -- I understand the LDAP method does not allow for multiple LDAP servers. Using winbindd (I theorize, I
yes, it does (allow multiple servers)
Good to know; thanks! Cheers, Eloy Paris.-
Eloy Paris wrote:
I'm all for that, but then how can the "Group == 'xxxxx'" check be done against this temporary/local attribute? The group check is always done against the User-Name attribute, isn't it?
Use Stripped-User-Name. That's what it's for. Everything in the server that needs a stripped user name uses that. Changing Stripped-User-Name won't affect EAP. Alan DeKok.
participants (5)
-
A.L.M.Buxey@lboro.ac.uk -
Alan DeKok -
Eloy Paris -
matsimon.lists@simweb.ch -
Phil Mayers