Intro
A lot has changed in this space over the last 6 years as more organisations adopt this kind of SSO facility.
There are still some age-old annoying issues that just don't want to go away - like the 15 character limit for Netbios names, Windows admins leaving the user's primary AD group at the default of "Domain Users" which contains that problematic space character, and automatic updates in DNS when joining domains, fortunately these can be worked around.
- The available documentation has improved immensely so only refer to the very latest information.
- The software has become more reliable and has many more features.
- Microsoft have dropped the Posix extension to their AD Schema.
In this example I will set up a Unix machine to use Active Directory for its authentication, and just to add some extra functionality I will force all users to belong to a local group on the host.
This document is current at the time of writing (January 2019) and assumes some basic Windows and Unix sysadmin knowledge. It has been successfully implemented in a commercial environment running RedHat 7.6 and Windows Server 2016.
Windows: Extending the Active Directory schema
Forget it. Accept the fact that Microsoft have lived up to their threat of dropping support of their Posix extension for Active Directory and get used to having a big Unix UID generated from a Microsoft SID. If you are still keen then with a bit of effort you can manually extend Active Directory with Posix attributes but this now appears to be just a legacy thing, move on.
A major benefit of doing this SSO configuration the new way is that you no longer have to manage UIDs and GIDs and trying to keep them unique in large organisations.
Ultimate control of UID and GID now comes from Active Directory, although the default Active Directory group of "Domain Users" still poses a problem on Unix systems there are workarounds to help deal with it.
One big security flaw with this approach is that you must lock down your SSO on your Unix hosts to only operate on just one Windows domain (or within trusted domains). Otherwise any untrusted Windows domain could replicate the same Unix accounts in their own domain and gain login ability.
One big question still left unanswered is can you transition a bunch of Microsoft SIDs to a new Windows domain so that your derived UID and GID values used throughout your Unix environment are preserved? Methinks you can't without resorting to kludges which defeats the whole purpose of choosing this approach for its simplicity in the first place.
Windows: Create an OU in Active Directory for joined Unix hosts
Create an OU structure in Active Directory for joined Unix hosts. E.g.
OU=Linux,OU=Devices,OU=corp,DC=mycompany,DC=com
Windows: Create a join user just for Unix hosts
Most Windows sysadmins don't understand Unix needs so you are better off creating a dedicated Active Directory join user in the Windows system with its own password just for joining Unix hosts to the Windows domain, and also allows device records to be written into the OU described above.
unix_join_userNo further permissions are required for this account.
Unix: Install client packages
On the Unix hosts install the following packages:
yum install adcli sssd sssd-ad realmd oddjob oddjob-mkhomedir samba-common-tools krb5-workstation
Unix: Installing Kerberos client
We only need to run the client so do not install a Kerberos server. There are two flavours of Kerberos available: MIT restricted to USA, and Heimdal for everyone else.
Install a recent version of the Kerberos client satisfying all dependencies. Older versions of the Kerberos client could not operate in a multi Windows domain environment due to an internal bug.
A large number of configuration directives are not implemented in the Heimdal version making the man page painful.
Ensure the clock on your Unix host is within 5 minutes of the clock on your Windows Domain Controllers otherwise Kerberos will fail all login attempts. You should install either chronyd or the Network Time Protocol (NTP) daemon on all your hosts to keep your computer clocks in sync to avoid this problem.
Configure krb5.conf (usually /etc/krb5.conf) as follows:
# Configuration snippets may be placed in this directory as well includedir /etc/krb5.conf.d/ [logging] default = FILE:/var/log/krb5libs.log kdc = FILE:/var/log/krb5kdc.log admin_server = FILE:/var/log/kadmind.log [libdefaults] dns_lookup_kdc = true dns_lookup_realm = false ticket_lifetime = 24h renew_lifetime = 7d forwardable = true rdns = false default_realm = CORP.MYCOMPANY.COM default_tkt_enctypes = aes256-cts-hmac-sha1-96 arcfour-hmac default_tgs_enctypes = aes256-cts-hmac-sha1-96 arcfour-hmac permitted_enctypes = aes256-cts-hmac-sha1-96 arcfour-hmac [realms] CORP.MYCOMPANY.COM = { kdc = dc1.corp.mycompany.com kdc = dc2.corp.mycompany.com admin_server = dc1.corp.mycompany.com admin_server = dc2.corp.mycompany.com } [domain_realm] .corp.mycompany.com = CORP.MYCOMPANY.COM corp.mycompany.com = CORP.MYCOMPANY.COM
Unix: nsswitch.conf
Ensure the Name Service Switch is using SSSD.
If not already present after the SSSD RPM install then configure specific entries in nsswitch.conf (usually /etc/nsswitch.conf) as follows:
passwd: files sssd shadow: files sssd group: files sssd
Unix: Configuring realmd
Configure a realmd.conf (usually /etc/realmd.conf) as follows:
[users] default-home = /home/%U default-shell = /bin/bash [active-directory] default-client = sssd [corp.mycompany.com] computer-ou = OU=Linux,OU=Devices,OU=corp,DC=mycompany,DC=com fully-qualified-names = no manage-system = noI did find it odd that realmd is as the name implies shipped as a daemon but according to systemctl isn't running. Maybe I missed something in the documentation, nonetheless, just leave the realmd run-status the way it was installed.
Unix: Joining the Windows domain
Before you perform this step:
- It's best you login as root from the console until you get this right otherwise you may lock yourself out.
- You have a Windows username and password of a privileged user that has join rights to the Windows domain.
- You have a unique uppercase version of the Unix Netbios name and it is less than or equal to 15 characters in length.
- You have the name of the nearest Windows domain controller to your Unix host.
Before you join the domain, count how many characters there are in your Unix short host name (i.e without the domain suffix). If your Unix short name is greater than 15 characters in length then the Netbios object that is created in Active Directory will simply be truncated to 15 characters in length. This means that if you join another host to the same domain and OU that starts with the same 15 characters then it will clobber any existing Netbios object in there rendering that previous host broken (but your new host will work). The workaround with Unix hosts that have short names longer than 15 characters is to manually specify a unique Netbios name to the realm command that is less than or equal to 15 characters in length. Any adjusted Netbios name will also need to be referenced in the SSSD config.
There is a funny bug between realm and SSSD in trying to be too smart with the handling of the case of the Netbios name that is used in the Kerberos principal name. I've found converting the Netbios name to be all uppercase for the realm command line option avoids this bug.
The following realm command is what I use to join the Windows domain:
realm -v join "<ad_join_server>" -U "<ad_join_user>" --computer-name="<ad_netbios_name_uppercase>" --os-name="<os_name>" --os-version="<os_version>" Password: <ad_join_user_password>Note: If your Unix shortname is less than or equal to 15 characters then you don't need to supply the --computer-name option.
It is good practice to supply the --os-name and --os-version options to aid in searches/reports of joined computers in your Active Directory system.For the following example of joining a Linux RedHat 7.6 server called marketing-webhost-01.corp.mycompany.com to the Windows domain I would execute the following:
realm -v join "dc1.corp.mycompany.com" -U "unix_join_user" --computer-name="MARKETING-WH-01" --os-name="RedHat Enterprise Linux Server" --os-version="7" Password: <unix_join_user_password>The join should update both the target OU with the Netbios name you specified and the /etc/krb5.keytab file, listing it should display the principal names.
klist -k /etc/krb5.keytabThe realmd tool generates a rather useless sssd.conf file which we will clobber with our own specific version of it later.
Unix: Configuring PAM
There are many ways to configure PAM which is complicated further by the many differing implementation techniques used by different Unix systems. The goal however is the same and that is to load the pam_sssd.so library at the correct point. You may want to search the web or consult your own system's documentation on how to go about this on your own computer(s).
Fortunately with RedHat 7 the installation of the SSSD RPM will update and configure PAM for you.
However, in this setup, I'd also like to restrict login access to this host to users who belong to an Active Directory group called unix_users. I can achieve this by using an SSSD feature called proxy which effectively calls PAM again from SSSD to make use of the /etc/security/access.conf file for AD logins.
A note of caution, if you are not careful with calling PAM a second time you can inadvertently create an infinite loop. To safely prevent such a thing from happening then create a dedicated PAM config file called sssd-proxy (usually /etc/pam.d/sssd-proxy) as follows:
account required pam_access.soNow rebuild your PAM as follows:
authconfig --updateall
Unix: Configuring SSSD
Unfortunately due to the Netbios name length limitation of 15 characters we cannot have a simple standard sssd.conf for all Unix hosts on the domain - thanks Microsoft (not).
For configuring sssd.conf (usually /etc/sssd/sssd.conf) for a Unix host called marketing-webhost-01.corp.mycompany.com it would look something like the following:
[sssd] domains = CORP.MYCOMPANY.COM config_file_version = 2 services = nss, pam [pam] reconnection_retries = 3 [domain/CORP.MYCOMPANY.COM] ad_server = dc1.corp.mycompany.com,dc2.corp.mycompany.com ad_domain = corp.mycompany.com krb5_realm = CORP.MYCOMPANY.COM realmd_tags = manages-system joined-with-adcli cache_credentials = True id_provider = ad krb5_store_password_if_offline = True dyndns_update = False default_shell = /bin/bash override_homedir = /home/%u ldap_id_mapping = True ldap_group_nesting_level = 1 auto_private_groups = True use_fully_qualified_names = False # The next two lines are only necessary if using an adjusted netbios name ldap_sasl_mech = GSSAPI ldap_sasl_authid = host/marketing-wh-01 access_provider = proxy proxy_pam_target = sssd-proxy # Don't partake otherwise [domain/default] cache_credentials = False access_provider = denyThe key points of this configuration file are:
- A lot of configuration items are repeated from the krb5.conf file.
- We cache credentials so as not to overwhelm the domain controllers with too many requests.
- We don't request DNS be updated dynamically.
- We enforce the users' home directory and shell - useful with automount.
- We generate a Unix UID from a Microsoft SID.
- We enforce the user's GID to be equal to their UID - this prevents many user management headaches.
- We don't traverse AD groups deeper than 1 level.
- We specify the Kerberos principal's host name which also contains the Netbios name in lowercase.
- We re-check the user's access via PAM (sssd-proxy).
- We deny all access from any other domain.
Restart SSSD, sometimes it can be peace-of-mind to also blow away the SSSD cache before a restart.
rm -f /var/lib/sss/db/* /var/lib/sss/mc/* rm -fr /var/cache/realmd/* systemctl restart sssd systemctl status sssdAt this stage you should now be able to talk to Active Directory and query the user database using standard Unix commands such as id, groups, getent passwd, getent group, etc.
Unix: Configure access
Assuming you have an Active Directory group called unix_users which contains members that are allowed to log into Unix hosts, then configure an access.conf (usually /etc/security/access.conf) as follows:
+ : (login) : ALL + : root : LOCAL + : unix_users : ALL - : ALL : ALLif you want to restrict login to the host to only specific users, then create an Active Directory group just for this Unix host and use the host name in some part of the group name.
Unix: /etc/group
I've found the reliability of SSSD to be much better these days that this step is now not really necessary. However not every situation can be catered for so to save you any potential headaches with any unreliability caused by SSO such as domain controllers becoming unreachable, I'll repeat what I said 6 years ago.
Tools like cron, sudo and sshd are often configured using group privileges that refer to the group name and not the GID, for example most Unix admins often restrict sshd to only allow users in a specified group to log in e.g. unix_users. When the association between GID and group name cannot be resolved then these tools can start causing problems.
The fix is to either identify the situation that causes the group name to not resolve to its GID, or add the group name in /etc/group using the same GID defined in Active Directory. I recommend the latter approach if your Unix admins don't have full Windows domain administrator privileges.
unix_users:*:19224598:In sshd_config (usually /etc/sshd/sshd_config) I have a line as follows (Note the double entry to handle both cases).
AllowGroups unix_users CORP\unix_usersI do something similar for sudoers (usually /etc/sudoers.d/unix_users) I have a line as follows (Note the double entry to handle both cases).
%unix_users,+unix_users ALL = (build) /usr/bin/make
Unix: Forcing AD users into local group membership
You may have an application that requires your users to become a member of a local group in order to use the application. Typically your options are:
The first option is an administrative chore unless you run some kind of software configuration management tool like Puppet or Ansible. The second option is weakening the resilience of your application because it now also depends on AD being available. It also gets messy if your application needs to define its own GID to be used.
- Edit /etc/group and add your AD usernames as members of the application group.
- Convert the application group to an AD group.
The workaround is to use a PAM module called pam_group.so that can automatically assign users to local groups once they log in.
Assuming a local group called myapp is already configured on your Unix host as follows:
myapp:*:1450:Configure a group.conf file (usually /etc/security/group.conf) as follows:
# Allow all SSH users to also be in the myapp group *;*;*;Al0000-2400;myappThe above forces all logged in users to become a member of the group myapp. See the group.conf man page for further details on how to use it.
If you just want to restrict membership of the myapp group to an AD group called unix_users then configure the group.conf file as follows:
# Allow members of AD group unix_users to also be in the myapp group *;*;%unix_users;Al0000-2400;myappThe next step is to install the pam_group.so library into PAM. This can be tricky as pam_group.so only supports the auth function class which means there are limited places you can use it effectively. In the case below I am adding it to SSHD (usually /etc/pam.d/sshd) as follows:
#%PAM-1.0 auth required pam_sepermit.so auth substack password-auth auth include postlogin # Extend group membership control auth optional pam_group.so # Used with polkit to reauthorize users in remote sessions -auth optional pam_reauthorize.so prepare account required pam_nologin.so account include password-auth password include password-auth # pam_selinux.so close should be the first session rule session required pam_selinux.so close session required pam_loginuid.so # pam_selinux.so open should only be followed by sessions to be executed in the user context session required pam_selinux.so open env_params session required pam_namespace.so session optional pam_keyinit.so force revoke session include password-auth session include postlogin # Used with polkit to reauthorize users in remote sessions -session optional pam_reauthorize.so prepareOnce again rebuild your PAM as follows:
authconfig --updateallLog in and execute the groups command. You should now see that you are a member of a local Unix group called myapp.
Interestingly, run the same command but this time specify your username as an argument, e.g. groups me. You no longer appear to be a member of the myapp group, why? I haven't straced the execution but I think when no argument is used it reads some kernel allocated environment that was set up during SSH login, and with an argument supplied then SSH hasn't been invoked and thus pam_group.so hasn't run. Told you it was tricky.