19 September, 2008

Setting up OpenLDAP for centralized accounts

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:

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 openldap
# cd /etc/openldap/
# slappasswd
After typing in the password twice, you'll get a hash that should be pasted into /etc/openldap/slapd.conf, along with some other edits.
suffix "dc=security,dc=test,dc=com"
rootdn "cn=Manager,dc=security,dc=test,dc=com"
rootpw {SSHA}hashed_password
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.
access to attrs=userpassword
by anonymous auth
by self write
by * none

access to *
by self read
by users read
by anonymous auth
Once the basic server configuration is done, I start the LDAP daemon.
service ldap start

Client configuration
yum install authconfig openldap-clients nss_ldap
Configuring 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=*)' namingContexts
# 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
Now 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.
dn: dc=security,dc=test,dc=com
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
To load the information from the file into LDAP. Add '-ZZ' to issue StartTLS from any of the ldap* commands:
ldapadd -x -ZZ -D "cn=Manager,dc=security,dc=test,dc=com" -W -f ldapinfo.ldif
If 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*" uidNumber
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base with 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
Once 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.

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 0
The 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.pem
TLSCertificateKeyFile /etc/pki/tls/certs/slapd.pem
If 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.

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.0
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
For 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.
# tail -n 1 /var/log/secure
Sep 18 17:11:58 slapd sshd[24274]: fatal: PAM: pam_open_session(): Module is unknown
At one point in the process, I was getting an error at login:
id: cannot find name for user ID 1002
[I have no name!@slapd ~]$
To 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.

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.

4 comments:

  1. by dn="cn=Manager,dc=security,dc=test,dc=com" write

    doesn't do anything as Manager is the rootdn user and bypasses all ACLs.

    Thought you'd like to know.

    ReplyDelete
  2. Gavin, thanks, I fixed it to point that out.

    ReplyDelete
  3. Thanks for a great article.

    I was wondering if you could post your /etc/ldap.conf and /etc/openldap/ldap.conf files on your client, as set up in this article.

    I'm having issues getting sudo to work, and I feel as though it is configured correctly on the server side (schema is correct, sudo nodes added to the db, etc), I think the client just doesn't know where to look.

    I can log in to the client with an LDAP user, but if I sudo -s, it just hangs. Doesn't ask for a password, just hangs. I'm assuming it is still checking the local sudoers file, and obviously not finding that user.

    In terms of flow, is the client doing something similar to the nsswitch.conf? In other words, if the sudoer isn't local, it goes over to the LDAP server to look for them?

    Any help you could provide me would be amazing.

    Thanks again.

    ReplyDelete
  4. J.P., thanks for the comment.

    To troubleshoot, start by checking your logs. Depending on your distribution or OS, /var/log/messages and /var/log/secure may be useful. Keep in mind that some distributions do not have LDAP support in the sudo package by default, for instance RHEL4.

    Finally, enable LDAP sudo debugging in /etc/ldap.conf.

    I address some of this, including enabling sudo debugging, in my second LDAP post. I also have a third one that focuses more on LDAP security.

    ReplyDelete