Valid HTML 4.01 Transitional

Setting Up LDAP

James F. Carter <jimc@math.ucla.edu>, 2012-02-10

There seems to be a shortage of howto documents telling how to actually set up LDAP. I'm doing this, and I'm documenting the procedure.

Index

Web links:

Introduction

LDAP is a directory service. Originally, telephone landline operating companies provided a service, accessible online via your landline, whereby you could give the Distinguished Name of someone, i.e. his Common Name and locality, and the human operator would read back his phone number. This directory service in digital form was codified in ITU X.500. However, implementers had complaints that X.500 was too complicated and used too much network bandwidth, and also required the OSX network protocl stack which was a non-starter, and so a subset was defined and implemented (on TCP/IP), under the name Lightweight Directory Access Protocol (LDAP) (RFC 4510).

The X.500 Directory Information Tree (DIT) is theoretically global but is supposed to be rooted in national root servers. In practice for LDAP, the maintainer of each server creates a plausible suffix for his own branch of the DIT and uses it without coordinating with regional or national authorities. For UCLA-Mathnet the suffix is dc=math,dc=ucla,dc=edu.

The objects sent out by a LDAP server are called entries. Each has a Distinguished Name (attribute code is dn) and arbitrarily many additional attributes as specified by a schema. Same-named attributes may appear more than once per entry, and OpenLDAP has an ad-hoc extension to keep them in a specific order. The distinguished name must be unique within the DIT sent out by a LDAP server, and is supposed to be unique globally. It consists of a comma separated list of key=value pairs where the keys are attributes in DIT entries. (It is not clear whether values may contain commas or spaces; they certainly can in X.500 Distinguished Names.) The distinguished name in LDAP is little-endian: the relative distinguished name (RDN) specific to the referent comes first, and the country code or equivalent toplevel unit comes last. (The order is big-endian in X.500.) For example, this is the Distinguished Name we are using for UCLA-Mathnet's webserver, in little-endian order:

cn=arachne.math.ucla.edu,ou=Hosts,dc=math,dc=ucla,dc=edu

dn is interpreted semantically as Distinguished Name, cn is Common Name, ou is Organizational Unit, and dc is Domain Component.

Requirements

In this project I am setting up LDAP on both my work nets and at home, but the requirements are fairly similar in both contexts.

Configuration Files and Passwords

I am installing OpenLDAP version 2.4, package openldap2 from OpenSuSE v11.4. Out of the box, it needs these files to be edited:

Before starting setup, you need to prepare some symbol space objects.

What are the security implications of using the LDAP superuser for replication versus a separate user?

It appears to be feasible to have a separate user for replication, but it looks to me that the security improvement from doing so is minimal. Therefore I have decided to punt on this: to use the LDAP superuser for replication and to allow its password to be present in the configuration database in plain text.

Initial Configuration For cn=config

SuSE's openldap2 by default relies on slapd.conf whereas I want to use runtime configuration. The easiest way to make that happen is to create a seed slapd.conf and convert it. Alternatively, if you have a working LDAP database you can copy it to another machine and it will work, but there is a chicken and egg issue here.

The initial file content goes like this (jimc's creation). Note: in this document I'm trying to show how to use runtime configuration maximally. There are several items below which could profitably be added to this configuration and converted. Update: In particular, I decided that using LDIF to load schemas, at the present stage of development, is just asking for trouble, so on a re-installation I included the schemas in slapd.conf.

Include kerberos.schema in the likely case that you are going to store your Kerberos KDC database in LDAP. See Kerberos Database in LDAP for details.

Warning! This file contains the LDAP superuser's password hash, and so it should have restrictive permissions. The ldap user has to read it, so the recommended permission is ldap 600.

# Minimal slapd.conf to create cn=config.  
# By jimc@math.ucla.edu, 2012-02-11

# Ends up in /var/lib/ldap/slapd.d/cn\=config.ldif
pidfile		/var/run/slapd/slapd.pid
argsfile	/var/run/slapd/slapd.args
TLSCertificateFile	/etc/ssl/hostcerts/host.crt
TLSCertificateKeyFile	/etc/ssl/private/host.key
TLSCACertificatePath	/etc/ssl/certs
# Ends up in /var/lib/ldap/slapd.d/cn\=config/cn\=module\{0\}.ldif
modulepath	/usr/lib/openldap/modules
moduleload	back_hdb.la
moduleload	back_ldif.la
moduleload	back_monitor.la
# Ends up in /var/lib/ldap/slapd.d/cn\=config/olcDatabase\={0}config.ldif
# Does not need a suffix.
database	config
rootdn		"cn=config"
rootpw		"{SSHA}WouldntYouLikeToKnow"
# Schemas, in order
include /etc/openldap/schema/core.schema
include /etc/openldap/schema/cosine.schema
include /etc/openldap/schema/inetorgperson.schema
include /etc/openldap/schema/rfc2307bis.schema
include /etc/openldap/schema/yast.schema
include /etc/openldap/schema/misc.schema
include /etc/openldap/schema/mathnet.schema
include /etc/openldap/schema/kerberos.schema

Now make sure that slapd is not running by accident, and execute this command line; append -u to test but not convert. It's essential that the files created be read-write for the ldap user, so slapd can read them. The -F directory has to already exist, owned by ldap, mode 700.

As distributed, slapd expects to find the dynamic configuration in /etc/openldap/slapd.d; this is probably a compiled-in default for various utilities. But mutable files properly are located in /var. Thus I have created /var/lib/ldap/slapd.d and made a symbolic link to it from /etc/openldap/slapd.conf .

mkdir /var/lib/ldap/slapd.d
ln -s /var/lib/ldap/slapd.d /etc/openldap/slapd.d
su -c "/usr/sbin/slaptest -f /etc/openldap/slapd.conf -F /etc/openldap/slapd.d -v " ldap

You now have a cn=config (runtime configurable) LDAP database with no content. Since we're doing runtime configuration, start slapd by:

/etc/init.d/ldap start

First test if you can connect to your slapd. During initial setup, it's going to be safest if you use the ldapi (UNIX socket) URI as shown. Do the command as root so you can read the secret.

ldapsearch -x -D cn=config -y root.secret -H ldapi:/// -b cn=config

Suggestion: redirect the output to a file because it dumps everything including the hardwired schemas.

Note on LDAPI: while OpenLDAP is compiled with a correct default for the pathname of the LDAPI socket, other packages cannot discover it, and so you will need to specify the pathname, URL encoded (i.e. slashes changed to %2f). Here is the correct value on a SuSE system:

ldapi://%2fvar%2frun%2fslapd%2fldapi/

Oops! I can't connect, invalid credentials (error 49). The reason is that I forgot to replace the sample password with the actual one in the slapd.conf.seed file. To fix, shut off slapd (important, do /etc/init.d/ldap stop), and edit /var/lib/ldap/slapd.d/cn\=config/olcDatabase\=\{0\}config.ldif replacing the value of olcRootPW:: (2 colons mean a base64 encoded value). Restart slapd. Now I can connect and see everything that slapd knows so far.

Here's a modified test command which only shows the root DN and password. (The frontend database is shown too but it doesn't have any root DN.)

ldapsearch -x -D cn=config -y /etc/openldap/root.secret -H ldapi:/// -LLL -b cn=config '(objectClass=olcDatabaseConfig)' olcDatabase olcRootDN olcRootPW

About the Schemas

Here is a description of the schemas that I added (order is significant). The sources are in /etc/openldap/schema/ .

  1. core.schema -- The basic object types that you have got to have, from RFC 2252, 2256, and several others.

  2. cosine.schema -- Attributes of varying usefulness from RFC 1274 and/or imported from X.500.

  3. inetorgperson.schema -- Attributes of a person from RFC 2798.

  4. rfc2307bis.schema -- Defines the objectClasses and attributes needed to represent the normal UNIX directory files such as /etc/passwd, /etc/hosts and /etc/services. Not including /etc/aliases.

  5. yast.schema -- I'm not entirely sure what this is used for, but of course on a SuSE system it should be included.

  6. misc.schema -- The main item here is a representation for /etc/aliases.

  7. mathnet.schema -- Declarations for the udata and hostgroup maps which are special creations at UCLA-Mathnet.

  8. kerberos.schema -- If you are going to put your Kerberos database in LDAP, it would be a good idea to include the schema now rather than appending it later.

I initially loaded the schemas successfully using dynamic configuration, but since you need to do the slaptest thing to convert the schemas to LDIF format anyway, I decided that it was more sane to include schema loading in the initial slapd.conf.seed. Beware, it is not possible to delete or alter a schema by dynamic configuration; you will give slapd a segfault if you try. Only cowboy programming will succeed. You need to shut down your slapd and edit the configuration database by hand.

These instructions are kept for historical reference. I used them successfully but they are superceded by include *.schema in slapd.conf.seed.

If you already have a working slapd configuration using runtime configuration (cn=config) with the schemas you want, you can just steal them; copy them someplace on the new machine. Look in /var/lib/ldap/slapd.d/cn=config/cn=schema/*.ldif of the old one.

If you don't have the LDIF files, you can create them by once again converting a slapd.conf file. Let's use /tmp/schemas.conf as its name; it would say just:

include /etc/openldap/schema/core.schema
include /etc/openldap/schema/cosine.schema
include etc. etc.

Now use this command line, where I'm using /tmp/schemas.d for the output:

mkdir /tmp/schemas.d
chown ldap /tmp/schemas.d
su -c "/usr/sbin/slaptest -f /tmp/schemas.conf -F /tmp/schemas.d -v " ldap
And then the LDIF files would appear in /tmp/schemas.conf//cn=config/cn=schema/*.ldif . You may want to save them somewhere permanent for the next time you do this, like /etc/openldap/schemas/*.ldif .

Wherever you stole the LDIF files from, edit your copies to remove the operational attributes. The running slapd does not want to see them; it wants to use the current values that it computes. In mine they all come at the end of the file, with these names: structuralObjectClass entryUUID creatorsName createTimestamp entryCSN modifiersName modifyTimestamp .

Also edit the Distinguished Name to be added (the first line starting with dn:). The stolen copy will begin like this:

dn: cn={0}core
objectClass: olcSchemaConfig
cn: {0}core

It needs to change to be like this. Remove the bracketed ordering numbers in dn: and in cn:, and to the dn append the suffix ,cn=schema,cn=config. The result should look like:

dn: cn=core,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: core

If you forget, you will get an error messsage Server is unwilling to perform (53) additional info: no global superior knowledge. It's telling you that it doesn't know where to put the new object in the tree, because the suffix is missing.

Now load the schemas (in order) by this command line (substitute the name of each LDIF file in turn):

ldapadd -x -D cn=config -y /etc/openldap/root.secret -H ldapi:/// -f core.ldif

If in the next step you get the error message: Invalid syntax (21) additional info: olcSuffix: value #0 invalid per syntax, it's telling you that your suffix (or other attribute value) has invalid syntax. At this stage of setup a very likely reason the syntax is invalid is that the schema containing that syntax has not been loaded, which should have happened in the previous step.

Turning On TLS (Encryption)

In slapd.conf.seed given above I included the TLS parameters so that TLS would be available from the beginning, for security when sending passwords. However, it is also possible to set up TLS by dynamic configuration. Here is the procedure that I formerly followed, for historical interest, together with a discussion of the TLS parameters themselves.

OpenSuSE provides the openssl package for this; certificates are stored in subdirectories of /etc/ssl. The required parameters are:

Here is the command to configure TLS:

ldapmodify -x -D cn=config -y /etc/openldap/root.secret -H ldapi:/// <<EOF
dn: cn=config
changetype: modify
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ssl/private/host.key
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ssl/hostcerts/host.crt
-
add: olcTLSCACertificatePath
olcTLSCACertificatePath: /etc/ssl/certs
-

EOF

And a command to test the configuration; it uses the Internet port (ldap:// vs. ldapi:// URI) and -ZZ makes it die if TLS cannot be set up.

ldapsearch -x -D cn=config -y /etc/openldap/root.secret -H ldap://walnut.math.ucla.edu/ -ZZ -b cn=config -s base -LLL olcTLSCertificateKeyFile

Security Strength Factors

The next job is to configure the level of security required for various tasks. See the man page for slapd.conf for details about security strength factors (ssf), but basically, the ssf is approximately the number of bits required for a brute force attack. That is, if the ssf were 4 than 24 = 16 attack attempts would be needed to cover all possibilities and be sure of cracking the security. 0 means no security is required, 1 means integrity checking only, and 128 is the minimum strength for modern ciphers (paranoid sysops are moving to 256 bit strength).

The parameters set here globally are inherited by all the databases. Update_ssf pertains to changing attributes, versus just reading them. In a simple bind a password is sent over the wire, and so the simple_bind parameter requires encryption. olcLocalSSF specifies the ssf which is assumed for LDAPI, i.e. access over UNIX sockets; it takes a root exploit to steal information out of the memory buffers attached to a UNIX socket, and so there is no need to set up TLS.

Here is the command and LDIF to set the ssf:

ldapmodify -x -D cn=config -y /etc/openldap/root.secret  -H ldapi:/// >>EOF
dn: cn=config
changetype: modify
replace: olcLocalSSF
olcLocalSSF: 128
-
replace: olcSecurity
olcSecurity: update_ssf=128 simple_bind=128
-
EOF

Creating the Payload Database

The first (and usually only) payload subtree is held in a bdb or hdb database. The documentation (Administrator's Guide v2.4 section 11.1) says they are very similar, and it is not too clear why you should pick one or the other. The provided sample slapd.conf file uses bdb. However, hdb can do subtree renames, and for maximum flexibility in recovering from bad design choices, that's the one I picked. Note the warning that idlcachesize (index cache) should be 3 times cachesize for hdb.

ldapadd -x -D cn=config -y /etc/openldap/root.secret -H ldapi:/// <<EOF
dn: olcDatabase={1}hdb,cn=config
olcDatabase: {1}hdb
objectClass: olcDatabaseConfig
objectClass: olcHdbConfig
olcDbDirectory: /var/lib/ldap/data.d
olcDbCheckpoint: 1024 5
olcDbCacheSize: 10000
olcDbIDLcacheSize: 30000
olcSuffix: dc=math,dc=ucla,dc=edu
olcRootDN: uid=ldaproot,dc=math,dc=ucla,dc=edu
olcRootPW:: {CLEARTEXT}WouldntYouLikeToKnow
olcAccess: {0}to attrs=userPassword by ssf=128 self write by * auth
olcAccess: {1}to attrs=shadowLastChange by ssf=128 self write by * read
olcAccess: {2}to attrs=userPKCS12 by ssf=128 self read by * none
olcAccess: {3}to * by * read
olcDbIndex: objectClass eq
olcDbIndex: entryUUID eq
olcDbIndex: entryCSN eq
olcDbIndex: cn eq,sub
olcDbIndex: uid eq,sub
olcDbIndex: uidNumber eq
olcDbIndex: gidNumber eq
olcDbIndex: oncRpcNumber eq
olcDbIndex: ipServicePort eq
olcDbIndex: ipServiceProtocol eq
olcDbIndex: ipProtocolNumber eq
olcDbIndex: ipHostNumber eq
olcDbIndex: hostGroup eq
olcDbIndex: member eq
olcDbIndex: memberUid eq
olcDbIndex: mail eq
olcDbIndex: displayName eq,sub
olcDbIndex: sn eq,sub
olcDbIndex: givenName eq,sub
EOF

Some notes about the database declaration:

Monitor Database

You need to create a monitor database, if only to shut up its incessant whining that you're missing out on the latest cool feature. Here is what you need to feed to ldapadd:

ldapadd -x -D cn=config -y /etc/openldap/root.secret -H ldapi:/// <<EOF
dn: olcDatabase={2}monitor,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {2}monitor
olcAccess: to dn.subtree=cn=Monitor by ssf=128 dn.exact=uid=ldaproot,dc=math,dc=ucla,dc=edu write by users read by * none
olcRootDN: cn=ldaproot,cn=Monitor
olcRootPW:: {CLEARTEXT}WouldntYouLikeToKnow
EOF

Replicated LDAP

Now it's time to engage replication. But which replication style is the best? Looking in the Admin Guide section 18.3, here are the available styles. For political correctness the guide refers to producer and consumer servers, rather than masters and slaves. Actually the same server is often both a producer and a consumer, so the change in terminology is useful.

Replicating the Payload

To set up N-way multi-master replication, first set up each server as a provider (master) of its payload database and a consumer of everyone else's. Here are some notes on the attributes:

ldapmodify -x -D cn=config -y /etc/openldap/root.secret -H ldapi:/// <<EOF
dn: cn=config
changetype: modify
add: olcServerID
olcServerID: 1 ldap://sunset.math.ucla.edu
olcServerID: 2 ldap://tupelo.math.ucla.edu
olcServerID: 3 ldap://walnut.math.ucla.edu

dn: olcOverlay=syncprov,olcDatabase={1}hdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov

dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcSyncRepl
olcSyncRepl: rid=001
  provider=ldap://sunset.math.ucla.edu 
  starttls=critical
  binddn="uid=ldaproot,dc=math,dc=ucla,dc=edu"
  bindmethod=simple
  credentials="WouldntYouLikeToKnow"
  searchbase="dc=math,dc=ucla,dc=edu"
  type=refreshAndPersist
  retry="60 1 300 12 7200 +"
  timeout=1
olcSyncRepl: rid=002
  provider=ldap://tupelo.math.ucla.edu 
  starttls=critical
  binddn="uid=ldaproot,dc=math,dc=ucla,dc=edu"
  bindmethod=simple
  credentials="WouldntYouLikeToKnow"
  searchbase="dc=math,dc=ucla,dc=edu"
  type=refreshAndPersist
  retry="60 1 300 12 7200 +"
  timeout=1
olcSyncRepl: rid=003
  provider=ldap://walnut.math.ucla.edu 
  starttls=critical
  binddn="uid=ldaproot,dc=math,dc=ucla,dc=edu"
  bindmethod=simple
  credentials="WouldntYouLikeToKnow"
  searchbase="dc=math,dc=ucla,dc=edu"
  type=refreshAndPersist
  retry="60 1 300 12 7200 +"
  timeout=1
-
add: olcMirrorMode
olcMirrorMode: TRUE
-

EOF

Do this on each server. The first server will fail to establish its connection to the others that haven't been configured yet, so you will have to either wait for the connection to be retried, or restart the server, which will then kill the connections to it, which will be restored after a (shorter) timeout. Be patient and watch syslog to identify when everyone has connected.

Now load users and other system tables into the payload database on one server. It doesn't matter which one. Suggestion: at first do just one entry and test it. Test if you can retrieve the content from the other servers. Here is a sample command line for testing, using my user entry. Substitute the Distiguished Name of the entry you added and pick an attribute that it actually has, instead of gecos, if it isn't a user.

ldapsearch -x -H ldap://walnut.math.ucla.edu -b uid=jimc,ou=People,dc=math,dc=ucla,dc=edu -LLL gecos

Replicating the Configuration

Turning on replication for the configuration database is very similar to doing it for the payload, and the Admin Guide actually suggests that you do this first, before setting up your payload database, which you would allow to be replicated. However I had no end of trouble, because altering or removing schema and database entries on a running server is really bad mojo. For example, my payload database would randomly disappear on some but not all servers. Maybe it would have worked in a pure master-slave configuration, but I decided to cut my losses, wipe the whole database, and start from scratch without replicating the configuration.

Populating LDAP

The configuration is now finished, and you can populate your LDAP server group with user and system tables. Putting this into practice is not exactly part of this document, but I have written:

Activating LDAP For Login

Now we need to find clients that use the directory service, and to tell them to use LDAP.

LDAP in /etc/nsswitch.conf

Most clients that use directory services go through library routines such as getpwnam and its numerous friends, and these all look at /etc/nsswitch.conf to know where to get the information.

In a NIS installation, most of the directory data is obtained preferentially from NIS, with a fallback to local files. The exceptions are passwd, shadow and group, where local system users need to override NIS if present. I have followed the same approach in my nsswitch.conf . This file omits the comments at the start of the file.

passwd:		files ldap
shadow:		files ldap
group:		files ldap
hosts:  	files dns 
networks:       files dns 
services:       files ldap 
protocols:      files ldap 
rpc:		ldap files
ethers:		ldap files
netmasks:       files
netgroup:       ldap files
publickey:	files
bootparams:     files
automount:      files
aliases:        ldap files 

According to the documentation LDAP is quite efficient, and on Solaris NIS is used for all tables except passwd, shadow and group, with a fallback to files. It is very tempting to put ldap first on as many tables as possible. But experience shows that this is not feasible on Linux because of noisy error messages produced at boot time by daemons which start before the network is up and LDAP is available. The settings shown above (probably) have ldap first on the maximum number of tables.

LDAP in Automounter

Our home directories are exported via NFS. Details of mounting are handled mostly by generic files that are the same for all clients, but there is one exception, the auto_home map. Whereas SuSE, Ubuntu, etc. normally put users' home directories directly under a locally mounted /home, we have extended this usage through automounting. Formerly /etc/auto.master had a row saying /home yp:auto.home. This is replaced by:

/home ldap:ou=Auto_home,dc=math,dc=ucla,dc=edu

rfc822bis.schema defines an objectClass called automount. Each user has an entry with (at Mathnet) a Distinguished Name of automountKey=${user},ou=Auto_home,dc=math,dc=ucla,dc=edu and with these attributes:

Apparently it's customary to put these objects as leaves under a container of objectClass automountMap, whose main purpose is to have an attribute of automountMapName. However, Mathnet has not done that; all tables are leaves of Organizational Unit objects, as is normal for rfc822bis. However, autofs complains automount[30569]: lookup_read_map: lookup(ldap): Unable to download entire LDAP map for: /home. Perhaps we need to put some effort into using the automountMap container.

At least on a SuSE system, autofs (or actually, its plugin /usr/lib64/autofs/lookup_ldap.so) consults /etc/sysconfig/autofs and looks for these settings, as used at Mathnet:

DEFAULT_MAP_OBJECT_CLASS="organizationalUnit"
DEFAULT_MAP_ATTRIBUTE="ou"
DEFAULT_ENTRY_OBJECT_CLASS="automount"
DEFAULT_ENTRY_ATTRIBUTE="automountKey"
DEFAULT_VALUE_ATTRIBUTE="automountInformation"

As mentioned, normally for this schema DEFAULT_MAP_OBJECT_CLASS="automountMap".

LDAP in Postfix

In /etc/postfix/main.cf the alias_maps parameter starts out as hash:/etc/aliases, nis:mail.aliases. The latter term should be changed to

alias_maps = hash:/etc/aliases, ldap:/etc/postfix/ldap-aliases.cf

The SuSE package provides a sample file called ldap_aliases.cf which is for a schema that we don't use. Apparently there are quite a number of alias schemas, each incompatible with the others. Ours uses an objectClass called nisMailAlias from misc.schema. Each user or other recipient has an entry of this class with a Distinguished Name of cn=${user},ou=Aliases,dc=${realm},dc=ucla,dc=edu where ${realm} is math or pic. The entry has two attributes:

The ldap-aliases.cf file which works for us looks like this:

# Testing LDAP alias map -- jimc, 2012-02-19
# See http://www.postfix.org/LDAP_README.html and man 5 ldap_table
# A lot of items are realm-specific (pic vs math).
# In main.cf set: 
# alias_maps = hash:/etc/aliases, ldap:/etc/postfix/ldap-aliases.cf

# Why couldn't they have utilized /etc/ldap.conf?  Needs sorthosts treatment.
# Port can be included; sorthosts needs to take this into account.
server_host = ldap://sunset.math.ucla.edu ldap://sumac.math.ucla.edu ldap://tupelo.math.ucla.edu ldap://walnut.math.ucla.edu

# Where to look for the aliases.  %u = user without @domain.
search_base = cn=%u,ou=Aliases,dc=math,dc=ucla,dc=edu

# The filter must be specified to override the lameass default.
query_filter = (cn=%u)

# The attribute(s) containing the alias (lookup value)
result_attribute = rfc822MailMember

# scope default is sub, but is irrelevant since these are leaf nodes.

# The alias map can be accessed anonymously and without TLS.
bind = no

LDAP for Kerberos

It is possible for Kerberos to store its database in LDAP, utilizing LDAP's replication capability, which is an area Kerberos is weak in. See this separate document about putting the Kerberos Database in LDAP.

LDAP in PAM

You first need to install the pam_ldap package. It gives you the module /lib64/security/pam_ldap.so, a man page for pam_ldap, and a bunch of sample PAM scripts in /usr/share/doc/packages/pam_ldap/pam.d . Per README.SuSE, you just need to edit /etc/security/pam_unix2.conf and add the "use_ldap" option for account, auth and password management. However, the currently provided pam_unix2.so does not read that file; it is sensitive to /etc/nsswitch.conf /etc/default/passwd and /etc/login.defs . It will be necessary to specify use_ldap on the command line of pam_unix2.so, or else explicitly call pam_ldap. The sample PAM scripts show how to do the latter strategy.

Add these parameters to /etc/ldap.conf. (These are already configured in the default /etc/ldap.conf .)

I tried adding use_ldap to the pam_unix2.so command line, but this was not successful. First, pam_ldap.so botches the start_tls negotiation; slapd thinks that it has set up TLS, but the client rejects the connection for no obvious reason. The cure for this was to specify server URLs of ldaps://host.fqdn (or LDAPI if the server is on the local host). Then remove explicit specification of the SSL parameter (none of off, on or start_tls). Then the client auto-switches to no TLS when using LDAPI, and it does not botch a LDAPS TLS connection.

The next problem is that when pam_unix2.so uses ldap it does not pass through the use_first_pass parameter, and so pam_ldap unconditionally asks again for the LDAP Password. This is really user unfriendly, and so we are going to have to specify pam_ldap.so in the PAM scripts. Here is how I have them set up:

Authentication

common-auth

For authentication there are three mechanisms: Kerberos, LDAP and files (pam_unix2.so). If one of them accepts the user we want to skip the others. The sample PAM scripts show one effective way to accomplish this: make each of them sufficient (and don't have any subsequent modules). We want Kerberos first because it can save the credential (ticket-granting ticket) and store it in the session phase. We want to do pam_ldap.so next, before pam_unix2.so, because pam_ldap checks authentication by doing a simple bind with the target user's password. Pam_unix2, in the likely case that it doesn't find the user in the local /etc/shadow, retrieves the hashed password from LDAP by binding with the root secret, and checks the password locally. This seems to me to be more heavyweight, so I set it up so pam_unix2 will be skipped most of the time.

Authorization (Account Phase)

common-account

For authorization, pam_krb5 checks ~/.k5login and password expiration (if configured, which we don't). Pam_ldap checks some authorization fields involving groups, which we don't use. Pam_unix2 checks password expiration. In reality Mathnet handles password aging and expiration in its own way, so all of these are irrelevant except .k5login, but in the authorization phase all the modules should be executed. Thus they are made required.

However, if the user is not in Kerberos or LDAP it will cause authorization to fail, which is not what we want. Thus I have added the ignore_unknown_principals parameter to pam_krb5 and both ignore_unknown_user and ignore_authinfo_unavail to pam_ldap. I have also added use_first_pass so pam_ldap so it won't ask for a password again. This is the default for pam_krb5 and I had trouble with it in the past, so I didn't put it there. It is also the default for pam_unix2, but it's OK to specify it explicitly as I have done.

Password Changing

common-password

For password changing, the sample files have all mechanisms required. I think this is a mistake: if any mechanism fails, we want to do the others anyway, because it's common for system users to be in files but not LDAP and Kerberos, or ordinary users to be in LDAP and Kerberos but not files. Cases in LDAP but not Kerberos will be common during initial deployment. Thus I've made all the mechanism calls optional.

They all also need use_first_pass so they will rely on the old password collected by pam_pwcheck.so. Try_first_pass is tempting, so a user whose password gets out of sync in the various mechanisms has a chance to fix the situation, but then if the user does not exist for a particular mechanism the module will try to improve the situation by asking for a possibly out-of-sync different password, which is going to be very annoying. Therefore I have specified use_first_pass for all mechanisms. It doesn't help to specify ignore_unknown_principal, at least for pam_krb5, but specifying the ignore_unknown parameters suppresses error messages in syslog for such users.

Session Setup

common-session

For session setup, all the mechanism calls are optional, for the same reason I did this in the password phase. In reality pam_ldap has nothing to do in this phase and is omitted in the sample PAM scripts.

Misfiled Tidbits

LDAP directory browser software:

Link to test script for setting up multi master

Look in admin guide sect. 12.8, Reverse Group Membership Maintenance, for a way to invert the group map to put group memberships into the posixAccount objects. See also man slapo-memberof .

Web resource, a book about LDAP: http://www.zytrax.com/books/ldap/