SubscribeMainXMLTagsEditHistoryDiscussion (1)

  1. Introduction
    1. More Information
  2. Security considerations
    1. Using encrypted connections
    2. Configuring the server not to show passwords
    3. Microsoft's Active Directory and password hashes
  3. Configuring the LDAP server
    1. Setting up Active Directory
      1. Modifying AD's Schemas
      2. Setting up SFU
      3. Client configuration notes
      4. Setting the maximum number of results allowed for a given search
    2. Setting up OpenLDAP
      1. Basic server configuration
      2. Creating a location for your users
      3. Creating users / groups with ldapadd
      4. Creating users / groups with useradd and family
      5. Changing users passwords
    3. Setting up eDirectory
      1. Client configuration notes
    4. Testing the Server
  4. Setting up the Linux client
    1. Setting up nss_ldap
      1. Configuring OpenLDAP (ldap.conf)
      2. Configuring NSS (nsswitch.conf)
      3. Testing things
  5. Things to watch for
    1. Home directories
      1. Using NFS
      2. Using pam_mkhomedir
  6. Troubleshooting
    1. Use a sniffer
    2. Use strace
    3. Make sure your configuration uses Unix format

Introduction

This document explains the steps necessary to configure GNU/Linux to use an LDAP server as its source of users, passwords and groups. We cover both the configuration of the server and the clients.

This document assumes some basic familiarity with LDAP as well as with the standard Unix mechanisms for managing users and groups. You might want to read A 5.000 words introduction to LDAP first.

Basically, we will use NSS to have glibc retrieve its information from the LDAP server in addition to the local files /etc/passwd, /etc/shadow and /etc/group and, optionally (see below), PAM to authenticate users.

Alejandro Forero Cuervo <azul@freaks-unidos.net> is the current maintainer of this document. Feel free to contact him or to help improve this document modifying it directly.

More Information

For more information on LDAP and authenticating with it, also see:

Security considerations

There are some important considerations that can greatly affect the security of your setup.

Using encrypted connections

The first is that you should probably use an encrypted connection (LDAPs instead of LDAP) between the clients and the server, unless you trust all the machines that receive the traffic between the clients and the server (such as computers connected to their same hub). If you use unencrypted connections, anyone running a packet sniffer on one such machine could catch any passwords sent over LDAP.

This howto, however, does not yet explain how to do that. I might explain it in the future (I haven't had time to make one such setup).

Configuring the server not to show passwords

The second consideration is that you will want to configure your LDAP server to only show password hashes to the users they belong to and perhaps to a certain admin accounts.

If you trust the client machines (that is, you can guarantee that attackers will not get root access to the client machines nor be able to remove their hard disks), you do not need to install the PAM LDAP module: it will suffice with configuring the NSS LDAP module and setting it to use one of the admin accounts (capable of downloading all the users' password hashes). In this case you'll need to set rootbinddn in the client and store its password in /etc/ldap.secret, which only root may read; more details on this later.

One example when this happens is when you're setting your email server as the LDAP client. In this case you want to make sure you can actually trust the email server, so it might be okay to store the password of an account capable of downloading all password hashes on it.

In the other hand, you might not trust the clients. For example, you are setting desktops to authenticate against a central server. Here you don't want to store the password of users allowed to retrieve the password hash of all users in these machines (even if the files are only readable by root). In this case you'll need to use PAM in addition to NSS. NSS won't be able to obtain password hashes but the PAM LDAP module will take care of authenticating users by attempting to connect to the LDAP server with the passwords they provide.

This howto does not yet explain how to setup PAM, only the first alternative. I will probably fix that soon (this note was added on Mar/29). Also, when configuring the servers, it doesn't yet explain how to properly set them up so standard users will only be able to see their own password hashes.

Microsoft's Active Directory and password hashes

Microsoft's Active Directory with Microsoft's Services for Unix treats the password hashes just as any other user attribute, showing them to any users. I have not been able to configure it otherwise. If you figure out how to do it, please drop me a line (or edit this document).

This makes it a very insecure alternative (since any user capable of connecting through LDAP can download the password hashes of all the users). I strongly advise you not to use it unless you can figure out how to change this behaviour or you somehow prevent your users from connecting (through LDAP) directly.

Configuring the LDAP server

This section explains the steps required to configure the LDAP server that holds the user's information.

For each Unix user we will need an object in the LDAP server. This object will have classes PosixAccount and ShadowAccount: they have an attribute for each of the fields usually stored in /etc/passwd and /etc/shadow respectively. These classes and their attributes are specified in RFC 2307.

Setting up Active Directory

If your LDAP server is Active Directory, you need to extend its schemas to include the above classes.

There are three possible options:

I recommend you use SFU, which is the option that worked for me. I explain how to do this with a little more details in the next section.

ad4unix seems to be discontinued. Most of the links you'll find to it point to an unexistant domain. There is a site in SourceForge but it doesn't seem to get much action. Altough, unlike MS SFU, ad4unix is free software (as in "speech"), its lack of recent activity makes me slightly distrust it. Then again, I've seen many reports of success with it.

Modifying AD's Schemas

If you decide to modify AD's schemas directly (as opposed to going for the SFU approach) you better be experienced with doing this. Once you've managed to create the schemas, the only drawback I see to this option compared with the other would be that you'll need to add the data for the users directly (for example, using an LDIF file), rather than being able to use MS's graphical console for creating and modifying users.

You'll need to create the classes PosixAccount and ShadowAccount, with all their attributes. Again, see RFC 2307. AD stores its schemas in the LDAP tree in CN=Schema,CN=Configuration,{SUFFIX}; then again, since you're experienced with doing this, you already know that.

Setting up SFU

You can download SFU for free directly from Microsoft. Run the setup.exe file to install it.

While installing SFU, make sure you install the NIS server. The default settings usually work, you won't need to change anything. For the Security Settings, you'll probably want to pick option two (TODO: specify better). Select Local User Name Mapping Server and leave the NIS domain name in blank.

Once you have SFU installed, you should be able to modify the Unix information for any of your users (which, by default, is disabled). Select a given user and edit his properties. Locate a "Unix Attributes" or similar tab and use it to enter information about him (his shell, home directory, user id, group id, etc.).

Note that there is no field to enter users' Unix password. This is because the system will use the standard passwords the Unix password. However, in order for this to work, you'll have to reset the user's password (after you installed SFU).

Client configuration notes

The schema created by SFU does not correspond exactly to the PosixAccount described in RFC 2307. The following fields in RFC 2307 are mapped to the following fields in SFU:

RFC 2307

SFU

uid

sAMAccountName

uidNumber

msSFU30UidNumber

gidNumber

msSFU30GidNumber

userPassword

msSFU30Password

homeDirectory

msSFU30HomeDirectory

loginShell

msSFU30LoginShell

gecos

name

PosixAccount
is mapped to the User class; PosixGroup to the Group class.

As a consequence, when modifying ldap.conf in the client (see below), you'll need the following lines:

nss_map_objectclass posixAccount User
nss_map_objectclass posixGroup Group
nss_map_attribute uid sAMAccountName
nss_map_attribute uidNumber msSFU30UidNumber
nss_map_attribute gidNumber msSFU30GidNumber
nss_map_attribute userPassword msSFU30Password
nss_map_attribute homeDirectory msSFU30HomeDirectory
nss_map_attribute loginShell msSFU30LoginShell
nss_map_attribute gecos name

Setting the maximum number of results allowed for a given search

Sometimes NSS_LDAP sends queries asking for all users. Active Directory never sends more than MaxPageSize results (defaults to 1000) to any given search. If you have more than 1000 users in your directory, you'll probably need to set this to a higher value.

Click Start then Run and execute ntdsutil, a command-line program to control many limits for Active Directory. Enter the following commands (without quotes) at its prompt:

  1. "ldap policies"
  2. "connections"
  3. "connect to server SERVER", where SERVER is your server's IP address or FQDN
  4. "q", to go back to the LDAP policies state
  5. Optional: "show values" to see your current limits.
  6. "Set MaxPageSize to N", where N is your new limit (such as 5000).
  7. Optional: "show values" to see your new limits.
  8. "Commit Changes", to apply your changes.

That should do it.

Setting up OpenLDAP

OpenLDAP is the prefered LDAP server in GNU/Linux.

Basic server configuration

Make sure you have package corresponding to OpenLDAP installed. In the case of SLES this is openldap; in the case of Debian and Debian-based distributions it is slapd.

In SLES, edit the configuration in /etc/sysconfig/openldap (and run SuSEconfig if you make any changes). Chances are you won't need to change anything, but it is good to be sure.

You will likely need to modify /etc/openldap/slapd.conf. In particular, you'll want to set the options suffix, rootdn and rootpw. Use something such as:

suffix "dc=ammonite,dc=bachue,dc=com"
rootdn "cn=Administrator,dc=ammonite,dc=bachue,dc=com"
rootpw "password"

suffix specifies the distinguished name under which your whole database will live. rootdn specifies the common name of your administrator and rootpw its password.

You can now run the OpenLDAP server (rcldap restart, insserv ldap in SLES).

Creating a location for your users

You'll need to create an organization and an organizational unit on which to place your users. The simplest way to do this is creating an LDIF file creating the new entries and then apply them to the database (with ldapadd).

Create a file, say start.ldif, with the following contents:

# The following entries define the organization:
dn: dc=ammonite,dc=bachue,dc=com
objectClass: dcObject
objectClass: organization
dc: ammonite
o: Ammonite
description: The Ammonite Corporation
# Now we create a organizational unit to hold our users:
dn: ou=users,dc=ammonite,dc=bachue,dc=com
objectClass: organizationalUnit
ou: Users
# And, finally, one to hold our groups:
dn: ou=groups,dc=ammonite,dc=bachue,dc=com
objectClass: organizationalUnit
ou: Groups

Apply the changes to the database with the following command:

ldapadd -f "start.ldif" -x -D "cn=Administrator,dc=ammonite,dc=bachue,dc=com" -w "secret"

That should be all you need to do.

Creating users / groups with ldapadd

You can create, modify and delete users with the ldapadd, ldapmodify and ldapdelete commands respectively.

To create a new user, make an LDIF file such as the following:

# Create a new user:
dn: cn=Alejandro Forero,ou=Users,dc=ammonite,dc=bachue,dc=com
objectclass: top
objectclass: inetOrgPerson
objectclass: PosixAccount
objectclass: ShadowAccount
cn: Alejandro Forero
sn: Forero
# The Unix login-name for the user:
uid: aforero
# The group and user IDs:
gidNumber: 1000
uidNumber: 1000
# The home directory for the user:
homeDirectory: /home/aforero
# The encrypted password for the user:
userPassword: {crypt}$1$qs70ynbk$UBuewN7ZdIvqavIxkxdmX0

Probably the best method to generate encrypted passwords is to use the following command:

openssl passwd -1 "secret"

To create a new group you can use an LDIF such as the following:

dn: cn=users,ou=groups,dc=ammonite,dc=bachue,dc=com
objectClass: top
objectClass: posixGroup
objectClass: person
cn: users
sn: users
gidNumber: 1000
memberuid: aforero

By default the posixGroup is an auxiliary class, not a structural class. You must either modify the OpenLDAP schemas to make posixGroup a structural class or, like I'm doing above, give your groups a specific structural class (in this case I'm using person, which requires that I add a sn attribute). Setting posixGroup as a structural class is probably a better solution (but then again, why is OpenLDAP shipped with PosixGroup as an auxiliary class?).

Once you have your LDIF file ready, apply it to the database:

ldapadd -f "user.ldif" -x -D "cn=Administrator,dc=ammonite,dc=bachue,dc=com" -w "secret"

Some versions have made posixGroup a structural class, in which case you will get an error like: "ldap_add: Object class violation (65) additional info: invalid structural object class chain (posixGroup/person)"

In this case, just remove the "objectClass: person" and "sn: users" entries and apply the file again.

Note that you could add multiple users in the same LDIF file.

Creating users / groups with useradd and family

You can use useradd, userdel and usermod to manage users in your LDAP database. This has the advantage of not requiring you to write LDIF files for every simple operation on the database, which can be cumbersome.

Note, however, that you must create your first user and group manually (using an LDIF file, for example). This is because the useradd and groupadd scripts shipped in SLES for users/groups manipulation need to find at least one user/group in the directory to know where to add further users/groups.

Edit /etc/ldap.conf and set the following options:

# Set to 127.0.0.1 to cause clients to connect locally.
host 127.0.0.1
# This is the distinguished name where our users are stored:
base ou=Users,dc=ammonite,dc=bachue,dc=com
# The user to bind with for administration task:
rootbinddn cn=Administrator,dc=ammonite,dc=bachue,dc=com

...

Changing users passwords

The ldappasswd program allows users to change their passwords. Please make sure they don't use it!

The format of the passwords in your database should always be {crypt}HASH, where HASH is some valid hash that will be returned by getspnam(3). If you use other methods for the passwords in your database (such as {MD5}, {SMD5}, {SHA} or {SSHA}), chances are getspnam(3) won't return the password so your users won't be able to login. In this case you might have some luck using LibPAM directly. If you figure out how to setup OpenLDAP to set the passwords in a manner such that getspnam(3) will return them (through NSS-LDAP, of course), please let me know.

The rest of this section explains how it would work, assuming it did.

You can run it as the administrator to reset users' passwords:

ldappasswd -D "cn=Administrator,dc=ammonite,dc=bachue,dc=com" -x -w "secret" \
   -s "newpasswd" "uid=gollum,ou=Users,dc=ammonite,dc=bachue,dc=com"

Regular users can also change their own password (but not other users').

See ldappasswd(1) or run ldappasswd –help for more information.

Setting up eDirectory

Client configuration notes

Some versions of Novell's eDirectory do not seem to return the cn attribute in the results of anonymous LDAP queries, which causes some versions of NSS LDAP not to see the users. If you setup NSS LDAP to bind anonymously, you might want to make sure eDirectory returns the cn attribute. Alternatively, you could set up your client to map it to the uid attribute, with the following entry (in /etc/ldap.conf):

nss_map_attribute       cn      uid

Testing the Server

You should now create a user in the LDAP server and test it from the client to make sure it can bind to the LDAP server and download the data for a given user. You should be able to obtain the correct values for the home directory, login shell, user id, group id, etc..

You can browse the LDAP tree using the Java-based LDAP Browser/Editor or the tools provided with OpenLDAP.

The following is an example of how I download all the information from one of my users:

ldapsearch -x -w $PASSWORD \
    -D "CN=Administrator,CN=Users,DC=winlab,DC=bog,DC=novell,DC=com" \
    -b "CN=Users,DC=winlab,DC=bog,DC=novell,DC=com" \
    msSFU30Name=$USER

You'll obviously need to replace the parameter to -D with the distinguished name of the user you're accessing the server as; $PASSWORD as the password of the user passed in -D; the parameter to -b with the base where you included your users; and specify the actual user you're downloading (or leave out the msSFU30Name=$USER to download the data for all your users).

Note that the above query is specific for Active Directory with MS SFU. For another directory you might want to use CN or Login instead of msSFU30Name.

Setting up the Linux client

Setting up nss_ldap

You need to install nss_ldap and configure it.

I recommend you install it using your distribution's packages:

Configuring OpenLDAP (ldap.conf)

The first thing you need to do is edit /etc/ldap.conf (on some distributions this file is installed in other locations, such as /etc/ldap/ldap.conf or /etc/openldap/ldap.conf). In this file you'll define how to connect to the LDAP server.

You'll need to define the following entries in this file:

host The IP address of the LDAP server.

base The distinguished name of the search base.

rootbinddn The distinguished name that root users to bind to the server. In an AD you could use something like CN=Administrator,CN=Users,DC=lab,DC=bachue,DC=com.

nss_base_passwd, nss_base_shadow and nss_base_group: The distinguished name in which to search for entries associated with passwd, shadow and group respectively.

ssl no: I *think* you need to explicitly disable SSL.

If the data in your LDAP server does not exactly match the classes defined in RFC 2307, you'll need to define nss_map_objectclass and nss_map_attribute entries for the classes posixAccount and posixGroup and the attributes uid, uidNumber, gidNumber, userPassword, homeDirectory, loginShell and gecos. There is more information about this above, in the sections for configuring the servers.

Also, create a file /etc/ldap.secret. Its sole contents should be the password of the user specified in rootbinddn above. You could create it with a command such as:

echo "secret" >/etc/ldap.secret

After creating this file be sure to set permissions so that only root can read it with:

chmod 600 /etc/ldap.secret

Configuring NSS (nsswitch.conf)

When applications need information from /etc/passwd, /etc/shadow or /etc/groups, they don't read the files directly but instead call certain functions from the C library (such as getpwent(3)). These functions first open the /etc/nsswitch.conf file, which specifies where to look for the requested information.

Typically, /etc/nsswitch.conf will contain the following lines:

passwd: compat
shadow: compat
group: compat

Sometimes you'll find the word "files" instead of "compat". You need to change these three lines to:

passwd: files ldap
shadow: files ldap
group: files ldap

This means that there are two sources for the information that would usually be read from /etc/passwd, /etc/shadow and /etc/group. The first source, "files", means that the system will try to read this information from the files directly. Should a match (for whatever the system is requesting) be found, it will be returned. When nothing is found, the system will use LDAP as its second source.

Note that you do *not* need to setup PAM to use pam_ldap! If your distribution is fairly recent, it should use pam_unix2.so, which uses getpwent(3) and similar functions, which in turn use the nss_ldap module.

After you've changed this file, you might need to restart the Name Service Cache Daemon (nscd), using a command such as /etc/init.d/nscd restart (or rcnscd restart in the case of SLES).

Testing things

For every user $user in the AD, you should now be able to:

Things to watch for

Home directories

You might need to make sure that whenever a user logs in, her home directory actually exists.

Whether you actually need this or not will depend on the kind of service provided by the client (in the LDAP connection) machine. For example, if the machine simply provides access through IMAP (running UW IMAPD or Cyrus), this won't be necessary. However, if you expect the users to initiate full sessions on the machine (such as connecting through SSH or XDMCP), it will.

There are (at least) two solutions for this.

Using NFS

First, you can, as always, use NFS (or even Samba) to mount /home (or whatever directories you have home directories in for the users in the LDAP server) from a central server. The advantages should be obvious: no matter what machine the user logs into, his entire home directory will be automatically available. If you want to centralize authentication, you'll probably also want to make all users see a relatively similar environment in all the machines.

The only drawback I can see to using NFS is that it isn't very scalable and will usually (unless you use some advanced setup such as a cluster, probably involving DRBD) leave a single point of failure for your entire network: if the NFS server goes down, so does your network.

Using pam_mkhomedir

So the other solution is using the PAM mkhomedir module, which, whenever a user logs in, right before allowing him to work, will check whether his home directory exists or not. If it doesn't, it will create it and copy the skeleton (usually from /etc/skel).

To use this module you'll need a pam_mkhomedir.so file in your /lib/security directory (or wherever your PAM modules live). You can enable it by adding the following line to the PAM file for the service that your users will log in through (which is usually stored in /etc/pam.d):

session required pam_mkhomedir.so skel=/etc/skel/ umask=0022

Troubleshooting

Use a sniffer

If something does not work, your first check should be to use a sniffer to inspect any packets sent to the LDAP server and their replies.

I would advice you to use the excellent Wireshark (previously known as Ethereal) sniffer, which takes care of decoding the LDAP packets for you and presents the information in a very easy to browse manner. Another fine choice would be tcpdump.

If you spot something wrong in the packets, it should point you in the direction of the error, either the client or the server.

Note that when you consult the passwd database (eg. getent passwd or id USER), the results of the LDAP search queries must have the following attributes:

If one of these required fields is not present, you can always use an nss_map_attribute directive in /etc/ldap.conf to map it to one other (present) attribute.

Use strace

In some distributions some commands will not result in any LDAP messages getting sent, even though you have properly configured NSS as described above. You can use strace to see all the system calls performed by the command, which will allow to see, for example, whether it reads /etc/nsswitch.conf or not (if it doesn't, something is obviously broken).

Make sure your configuration uses Unix format

Sometimes, specially if you have transfered your configuration files from Windows machines, the lines are terminated with CR LF (bytes 13 and 10) instead of just LF. In this case, some Unix programs might be unable to read them correctly.

You can hexdump file to see a bytes-dump of file. Should your file use CR LF to terminate lines, there are many ways to fix it:

There have been reports of problems involving /etc/ldap.conf. However, the NSS-LDAP software may soon be improved to be able of handling files in DOS format (see here).

Last update: 2007-06-01 (Rev 11491)

svnwiki $Rev: 14721 $