RHEL6 and CentOS6 Active Directory Integrated Logins

By Zane Mingee, Wed 13 May 2015, modified Wed 13 May 2015, in category Tech

active directory, centos6, python, saltstack

Plain LDAP integrated logins on RHEL (or other distributions, really) is far from a trivial project. There are multiple software stacks that can be used, and along with that there seems to be an infinite number of ways to configure each of those pieces of software. Plus, all of those various software stacks can be put together in many different combinations. The process usually involves days and weeks of compiling different solutions from a handful of blogs just to find out it doesn't work and then starting from scratch again. The worst part is that many of these blogs do not have any real explanations as far as what each part accomplishes in the login process. And, to top it all of, most of them are utilizing a 'generic' LDAP backend and do not mention Active Directory at all.

The purpose of this guide is to show the process of deploying LDAP/Active Directory logins in a RHEL/CentOS 6.x environment from beginning to end, and explain each step in the process at a granular level.

I will also be providing a SaltStack Formula, along with a Vagrant setup, for deploying this in your environment. I would recommend deploying it in a development environment first, and testing it profusely before letting it touch production. This isn't a time you want your automation stack to fail. If you have questions, feel free to drop me a line and I'll help as much as I can. I'd like for this to be a real, living resource for the community.

My Setup

A good first step would be to define my environment.

Definition of Goals

The goal of this article is to setup LDAP/Active Directory integration on RHEL/CentOS 6.x. This may work on other distributions, but cannot be guaranteed. This stack will utilize LDAP, Kerberos, and SSH keys stored in Active Directory. The SSH keys are by no means required, just a nice touch. You can pull out the parts of this you want or need and build it to fit. The goal is for you to have the knowledge to take this information do what you need with it. I would recommend building this environment to match mine 100% and then break it apart afterwards. This way, you will have a full grasp of everything before attempting it on your own.

First Steps

Software being used

We will be using a multitude of software stacks to accomplish this project. Some of these should already be installed, but we just need to verify.

All of these can be installed with the following command

$ yum install pam pam_ldap pam_krb5 sssd sssd-ldap sssd-common authconfig oddjob oddjob-mkhomedir openldap openldap-clients krb5-workstation -y

Special note

The SSH key functionality relies directly on a Python script I wrote. If you are interested in using SSH keys (which I recommend), you will need access to Python 3 (preferably Python 3.4 for ease of access to PIP). This is not provided in the standard nor the EPEL repositories, and requires either building from source or using a third-party repo. The IUS Community Project, ran by engineers from RackSpace provides a repo that has Python 3.4. But, I'm just not a fan of third-party repos. Feel free to use it, but this guide will have instructions for installing from source. Assuming you install Python 3.4, nothing will be different except for the way you install the software.

Installing Python 3.4 from source

This isn't very difficult, it just takes some time.

First you need to install the development tools that it will depend on:

$ yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel -y
$ yum groupinstall "Development tools" -y

Configure the shared libraries between Python 2.x and Python 3.x:

vi /etc/ld.so.conf

include ld.so.conf.d/*.conf
/usr/local/lib

#/sbin/ldconfig

Installing Python 3.4.2:

$ cd /tmp
$ wget -c https://www.python.org/ftp/python/3.4.2/Python-3.4.2.tgz
$ tar -zxvf Python-3.4.2.tgz
$ cd Python-3.4.2
$ ./configure -prefix=/usr/local -enable-shared
$ make
$ make altinstall
$ /sbin/ldconfig
$ /usr/local/bin/pip3.4 install ldap3

Setup configuration

There are quite a few files that need to be configured for LDAP integration to work. I'll list them all here, with some brief details after each.

As you can see, that's 9 files that need to modified or created. This is why I recommend using SaltStack to manage the deployment. The formula just requires edits to one "pillar" file, and most of the rest of the work is automated.

Below are all of the configuration changes needed for these files.

/etc/krb5.conf
---------------

# NOTE :: The "." in [domain_realm] is required

[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log

[libdefaults]
 default_realm = <DOMAIN NAME - ALL CAPS>
 dns_lookup_realm = true
 dns_lookup_kdc = true
 ticket_lifetime = 24h
 renew_lifetime = 7d
 forwardable = true
 rdns = false

[realms]
 <DOMAIN NAME - ALL CAPS> = {
  kdc = <DOMAIN CONTROLLER FQDN>    # "Primary"
  kdc = <DOMAIN CONTROLLER FQDN>    # "Secondary"
 }

[domain_realm]
 .<DOMAIN NAME - ALL CAPS> = <DOMAIN NAME - ALL CAPS>
 <DOMAIN NAME - ALL CAPS> = <DOMAIN NAME - ALL CAPS>

 <DOMAIN NAME - LOWER CASE> = <DOMAIN NAME - ALL CAPS>
 .<DOMAIN NAME - LOWER CASE> = <DOMAIN NAME - ALL CAPS>
[appdefaults]
 pam = {
   debug = false
   ticket_lifetime = 36000
   renew_lifetime = 36000
   forwardable = true
   krb4_convert = false
 }
/etc/pam.d/sudo
---------------

#%PAM-1.0
auth       include      system-auth
account    include      system-auth
password   include      system-auth
session    optional     pam_keyinit.so revoke
session    required     pam_limits.so
/etc/nsswitch.conf
---------------

#
# /etc/nsswitch.conf
#

passwd:     files sss
shadow:     files sss
group:      files sss

hosts:      files dns

bootparams: nisplus [NOTFOUND=return] files

ethers:     files
netmasks:   files
networks:   files
protocols:  files
rpc:        files
services:   files sss

netgroup:   files sss

publickey:  nisplus

automount:  files sss
aliases:    files nisplus
sudoers:    files sss
/etc/pam.d/system-auth-ac
---------------

#%PAM-1.0
# 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_sss.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_sss.so
account     required      pam_permit.so

password    requisite     pam_cracklib.so try_first_pass retry=3 type=
password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password    sufficient    pam_sss.so use_authtok
password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
session     optional      pam_oddjob_mkhomedir.so umask=0077
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session     required      pam_unix.so
session     optional      pam_sss.so
/etc/pam.d/password-auth-ac
---------------

#%PAM-1.0
# 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_sss.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_sss.so
account     required      pam_permit.so

password    requisite     pam_cracklib.so try_first_pass retry=3 type=
password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password    sufficient    pam_sss.so use_authtok
password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
session     optional      pam_oddjob_mkhomedir.so umask=0077
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session     required      pam_unix.so
session     optional      pam_sss.so
/etc/sssd/sssd.conf
---------------

[sssd]
services = nss, pam, sudo
config_file_version = 2
domains = <DOMAIN NAME - ALL CAPS>
sbus_timeout = 30

[nss]
filter_groups = root
filter_users = root
reconnection_retries = 3

[pam]
offline_credentials_expiration = 0

[domain/<DOMAIN NAME - ALL CAPS>]
debug_level = 10
enumerate = false
min_id = 1000
id_provider = ldap


# Base LDAP configuration for AD
ldap_uri = ldap://<DOMAIN NAME - LOWER CASE>
tls_reqcert = demand
ldap_tls_cacert = /etc/pki/tls/certs/<DOMAIN NAME - LOWER CASE>.cer
ldap_tls_cacertdir = /etc/pki/tls/certs
ldap_search_base = <BASE DN - ex. "dc=corp,dc=domain,dc=com">
ldap_user_search_base = <BASE USER DN - ex. "cn=Users,dc=corp,dc=domain,dc=com">
ldap_group_search_base = <BASE USER DN - ex. "OU=Groups,dc=corp,dc=domain,dc=com">
ldap_schema = ad
ldap_user_name = sAMAccountName
ldap_user_fullname = displayName
ldap_user_object_class = user
ldap_group_object_class = group
ldap_id_mapping = true
ldap_user_principal = userPrincipalName
ldap_user_shell = msSFU30LoginShell
ldap_account_expire_policy = ad
ldap_force_upper_case_realm = true
default_shell = /bin/bash
override_homedir = /home/%d/%u
emumerate = true
entry_cache_timeout = 30


# Kerberos config
auth_provider = krb5
chpass_provider = krb5
krb5_server = <PRIMARY DOMAIN CONTROLLER FQDN>, <SECONDARY DOMAIN CONTROLLER FQDN>
krb5_realm = <DOMAIN NAME - ALL CAPS>
krb5_changepw_principle = kadmin/changepw
krb5_ccachedir = /tmp
krb5_ccname_template = FILE:%d/krb5cc_%U_XXXXXX
krb5_auth_timeout = 15
cache_credentials = True


# Utility account to bind to AD
# Any user should work
ldap_default_bind_dn = <USER DN TO BIND>
#
# Value must stay "password"
ldap_default_authtok_type = password
#
# LDAP user's password
ldap_default_authtok = <BIND USER PASSWORD>


# Access restriction based on membership in "linux-users"
access_provider = ldap
ldap_access_filter = <BASE DN OF USER GROUP - ex. "memberOf=cn=linux-users,ou=Groups,dc=corp,dc=domain,dc=com"> 


# Sudo settings
sudo_provider = ldap
ldap_sudo_search_base = <BASE DN OF USER GROUP "linux-sudo" - ex. "memberOf=cn=linux-sudo,ou=Groups,dc=corp,dc=domain,dc=com">


[sudo]
/etc/pam.d/sshd
---------------

#%PAM-1.0
auth       required     pam_sepermit.so
auth       include      password-auth
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    optional     pam_keyinit.so force revoke
session    include      password-auth
/etc/sudoers.d/<NETBIOS DOMAIN NAME>-linux-sudo
---------------

%linux-sudo     ALL=(ALL)  ALL
/etc/ssh/sshd_config
---------------

#   $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 <SSH PORT>
ListenAddress <LISTEN ADDRESS>
#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
AuthorizedKeysCommand /etc/ssh/getSSHKeyAD-trigger.sh # More on this later
#AuthorizedKeysCommandRunAs root

# 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

# Change to no to disable s/key passwords
#ChallengeResponseAuthentication yes
ChallengeResponseAuthentication no

# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no
#KerberosUseKuserok yes

# 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
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS

#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:30:100
#PermitTunnel no
#ChrootDirectory none

# 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

Final Steps

Now that all of the services have been configured, we need to restart them and flush their caches, if any.

$ service sssd restart
$ chkconfig sssd on
$ sss_cache -E
$ service sshd restart
$ service messagebus restart
$ service oddjobd restart

You should now be able to ssh into the machine using your AD credentials, like so:

$ ssh <AD USERNAME>@<HOST> -p <SSH PORT>

ex.
$ ssh [email protected] -p 22