I recently decided to move to centralized account management on some RHEL systems. There are a number of ways to do this, including NIS, kerberos, Active Directory, LDAP, or some combination thereof. Ease of configuration, administrative overhead, and security were the top priorities. I chose LDAP because it is relatively simple and has some support for authentication, encryption, and password hashing. There are definitely security issues, but I plan a separate post to specifically address LDAP security.
This document:
External resources:
- Red Hat LDAP deployment guide
- OpenLDAP quick start
- TLDP LDAP Howto
- TLDP LDAP Implementation Howto
- Security with LDAP
Server configuration
First, I install the needed software on the server. Use yum for RHEL/CentOS v5 and up2date for v4. Once the software was installed, I generated a hashed password for LDAP's root user. There are a number of hashing schemes to choose from in the 'slappasswd' manual.
# yum install openldap-servers openldap-clients openldapAfter typing in the password twice, you'll get a hash that should be pasted into /etc/openldap/slapd.conf, along with some other edits.
# cd /etc/openldap/
# slappasswd
suffix "dc=security,dc=test,dc=com"The default access control policy of allowing anonymous reads with Manager getting write access will need to be changed. Users need write access to their own passwords for 'passwd' to work, anonymous users should only be able to authenticate, and I will let authenticated users read everything but passwords. There are some problems with the default and example configurations in the slapd.conf comments. The following is what I used to enforce saner settings. The rootdn always has write access to everything. I will update if needed since I am still playing with OpenLDAP's access controls.
rootdn "cn=Manager,dc=security,dc=test,dc=com"
rootpw {SSHA}hashed_password
access to attrs=userpasswordOnce the basic server configuration is done, I start the LDAP daemon.
by anonymous auth
by self write
by * none
access to *
by self read
by users read
by anonymous auth
service ldap start
Client configuration
yum install authconfig openldap-clients nss_ldapConfiguring the client is somewhat confusing because Red Hat and many other distributions have two ldap.conf files. The one needed by nss_ldap is in /etc and OpenLDAP's client configuration file is in /etc/openldap.
I edited /etc/ldap.conf and /etc/openldap/ldap.conf for host, base, and the binddn and bindpw of proxyuser. proxyuser will be used for read access by nss_ldap since anonymous reads are disallowed. I also added Manager as rootbinddn, which requires creating /etc/ldap.secret with the plain text password, owned by root, and chmod 600. Both ldap.conf files need to be chmod 644.
OpenLDAP's client configuration file is much smaller and only needs a few changes. Most of the settings are the same though I did notice the TLS directives are different.
Next, I ran 'authconfig' or 'authconfig-tui' to edit /etc/pam.d/system-auth. From the menu, I selected to use LDAP Authentication and use LDAP for user information. Enabling LDAP Authentication will make local accounts unusable when LDAP is down! The server and base can be set here or manually edited in a ldap.conf file. 'authconfig' will edit /etc/nsswitch.conf to add ldap.
passwd: files ldap
shadow: files ldap
group: files ldap
Testing, using and modifying LDAP
Note that it is easier to test first using the default slapd.conf access controls that allows anonymous users to read rather than the controls above that I am testing. The below search is performed without authenticating.
# ldapsearch -x -b '' -s base '(objectclass=*)' namingContextsNow that it's working, I need to create an ldif (LDAP Data Interchange Format) file that will hold information to be put into LDAP. It includes a proxy user account that will have read access to LDAP using a password. You can use slappasswd to generate hashes for the password fields in the ldif. Note that my test user in the below LDIF is in the wheel group.
# extended LDIF
#
# LDAPv3
# base <> with scope base
# filter: (objectclass=*)
# requesting: namingContexts
#
dn:
namingContexts: dc=security,dc=test,dc=com
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
dn: dc=security,dc=test,dc=comTo load the information from the file into LDAP. Add '-ZZ' to issue StartTLS from any of the ldap* commands:
objectclass: top
objectclass: organization
objectclass: dcObject
o: NR Test Group
dc: security
dn: ou=groups,dc=security,dc=test,dc=com
objectclass: organizationalUnit
ou: groups
dn: ou=people,dc=security,dc=test,dc=com
objectclass: organizationalUnit
ou: people
dn: ou=role,dc=security,dc=test,dc=com
objectclass: organizationalUnit
ou: role
dn: cn=proxyuser,ou=role,dc=security,dc=test,dc=com
cn: proxyuser
objectclass: top
objectclass: person
objectclass: posixAccount
objectclass: shadowAccount
uid: proxyuser
uidNumber: 1001
gidNumber: 100
homeDirectory: /home
loginShell: /sbin/nologin
userpassword: don't use plain text
sn: proxyuser
description: Account for read-only access
dn: cn=N R,dc=security,dc=test,dc=com
cn: N R
objectclass: top
objectclass: person
objectclass: posixAccount
objectclass: shadowAccount
uid: nr
uidNumber: 1002
gidNumber: 10
homeDirectory: /home/nr
loginShell: /bin/bash
userpassword: don't use plain text
sn: R
ldapadd -x -ZZ -D "cn=Manager,dc=security,dc=test,dc=com" -W -f ldapinfo.ldifIf there are errors, they should give a hint about the reason. Once the command succeeded, I did a search to display all the information.
ldapsearch -x -ZZ -D "cn=Manager,dc=security,dc=test,dc=com" -W -b "dc=security,dc=test,dc=com"You can also specify search terms.
ldapsearch -x -ZZ -D "cn=Manager,dc=security,dc=test,dc=com" -W -b "dc=security,dc=test,dc=com" "cn=proxyus*" uidNumberOnce the clients are configured with LDAP and working properly, creating an account in LDAP will allow that account to SSH to all systems functioning as LDAP clients. Before trying to authenticate to systems other than the LDAP server, I setup TLS.
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# basewith scope sub
# filter: cn=proxyu*
# requesting: uidNumber
# proxyuser, role, security.test.com
dn: cn=proxyuser,ou=role,dc=security,dc=test,dc=com
uidNumber: 1001
# search result
search: 3
result: 0 Success
# numResponses: 2
# numEntries: 1
TLS
Right now, without any encryption, password hashes and a ton of other information would be streaming through the network if I was not using a local client. To check, I ran 'tcpdump' on the loopback and searched LDAP again.
tcpdump -v -i lo -X -s 0The results of the search could clearly be seen in the traffic as shown from the following snippet (with the hash removed).
LoginShell1.. /bin/bash08..userPassword1(.&{SSHA}The process for configuring TLS is addressed in Red Hat's FAQ. For /etc/openldap/slapd.conf I used the following:
TLSCertificateFile /etc/pki/tls/certs/slapd.pemIf not requiring certificate checks because of self-signing, /etc/openldap/ldap.conf will need TLS_REQCERT allow and according to the comments /etc/ldap.conf is set to default of tls_checkpeer no. I still needed to explicitly set tls_checkpeer no to fix a problem with sudo not finding the uid in the passwd file. Both client configuration files need ssl start_tls entries.
TLSCertificateKeyFile /etc/pki/tls/certs/slapd.pem
Administrivia
There are other things to consider when using LDAP accounts to login with SSH. For instance, I edited /etc/pam.d/sshd to have user home directories created when users log in the first time with SSH on a particular system:
#%PAM-1.0For this to work, you will also need to have UsePAM yes in your sshd_config. On RHEL, you can tell if you are getting errors related to PAM not being enabled in sshd_config.
auth required pam_stack.so service=system-auth
auth required pam_nologin.so
account required pam_stack.so service=system-auth
password required pam_stack.so service=system-auth
session required pam_stack.so service=system-auth
session required pam_mkhomedir.so skel=/etc/skel/ umask=0077
session required pam_loginuid.so
# tail -n 1 /var/log/secureAt one point in the process, I was getting an error at login:
Sep 18 17:11:58 slapd sshd[24274]: fatal: PAM: pam_open_session(): Module is unknown
id: cannot find name for user ID 1002To fix this, I checked the permissions of /etc/ldap.conf and also made sure the proxyuser was working properly. Without LDAP read permissions, the user name can't be mapped to the user ID. Since I am denying anonymous reads, I have to make sure proxyuser is set up properly.
[I have no name!@slapd ~]$
You may need to modify iptables rules to allow linux systems to connect on 389. Using TLS will not change the port by default.
There is a Red Hat FAQ about getting sudo to work with LDAP. It will not necessarily work out of the box with LDAP users and groups.