Logon to Linux with your Active Directory Account

Here's a solution to enable Active Directory accounts to logon to your linux machines. Many companies are now starting to have more Linux machines in their estate. Traditionally, users who needed access to these machines had an account created locally on each machine. This becomes difficult to manage if you have many Linux machines and many users. Keeping passwords in sync becomes a problem for users, need I go on. Having a central store for user accounts and passwords is a no brainer. Many companies already have such a store: Active Directory. AD domain controllers provide LDAP and Kerberos services that are compatible with the Kerberos and LDAP clients found on Linux. At least the versions of Linux that I've tested this solution with (Fedora 12, and RedHat Enterprise Server 5.2).

Note: This article is for older versions of Fedora/RedHat Linux.  Please see Fedora 16 - Logging into Active Directory for more recent configuration information.
New on ITAdminTools.com

ITAdminTools now offers Linux Active Directory User Manager, the GUI for managing Linux users in Active Directory.

The solution uses LDAP to lookup user information from AD, and uses Kerberos to authenticate users. Below are the configuration files neccesary to make it work.

Configuring Kerberos

The first file to edit is /etc/krb5.conf which contains the definition of your AD domain.

default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log
default_realm = MYDOMAIN.COM
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = yes
kdc = mydomain.com:88
admin_server = mydomain.com:749
mydomain.com = MYDOMAIN.COM
.mydomain.com = MYDOMAIN.COM

The key items here are lines that contain MYDOMAIN.COM. Just replace with whatever your domain is. As soon as you've saved this file, you should be able to test Kerberos by acquiring a Kerberos ticket. Of course your Linux system must be able to resolve the DNS name of your domain, so make sure that it's configured to point to DNS servers that can resolve the AD DNS records. To test, type the following in a terminal window:

kinit [email protected]

You should be prompted to enter the AD password for that user. You can then use the klist command to view the ticket cache, and the kdestroy command to get rid of the ticket.

Next, let's configure the LDAP configuration by editing the /etc/ldap.conf file. There's also a copy of the ldap.conf file in /etc/openldap/ldap.conf so whenever you make changes to one, copy it over to the other. Some of the tools use the copy in /etc, others use the copy in /etc/openldap.

#basic settings
ldap_version 3
network_timeout 20
bind_policy soft
#debug 2
# your AD config
uri ldaps://mydc01.mydomain.com ldaps://mydc02.mydomain.com ldaps://mydc03.mydomain.com
binddn cn=myLDAPlookupUser,cn=users,dc=mydomain,dc=com
bindpw mySecret
base dc=mydomain,dc=com
scope sub
referrals off
#pam settings
pam_login_attribute     sAMAccountName
pam_filter              objectCategory=User
pam_password            ad
pam_groupdn             cn=myLinuxUsers,cn=users,dc=mydomain,dc=com
pam_member_attribute    member
#nss settings
nss_initgroups_ignoreusers root,bin,daemon,adm,lp,sync,shutdown,halt,mail,uucp,operator,games,gopher,ftp,nobody,vcsa,avahi-autoipd,ntp,dbus,rpc,rpcuser,nfsnobody,nscd,tcpdump,avahi,apache,openvpn,rtkit,mailnull,smmsp,nm-openconnect,sshd,smolt,backuppc,haldaemon,pulse,gdm,abrt,mysql
nss_base_passwd         dc=mydomain,dc=com?Sub
nss_base_shadow         dc=mydomain,dc=com?Sub
nss_base_group          dc=mydomain,dc=com?Sub
nss_map_objectclass     posixAccount    User
nss_map_objectclass     shadowAccount   User
nss_map_objectclass     posixGroup      Group
nss_map_attribute       uid             sAMAccountName
nss_map_attribute       cn              sAMAccountName
nss_map_attribute       uniqueMember    member
nss_map_attribute       homeDirectory   unixHomeDirectory
nss_map_attribute       gecos           name
#ssl settings
tls_cacertdir /etc/pki/tls
tls_cacertfile /etc/pki/tls/myrootcert.pem
tls_checkpeer no
ssl on

There's lots of information in this file to discuss. Let's take it one line at a time.

network_timeout 20: As ou can see, I've defined three domain controllers in the uri line. The network timeout allows the LDP client to move onto the next domain controller in a timely fashion if one is unresponsive. You can adjust this setting as you see fit.

bind_policy soft: I can't stress enough how important this line is! Don't forget to put it in! Don't fat finger it! The default is bind_poicy hard, which, if there's anything wrong and LDAP isn't working, your whole Linux box willl slow to an absolute crawl, you might not ever be able to login again, and you might be completely hosed and require a rebuild.

debug: If things aren't working, you can enable debug. There are various levels, I find 2 is a good setting. As you can see, I have it commented out with a # in front of it.

uri: this is the LDAP path to your AD domain controller(s). Do not use quotes. Seperate multiple DC's with a space. If you're going to use secure LDAP (LDAP overl SSL), use ldaps:// as the prefix, otherwise use ldap://.

binddn: This is the distinguished name of the user that you'll use to connect to AD to lookup information about users who are trying to logon to the Linux box. This should not be the account of a user, but rather a dedicated account for ldap lookups. The account should be set so that its password doesn't expire like regular users. Otherwise, the account needs no special privileges in AD.

bindpw: This is the password of the user mentioned above.

base: This is the distinguished name of the domain or organizational unit (OU) where your users are located within AD. If all of your users are in a specific OU, then specify that OU for the base. That will limit the scope of LDAP queries and speed things up.

scope: This can be set to one, base, or sub. sub includes any sub-OU's within the domain or OU specified in base.

referrals: AD can contain numberous directory partitions or contexts. To speed things up, set referrals off to keep LDAP from seaching every stinkin context for a user that doesn't exist.

The next section of the ldap.conf file is related to PAM (Pluggable Authentication Manager). We'll configure PAM to use LDAP a little later, but the following lines configure PAM's ldap module.

pam_login_attribute: This defines which AD attribute is used for the username. Typically, this should be the sAMAccountName attribute (that is the Windows user name).

pam_filter: This is an LDAP query filter that limits the scope of the seach for the user. The tighter you can filter the search, the faster LDAP will work, and the less load you will put on your domain controllers.

pam_groupdn: Specifies the distinguished name of an AD group, that the user must be a member of in order to login to the Linux server. If you don't add this line, then all of your AD users will be able to login.

pam_member_attribute: Specifies the AD attribute of the group mentioned above, that should contain the list of group members.

The next section contains the NSS_LDAP settings. Note that some of the information here appears to be redundant, because it is. Several Linux components (openldap, PAM, and NSS) are using the ldap.conf as their config file).

nss_initgroups_ignoreusers: You should put any local accounts (especially root) in this line, so that NSS knows not to do an LDAP query when these accounts login. I've included all of the local accounts that are created by default in Fedora 12.

nss_base_passwd: This is the distinguished name of the domain or OU where your users are located (same as base), followed by the search scope, seperated by a ?.

nss_map_objectclass: This tells NSS to use AD object classes (users and groups) rather than traditional LDAP object classes.

nss_map_attribute: This tells NSS to use AD attributes rather than traditional LDAP attributes.

The final section relates to SSL settings, which requires a bit of discussion.

LDAP, by default will send a username and password across the network unencrypted, so a sniffer can capture the packets and get the password. This isn't a huge deal in this case, because we're going to use Kerberos to authenticate users, and Kerberos not only encrypts its traffic, but also doesn't really pass the user's password over the network (it sends a hash instead). However, the binddn user that we're using to connect to AD will have its password sent over the network in clear text, so it's possible for that password to be intercepted. If that's a concern, then you can implement secure LDAP.

Secure LDAP uses SSL to setup an encrypted tunnel to send the LDAP traffic through. In order for you to implement SSL, you need to deploy a certificate to your domain controllers, and that means that you either have to have an internal certificate authority (CA) to issue certificates, or you have to buy commercial certificates from a vendor like Verisign or Thawte. Once you have certificates deployed on your domain controllers, you can communicate with your domain controllers using secure LDAP.

If you don't use secure LDAP, the uri line in your ldap.conf should use the prefix ldap:// rather than ldaps://, and the last few lines of the file should be commented out. I recommend that you configure LDAP without SSL at first to get things working, then configure SSL afterwords.

Besides encrypting the traffic, SSL also verifies the authenticity of the server you're communicating with. In order for this to work, you must trust the certificate authority that issued the certificate that is on your domain controllers. If you used a commercial certificate, then it's likely that your Linux server already trusts the vendor, because a copy of the vendor's root certificate is likely already on your Linux server. If you used an internal CA, then you'll need to put a copy of your internal root certificate on your Linux server. The certificate should be in saved in Base-64 encoded X.509 format, and saved with a .pem extension (e.g. myRootCaCert.pem).

Once you have a copy of the root CA certificate, you need to figure out where to put it. To do that, run the following command and take note of the OPENSSLDIR path:

openssl version -a

Now copy the cert into that directory. Next, since Linux uses a hash of the certificate to find the certificate, we need to create a link to the certificate using the hash. To get the hash, use this command:

openssl x509 -noout -hash -in myRootCaCert.pem

The command will return hex digits such as efe71310. Use these digits to create a symbolic link to the certificate. Create the symbolic link in the directory above, like so (using your hash of course):

ln -s myRootCaCert.pem efe71310.0

The trailing .0 is required because it's possible for multiple certs to have the same hash, so you must put an enumerator at the end.

OK, now that you have successfully added your root cert to your server, you should now be able to configure secure LDAP. Let's go back to the ldap.conf and enable it. To do that, simply use the ldaps:// prefix in your uri line, and uncomment the last few lines of the file. The lines specify the location of the certificates and enable SSL. Remember to copy your changes from /etc/ldap.conf to /etc/openldap/ldap.conf.

Next we need to configure PAM, to tell PAM to use LDAP and Kerberos for authentication. Edit the /etc/pam.d/system-auth file as shown below:

# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        required      pam_env.so
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 500 quiet
auth        sufficient    pam_krb5.so use_first_pass
auth        sufficient    pam_ldap.so use_first_pass
auth        required      pam_deny.so
account     required      pam_unix.so broken_shadow
account     sufficient    pam_localuser.so
account     sufficient    pam_succeed_if.so uid < 500 quiet
account     [default=bad success=ok user_unknown=ignore] pam_ldap.so
account     [default=bad success=ok user_unknown=ignore] pam_krb5.so
account     required      pam_permit.so
password    requisite     pam_cracklib.so retry=3
password    sufficient    pam_unix.so md5 shadow nullok try_first_pass use_authtok
password    sufficient    pam_krb5.so use_authtok
password    sufficient    pam_ldap.so use_authtok
password    required      pam_deny.so
session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
session     optional      pam_mkhomedir.so silent skel=/etc/skel/ umask=0022
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session     required      pam_unix.so
session     optional      pam_krb5.so
session     optional      pam_ldap.so

Notice that a line for LDAP (pam_ldap.so) and a line for Kerberos (pam_krb5.so) have been added to each section (auth, account, password and session), and also I've added a line in the session section that automatically makes a homedir for the user the first time they login (pam_mkhomedir.so).

Next, let's modify NSS to use LDAP for user information lookups. Edit the /etc/nsswitch.conf as follows:

# /etc/nsswitch.conf
# An example Name Service Switch config file. This file should be
# sorted with the most-used services at the beginning.
# The entry '[NOTFOUND=return]' means that the search for an
# entry should stop if the search in the previous entry turned
# up nothing. Note that if the search failed due to some other reason
# (like no NIS server responding) then the search continues with the
# next entry.
# Legal entries are:
# nisplus or nis+  Use NIS+ (NIS version 3)
# nis or yp  Use NIS (NIS version 2), also called YP
# dns   Use DNS (Domain Name Service)
# files   Use the local files
# db   Use the local database (.db) files
# compat   Use NIS on compat mode
# hesiod   Use Hesiod for user lookups
# [NOTFOUND=return] Stop searching if not found so far
# To use db, put the "db" in front of "files" for entries you want to be
# looked up first in the databases
# Example:
#passwd:    db files nisplus nis
#shadow:    db files nisplus nis
#group:     db files nisplus nis
passwd:     files ldap
shadow:     files ldap
group:      files ldap
#hosts:     db files nisplus nis dns
hosts:      files dns
# Example - obey only what nisplus tells us...
#services:   nisplus [NOTFOUND=return] files
#networks:   nisplus [NOTFOUND=return] files
#protocols:  nisplus [NOTFOUND=return] files
#rpc:        nisplus [NOTFOUND=return] files
#ethers:     nisplus [NOTFOUND=return] files
#netmasks:   nisplus [NOTFOUND=return] files
bootparams: nisplus [NOTFOUND=return] files
ethers:     files
netmasks:   files
networks:   files
protocols:  files
rpc:        files
services:   files
netgroup:   files ldap
publickey:  nisplus
automount:  files ldap
aliases:    files nisplus

Notice that I've added the word ldap to the following lines: passwd, shadow, group, netgroup and automount.

One more thing to tweak, assuming that you plan to use SSH to logon to the Linux server remotely. Let's edit the /etc/ssh/sshd_config file.

# $OpenBSD: sshd_config,v 1.80 2008/07/02 02:24:18 djm Exp $
# This is the sshd server system-wide configuration file.  See
# sshd_config(5) for more information.
# This sshd was compiled with PATH=/usr/local/bin:/bin:/usr/bin
# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented.  Uncommented options change a
# default value.
#Port 22
#AddressFamily any
#ListenAddress ::
# Disable legacy (protocol version 1) support in the server for new
# installations. In future the default will change to require explicit
# activation of protocol 1
Protocol 2
# HostKey for protocol version 1
#HostKey /etc/ssh/ssh_host_key
# HostKeys for protocol version 2
#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_dsa_key
# Lifetime and size of ephemeral version 1 server key
#KeyRegenerationInterval 1h
#ServerKeyBits 1024
# Logging
# obsoletes QuietMode and FascistLogging
#SyslogFacility AUTH
SyslogFacility AUTHPRIV
#LogLevel INFO
# Authentication:
#LoginGraceTime 2m
#PermitRootLogin yes
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10
#RSAAuthentication yes
#PubkeyAuthentication yes
#AuthorizedKeysFile .ssh/authorized_keys
#PubkeyAgent none
#PubkeyAgentRunAs nobody
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#RhostsRSAAuthentication no
# similar for protocol version 2
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# RhostsRSAAuthentication and HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes
# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no
PasswordAuthentication yes
#PasswordAuthentication no
# Change to no to disable s/key passwords
#ChallengeResponseAuthentication yes
ChallengeResponseAuthentication no
# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no
# GSSAPI options
GSSAPIAuthentication no
#GSSAPIAuthentication yes
#GSSAPICleanupCredentials yes
#GSSAPICleanupCredentials yes
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no
# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
#UsePAM no
UsePAM yes
# Accept locale-related environment variables
#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
#X11Forwarding no
X11Forwarding yes
#X11DisplayOffset 10
#X11UseLocalhost yes
#PrintMotd yes
#PrintLastLog yes
#TCPKeepAlive yes
#UseLogin no
#UsePrivilegeSeparation yes
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#ShowPatchLevel no
#UseDNS yes
#PidFile /var/run/sshd.pid
#MaxStartups 10
#PermitTunnel no
#ChrootDirectory none
UseDNS no
# no default banner path
#Banner none
# override default of no subsystems
Subsystem sftp /usr/libexec/openssh/sftp-server
# Example of overriding settings on a per-user basis
#Match User anoncvs
# X11Forwarding no
# AllowTcpForwarding no
# ForceCommand cvs server

The imporatant lines here are:

PasswordAuthentication yes
ChallengeResponseAuthentication no
GSSAPIAuthentication no
UsePAM yes
UseDNS no

Finally, we need to populate the necessary attributes in the AD user's account that Linux is looking for. In our ldap.conf, we've specified sAMAccountName as our uid, so that's already populated. However, we need to populate the uidNumber, gidNumber, loginShell, and unixHomedirectory attrbutes. Since these attributes aren't visible in the Active Directory Users and Computers gui, we'll use adsiedit to populate them. ADSIEDIT is a tool that comes with the support tools which are located on the Windows 2003 CD.

New on ITAdminTools.com

ITAdminTools now offers Linux Active Directory User Manager, the GUI for managing Linux users in Active Directory.

You need to populate the attrbutes as follows:

uidNumber - pick a unique number for each user.
gidNumber - pick a number and create a group with this gid on the Linux server. Maybe call the group ADACCESS?
loginShell - /bin/bash is typical for Linux
unixHomedirectory - /home/username would be typical.

Don't forget to add the user to the PAM group we configured earlier. Now that your user's attributes are populated, assuming you didn't fat finger any of the files, you should be able to login to the Linux server using your AD account. If things don't work as expected, look at your logs (/var/log/messages and /var/log/secure) . If necessary, enable debug in the ldap.conf.

Good luck. This post is a bit of a work in progress. I'll do my best to answer any questions.


MarcJ said...

Hi Brian,

This is a very cool article! With the need to integrate Linux based hosts in AD environments on the rise, it seems like your solution could actually help quite a few admins out there - nice work!

By the way, I run a blog on Free Active Directory Reporting Tools, and if you have any suggestions for any helpful AD tools that are FREE, I hope you'll let me know, as I would like to like to blog about it for everyone's benefit.

Thanks again, and good luck - nice work again!



- Marc

Brian Seltzer said...

Thanks Marc. I appreciate the feedback. I'll send any tools that I find your way.


Post a Comment

Related Posts Plugin for WordPress, Blogger...