Differences between revisions 10 and 31 (spanning 21 versions)
Revision 10 as of 2008-12-03 15:29:11
Size: 45452
Comment: slapd.d sucks, easy-rsa rocks, openssl vs gnutls is a flaming pile of poo, help
Revision 31 as of 2009-08-16 10:35:53
Size: 213
Editor: 114
Comment: -42
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
/!\ This is a work in progress!
= Introduction =

The point of this document is to explain to sysadmins how to set up LDAP as a distributed naming and authentication service for a small network. There exist plenty of guides that purport to do this, but they're all just cargo-cult "copy this and run this command" without any explanation of what's actually going on or how to fix it when it breaks. [http://techpubs.spinlocksolutions.com/dklar/ldap.html Debian GNU: Setting up OpenLDAP] is somewhat of an exception to the rule, so if you're really short on time check it out. This guide includes parts of it but also covers areas it doesn't. Some analogies to SQL will be made, as that's the dominant database metaphor at the moment.

LDAP can be used for much more than just naming and authentication, but this is by far the most common case and often the base for more advanced setups. (The second most common case is as an address book, which might be dealt with in a later supplement.) If you want a full-on introduction to LDAP, this guide is not it - there are several listed in the references.

This guide will focus on using the OpenLDAP server, as it's the premiere open source LDAP server and is widely packaged. Other LDAP servers can be used and much of the information will be applicable to them, but performance and OpenLDAP-specific overlays will not. You can use Debian lenny or Ubuntu 8.04 on the server, the instructions should work for both. If you want to use some other distribution or OS, find another guide or work out how to adapt what I say. Notably, Ubuntu 8.10 defaults to the slapd.d config format, which is theoretically better due to the configuration being editable over LDAP, however in practice is worse for the beginner as there are currently no high-level tools that use that. Also running OpenLDAP under Xen isn't recommended due to a [http://www.openldap.org/lists/openldap-software/200603/msg00214.html lack of support for an assembly mutex], although [http://www.openldap.org/lists/openldap-technical/200804/msg00057.html this might be fixed now]. On the client side, configuration for Linux, Solaris, OS X, Tru64 and Windows (using Samba) will be discussed.

[[TableOfContents]]

= LDAP Basics =

So, what is LDAP? The acronym expands to "Lightweight Directory Access Protocol", and it was originally intended to provide IP (lightweight) access to X.500 servers, which required the use of the heavy OSI stack. Over time, directory servers that could be directly accessed by LDAP were written, cutting X.500 out of the loop. X.500 was a directory system designed by telcos in the 80s, back when the ISDN was the future of telephony. As such, it has several design decisions that presume a central delegating authority which have been inherited by pure LDAP servers. X.509, the security/certificate specification is one of these, and it lives on as the base of SSL/TLS, the internet PKI.

While LDAP strictly refers to the protocol, it is commonly used to refer to the database accessed via the protocol, which is more correctly called the Directory Information Tree (DIT). The DIT consists of a tree structure of entries, each of which belongs to a set of object classes and has a set of key/value attributes. An entry is the fundamental object of LDAP, and its type is determined by the values of its objectClass attribute. Common values include inetOrgPerson, posixAccount and organization.

Each objectClass is defined in a schema that lists the mandatory and optional attributes, which are themselves specified in schema. [2] For this document the important schema files are core.schema, inetorgperson.schema, cosine.schema <insert more>. An objectClass has one or more SUPerior objectClasses, from which it inherits the list of MUST and MAY attributes, and come in three types - ABSTRACT, STRUCTURAL and AUXILIARY. Every entry must have one structural objectClass, and any number of auxiliary objectClasses. Abstract objectClasses are only used to inherit from, and all objectClasses must derive from the abstract top objectClass. The limitation on an entry having only one structural objectClass can be worked around by creating a new structural objectClass that inherits from all the structural objectClasses required. This is getting into creating schema however and is beyond the scope of this document. While an entry can only have one structural objectClass, the superior objectClasses can be listed, style issue. Attribute inheritence

The tree is composed of entries, which can be both leaves and nodes. The path from an entry to the base of the tree identifies an object and is referred to as its distinguished name (DN), e.g. uid=trs80,ou=people,dc=example,dc=com. The components of the DN are the naming attribute? RDN? of each level of the tree, separated by commas. The naming attribute can be any attribute [3] but there are conventions detailed below, and it must be unique at that level of the tree, although it need not be globally unique. DNs and attribute names are generally case-insensitive, so UID=TRS80,ou=People,dc=Example,dc=COM is the same as the previous example.

The original X.500 specification envisioned a global directory service, divided by geographical regions and then companies, resulting in DNs like o=Widgets Inc.,st=Western Australia,c=AU. This doesn't map well to the internet, so the domain objectClass was defined in RFC ???? (and is included in cosine.schema) which MUST have a domainComponent (dc) attribute [4] that is by convention used as the RDN. So a domain like engineering.example.com is split at the periods to become the DN dc=engineering,dc=example,dc=com.

Administration domains. At this point you might be wondering who runs the dc=com domain and provides referrals. The answer is nobody - instead, PTR records in the DNS are used.

== Filters ==

LDAP filters are the equivalent of the SELECT statement in SQL, but are formed of s-expressions. The full syntax is specified in RFCs ???? and ????, but the basic format is (attribute=value). Boolean operations are written like (&(a=b)(c=d)(e=f)) where & is the symbol for logical and, | is logical or, ! is logical not etc. Substring matching is allowed if the schema specifies it for that attribute...


== LDIF ==

LDAP entries are transmitted over the wire in a binary ASN.1 format called BER, but there's an alternative text representation, the LDAP? Data Interchange Format (LDIF). This is specified in RFC ????, and there are several extensions, but the core of the format is <code>attributeName: value</code>. Here's the LDIF for an example entry:

<code> </code>

Binary attributes are base64 encoded, and the separator is change to be two colons (::) to indicate this. This also applies to ASCII characters outside a safe range.

== LDAP as a name service ==

A name service is one of those infrastructure things that mostly just sits behind the scenes and works. More details.

To use LDAP as a naming service, entries need to be created for the accounts in the DIT. These entries typically have an objectClass of posixAccount as specified in RFC 2307, however this is an auxiliary objectClass so we still need a structural objectClass. For this document we'll use inetOrgPerson, as it contains a useful set of attributes that can be used for other things later on, such as mail, <insert more>.

== Authentication ==

LDAP can be used as an authentication service in several ways. The easiest, and the one used here, is to perform a 'simple bind' operation where the password provided by the user is used to authenticate against the LDAP server. Other methods include SASL and client certificates. Kerberos binds can also be used, but are not much use as an authentication service since it requires an existing Kerberos authentication service. SASL offers the ability to use a variety of methods that ensure the password isn't passed in plaintext over the network, however most of thses require that the password is stored in plaintext on the server. In my opinion, having everyone's password in plaintext is a much bigger risk (link to reddit) than sending it over the wire, particularly since SSL/TLS provides wire encryption these days. SASL/SRP doesn't suffer from the plaintext problem, but it's not implemented widely (or arguably, at all) and so is not an option. SASL/EXTERNAL uses client certificates, which if you're happy to deploy and maintain you have enough expertise to not require this guide.

The authentication process takes two steps - first, a search is conducted for the full DN using the provided username in the subtree specified in the configuration, then a bind is performed with the DN and the provided password.

= OpenLDAP Setup =

OpenLDAP is the best-performing and most powerful LDAP server available.
Unfortunately it has historically been somewhat poorly documented and
unfriendly to newbies. The [http://www.openldap.org/doc/admin24/ admin guide for 2.4] is good, but still requires a certain level of background knowledge. So I'll take the traditional method of
providing a slapd.conf, but unlike other guides I'll explain what's going on.

== Installation ==

First, install the OpenLDAP server (commonly referred to as `slapd`):

{{{apt-get install slapd ldap-utils}}}

Debconf will ask some questions (I recommend you use `pwgen` to generate a random password):

 Administrator password:: PASSWORD
 Confirm password:: PASSWORD

The following questions that are only asked if you have debconf at low
priority or are using Debian, otherwise they're set to the defaults below:

 Omit OpenLDAP server configuration?:: No
 DNS domain name:: `hostname -d`
 Organization name?:: `hostname -d`
 Database backend to use:: HDB
 Do you want the database to be removed when slapd is purged?:: No
 Allow LDAPv2 protocol?:: No

The slapd package creates a basic `slapd.conf` and populates the directory with
two entries. First, let's look at `/etc/ldap/slapd.conf`:
{{{# This is the main slapd configuration file. See slapd.conf(5) for more
# info on the configuration options.

#######################################################################
# Global Directives:

# Features to permit
#allow bind_v2}}}

LDAPv2 is deprecated, and not needed unless you have Tru64 clients.

{{{# Schema and objectClass definitions
include /etc/ldap/schema/core.schema
include /etc/ldap/schema/cosine.schema
include /etc/ldap/schema/nis.schema
include /etc/ldap/schema/inetorgperson.schema}}}

These are the schema files that cover the most common LDAP use-cases.

{{{# Where the pid file is put. The init.d script
# will not stop the server if you change this.
pidfile /var/run/slapd/slapd.pid

# List of arguments that were passed to the server
argsfile /var/run/slapd/slapd.args

# Read slapd.conf(5) for possible values
loglevel none}}}

Standard configuration items. I'm yet to work out what the best value for the loglevel is, but `none config stats` seems reasonable. `slapd` logs using the local4.debug facility, which by default ends up in `/var/log/debug`. To make it go elsewhere, add the following to `/etc/syslog.conf`: `local4.debug /var/log/slapd.log` with tabs, not spaces.

{{{# Where the dynamically loaded modules are stored
modulepath /usr/lib/ldap
moduleload back_hdb}}}

back_hdb is a backend storage module. It uses Berkeley DB and is largely the same as back_bdb, except it stores entries in a hierarchical fashion (hence '''h'''db) giving greater performance. The modules in `/usr/lib/ldap` include other backends (starting with `back_`) that can be used in backend/database statements (see below) and overlay modules (man pages start with `slapo-`) that can be used in overlay statements (none in this example file, but see replication). Overlay statements in particular are very sensitive to ordering, however I don't know of any documentation that lists a general order - people often resort to trial and error to get it right.

{{{# The maximum number of entries that is returned for a search operation
sizelimit 500}}}

If you have a large database you may want to tune this, or consider adding a larger limit for authenticated users.

{{{# The tool-threads parameter sets the actual amount of cpu's that is used
# for indexing.
tool-threads 1

#######################################################################
# Specific Backend Directives for hdb:
# Backend specific directives apply to this backend until another
# 'backend' directive occurs
backend hdb}}}

All bdb options can be used for hdb as well.

{{{#######################################################################
# Specific Backend Directives for 'other':
# Backend specific directives apply to this backend until another
# 'backend' directive occurs
#backend <other>

#######################################################################
# Specific Directives for database #1, of type hdb:
# Database specific directives apply to this databasse until another
# 'database' directive occurs
database hdb}}}

Why is there another line saying hdb? slapd can handle multiple independent databases, eg an ISP might have a staff database `ou=internal,dc=example,dc=com` and several customer databases `dc=example,dc=org` and `dc=example,dc=net` served from a single slapd instance. Each database can also have its own set of overlays.

{{{# The base of your directory in database #1
suffix "dc=daa,dc=com,dc=au"

# rootdn directive for specifying a superuser on the database. This is needed
# for syncrepl.
# rootdn "cn=admin,dc=daa,dc=com,dc=au"

# Where the database file are physically stored for database #1
directory "/var/lib/ldap"}}}

Each database is identified by its suffix, and needs its own directory to store
files in. The rootdn identifies the superuser who can add, delete and modify
any entry. You don't have to have one if your ACLs are sufficient (see below).

{{{# The dbconfig settings are used to generate a DB_CONFIG file the first
# time slapd starts. They do NOT override existing an existing DB_CONFIG
# file. You should therefore change these settings in DB_CONFIG directly
# or remove DB_CONFIG and restart slapd for changes to take effect.

# For the Debian package we use 2MB as default but be sure to update this
# value if you have plenty of RAM
dbconfig set_cachesize 0 2097152 0

# Sven Hartge reported that he had to set this value incredibly high
# to get slapd running at all. See http://bugs.debian.org/303057 for more
# information.

# Number of objects that can be locked at the same time.
dbconfig set_lk_max_objects 1500
# Number of locks (both requested and granted)
dbconfig set_lk_max_locks 1500
# Number of lockers
dbconfig set_lk_max_lockers 1500}}}

Tuning Berkeley DB is somewhat of a black art, see below for more details. `slapd` has several levels of caching, so tends to use a lot of memory.

{{{# Indexing options for database #1
index objectClass eq}}}

Indexes are used for optimising searches, just like in SQL. As the comments
note, these statements apply to a specific database, so you can tune them
individually.

{{{# Save the time that the entry gets modified, for database #1
lastmod on}}}

This adds four useful operational attributes, modifiersName, modifyTimestamp,
creatorsName, and createTimestamp. It's also needed for replication.

{{{# Checkpoint the BerkeleyDB database periodically in case of system
# failure and to speed slapd shutdown.
checkpoint 512 30}}}

More BDB tuning.

{{{# Where to store the replica logs for database #1
# replogfile /var/lib/ldap/replog}}}

This is cruft left over from the old replication method and can be deleted.
The new replication method (syncrepl) is discussed below.

{{{# The userPassword by default can be changed
# by the entry owning it if they are authenticated.
# Others should not be able to see it, except the
# admin entry below
# These access lines apply to database #1 only
access to attrs=userPassword,shadowLastChange
        by dn="cn=admin,dc=daa,dc=com,dc=au" write
        by anonymous auth
        by self write
        by * none

# Ensure read access to the base for things like
# supportedSASLMechanisms. Without this you may
# have problems with SASL not knowing what
# mechanisms are available and the like.
# Note that this is covered by the 'access to *'
# ACL below too but if you change that as people
# are wont to do you'll still need this if you
# want SASL (and possible other things) to work
# happily.
access to dn.base="" by * read

# The admin dn has full write access, everyone else
# can read everything.
access to *
        by dn="cn=admin,dc=daa,dc=com,dc=au" write
        by * read

# For Netscape Roaming support, each user gets a roaming
# profile for which they have write access to
#access to dn=".*,ou=Roaming,o=morsnet"
# by dn="cn=admin,dc=daa,dc=com,dc=au" write
# by dnattr=owner write}}}

Access control can form a large part of `slapd.conf` - it has an [http://www.openldap.org/doc/admin24/access-control.html entire chapter] in the administration guide. The rules here restrict
access to passwords, so people can authenticate against their own, but
can't see other people's. The second and third set up basic ACLs that
you'll probably want to customise later on. Note the statements are wrapped
over several lines - any line starting with whitespace is considered to be
a continuation of the previous line.

ACLs are evaluated in the order specified in the file, with the first match
being used.

{{{#######################################################################
# Specific Directives for database #2, of type 'other' (can be hdb too):
# Database specific directives apply to this databasse until another
# 'database' directive occurs
#database <other>

# The base of your directory for database #2
#suffix "dc=debian,dc=org"}}}

This is a stub to show how to set up a second database. You can read up on all the options available in the slapd.conf(5) and slapd-bdb(5) man pages. There's also another way of configuring slapd which uses ldif files in a directory called `slapd.d` and allows configuration over LDAP, which is [http://www.openldap.org/doc/admin24/slapdconf2.html covered in the administration guide], but since it's less obvious than a traditional config file I'm not going to cover it here.

== Utilities ==

OpenLDAP comes with a variety of utilities, both client- and server-side. In general, commands starting with `slap` are server-side, operate on the database directly and so you have to be root or the openldap user on the same machine as slapd to use them, while commands starting with `ldap` are client-side, operate using the LDAP protocol and can be run as anyone including over the network, but will require authentication for some tasks.

Let's start with the server-side commands. As root, run `slapcat` and you should see something similar to the following:
{{{dn: dc=daa,dc=com,dc=au
objectClass: top
objectClass: dcObject
objectClass: organization
o: daa.com.au
dc: daa
structuralObjectClass: organization
entryUUID: c2a8b180-ee60-102c-9e7b-914c836f2853
creatorsName:
createTimestamp: 20080725064340Z
entryCSN: 20080725064340.037379Z#000000#000#000000
modifiersName:
modifyTimestamp: 20080725064340Z

dn: cn=admin,dc=daa,dc=com,dc=au
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: admin
description: LDAP administrator
userPassword:: d2VsbCBkb25lLCB5b3UgY2FuIGJhc2U2NCBkZWNvZGU=
structuralObjectClass: organizationalRole
entryUUID: c2a95b62-ee60-102c-9e7c-914c836f2853
creatorsName:
createTimestamp: 20080725064340Z
entryCSN: 20080725064340.041861Z#000000#000#000000
modifiersName:
modifyTimestamp: 20080725064340Z}}}

This is the LDIF for the two entries the slapd package created when it was installed. The first is the top of the LDAP tree, `dc=daa,dc=com,dc=au`. All other entries in the tree will be added below this entry. A common trick encountered when installing OpenLDAP without using the packages is that this entry must be created before you can add any other entries, otherwise you get the error [http://www.openldap.org/faq/data/cache/157.html ldap_add: No such object]. The second entry is the database administrator. This object doesn't need to exist, you could just put the `rootpw` in `slapd.conf`, however if it's in the database you can [http://www.openldap.org/doc/admin24/access-control.html#Controlling%20rootdn%20access use ACLs] to limit which hosts the ''rootdn'' can authenticate from.

Note that ''creatorsName'' and ''modifiersName'' are empty. This is because they were added using `slapadd`, which bypasses `slapd` and directly writes to the database. This is a two-edged sword - it does allow you to set operational attributes, which are normally managed by the server, but it also allows you to fuck up your database if you're not careful. Generally `slapadd` is only used when restoring from backup (created using `slapcat`), particularly since it requires the `slapd` to be stopped.

The client-side commands start with `ldap` and use the LDAP protocol to talk to the LDAP server. They use the libldap library, which has defaults configured via `/etc/ldap/ldap.conf`. Add the following lines to it so you don't have to enter them on the command line all the time: {{{BASE dc=example,dc=com
URI ldap://your.ldap.server/}}}See the `ldap.conf(5)` man page for other options that can be set.

Now, as your regular user, run `ldapsearch -x` (if you hadn't edited `ldap.conf` you'd have to run `ldapsearch -x -b dc=example,dc=com -H ldap://your.ldap.server/`) and you should get something similar to the following:
{{{# extended LDIF
#
# LDAPv3
# base <dc=daa,dc=com,dc=au> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# daa.com.au
dn: dc=daa,dc=com,dc=au
objectClass: top
objectClass: dcObject
objectClass: organization
o: daa.com.au
dc: daa

# admin, daa.com.au
dn: cn=admin,dc=daa,dc=com,dc=au
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: admin
description: LDAP administrator

# search result
search: 2
result: 0 Success

# numResponses: 3
# numEntries: 2}}}

As you can see, `userPassword` isn't shown, since anonymous users don't have permission to read it. Also the operational attributes were not returned. To see them, run `ldapsearch -x +` - `+` is an OpenLDAP extension to request all attributes. The syntax for `ldapsearch` is `ldapsearch [''options''] [''LDAP filter''] [''list of attributes'']. All the options are listed in the `ldapsearch(1)` man page, but the most useful ones are `-x` for simple authentication instead of SASL (there's no way to default to using simple auth, so you'll always be using `-x`), `-D ''binddn''` to authenticate as a user, `-W` to interactively enter a password (you could use `-w ''password''` but someone could run `ps` and see it), and `-b (base|one|sub|children)' which controls how deeply the tree is searched. If you want to have a poke around your LDAP server, run `ldapsearch -x -b '' -s base` which shows what features `slapd` supports.

There are various other OpenLDAP client utilities like `ldapmodify`, `ldapadd` (actually just a link to `ldapmodify`), `ldapdelete`, `ldappasswd` and `ldapwhoami` which are basic wrappers for the libldap library calls. One very useful tool that's not part of OpenLDAP is `ldapvi` (`apt-get install ldapvi`) which performs a search, opens the resulting LDIF in your favorite \$EDITOR, then sends your changes (if any) back to the server.

There are many graphical LDAP clients about, but my recommendation is [http://directory.apache.org/studio/ Apache Directory Studio]. Yes, it's written in Java and requires Eclipse, but is unreasonably good despite this. Unfortunately the latest release requires Eclipse 3.3, but the latest version in Debian and Ubuntu is 3.2, so you'll have to download the standalone version.

= Importing existing users =

First we need to create some structure for the users to go in. The account entries are located in a subtree, typically called ou=People. Groups are located in ou=Group. To create these entries, put the following a file, eg `/tmp/ou.ldif`:
{{{dn: ou=People,dc=example,dc=com
ou: People
objectClass: organizationalUnit

dn: ou=Group,dc=example,dc=com
ou: Group
objectClass: organizationalUnit}}}

Now add it to the server with this command: `ldapadd -x -D cn=admin,dc=example,dc=com -W -f /tmp/ou.ldif`.

Next, add some indexes to make searches go faster. Replace the `index objectClass eq` with the following:{{{
index cn,sn,uid,displayName pres,sub,eq
index uidNumber,gidNumber eq
index objectClass pres,eq
index memberUid eq
index uniqueMember eq}}} then as root run {{{/etc/init.d/slapd stop
slapindex
chown -R openldap:openldap /var/lib/ldap
/etc/init.d/slapd start}}} to create these indexes.

The traditional way to import users is PADL's [http://www.padl.com/OSS/MigrationTools.html MigrationTools] or [https://gna.org/projects/smbldap-tools/ smbldap-tools], which are both available in Debian/Ubuntu - `migrationtools` and `smbldap-tools` respectively. However both are somewhat icky perl and I don't really trust them (if I had to choose I'd use MigrationTools) since they often add system groups, so here's a simple python script to import existing users:
{{{#!python
import pwd,spwd,grp,ldif,sys,socket
# Get user/groups range. If you have users outside this range, just set {fir,la}st[ug]id manually
adduser = file("/etc/adduser.conf",'r')
for line in adduser:
    if line[:9]=='FIRST_UID':
        firstuid=int(line[10:])
    if line[:8]=='LAST_UID':
        lastuid=int(line[9:])
    if line[:9]=='FIRST_GID':
        firstgid=int(line[10:])
    if line[:8]=='LAST_GID':
        lastgid=int(line[10:])
adduser.close()
# If this doesn't detect it correctly, check your /etc/hosts file or just set it manually
domain = socket.getfqdn().split('.')[1:]
# Set the base DN.
base = ','.join(['dc=%s' % i for i in domain])
domain = '.'.join(domain)
pwall = pwd.getpwall()
grpall = grp.getgrall()
ldifw = ldif.LDIFWriter(sys.stdout)

for user in pwall:
    if firstuid <= user.pw_uid <= lastuid:
        print user
        shadow = spwd.getspnam(user.pw_name)
        surname = ''
        given = ''
        # This ignores any GECOS data
        name = user.pw_gecos.split(',')[0]
        names = name.split()
        if len(names) > 1:
            surname = names[-1]
            given = ' '.join(names[:-1])
        dn = 'uid=%s,ou=People,%s' % (user.pw_name,base)
        entry = {
                'cn': [name],
                'objectClass': ['inetOrgPerson', 'posixAccount', 'shadowAccount', 'top'],
                'uid': [user.pw_name],
                'displayName': [name],
                'uidNumber': [str(user.pw_uid)],
                'gidNumber': [str(user.pw_gid)],
                'givenName': [given],
                'sn': [surname],
                'mail': ['%s@%s' % (user.pw_name,domain)],
                'gecos': [name],
                'homeDirectory': [user.pw_dir],
                'loginShell': [user.pw_shell],
                'shadowLastChange': [str(shadow.sp_lstchg)],
                'shadowMin': [str(shadow.sp_min)],
                'shadowMax': [str(shadow.sp_max)],
                'shadowWarning': [str(shadow.sp_warn)],
                'shadowInactive': [str(shadow.sp_inact)],
                'shadowExpire': [str(shadow.sp_expire)],
                'shadowFlag': [str(shadow.sp_flag)],
                'userPassword': [ '{CRYPT}' + shadow.sp_pwd] }
        # clean up whitespace
        for i in entry.keys():
            entry[i] = [t.strip() for t in entry[i]]
            entry[i] = [t for t in entry[i] if t]
        # sn and cn are required by schema
        if not entry.get('sn', None):
            entry['sn'] = ["No surname"]
        if not entry.get('cn', None):
            entry['cn'] = ["No name"]
        ldifw.unparse(dn, entry)

for group in grpall:
    if firstgid <= group.gr_gid <= lastgid:
        dn = 'cn=%s,ou=Group,%s' % (group.gr_name, base)
        entry = {
                'cn': [group.gr_name],
                'objectClass': ['posixGroup', 'top'],
                'gidNumber': [str(group.gr_gid)] }
        if group.gr_mem:
            entry['memberUid'] = group.gr_mem
        ldifw.unparse(dn,entry)
}}}

Some things to note about this script:
 * Although the password scheme is {CRYPT}, this uses the `crypt(3)` library function, which on Linux has support for salted MD5. Future password changes will use {SSHA}, as that's the `slapd` password-hash default - see `slapd.conf(5)` and the [http://www.openldap.org/faq/data/cache/344.html OpenLDAP Faq-O-Matic] for more details.
 * User information added by `chfn(1)` (eg phone numbers) is silently dropped, but it's not too hard to modify this script to keep it.
 * The schema for `inetOrgPerson` requires `cn` and `sn`, so fake values are used if there's no name.

To use this script, run it and redirect the output to a file, review it and remove any system users that have crept in, then use ldapadd to import it: {{{
./pw2ldap.py > /tmp/users.ldif
ldapadd -x -D cn=admin,dc=daa,dc=com,dc=au -W -f /tmp/users.ldif}}}

= Linux =

Name resolution in Linux is performed by glibc, using the NSS (name service switch) facility which is configured using `/etc/nsswitch.conf` and loads `/lib/libnss_*` modules as required. The Linux [http://www.padl.com/OSS/nss_ldap.html nss_ldap] module is written by PADL, and comes in the `libnss-ldap` package in Debian and Ubuntu.

Most Linux distributions (Slackware being the notable exception, but I doubt many lazy sysadmins use it) use [http://www.kernel.org/pub/linux/libs/pam/ Linux-PAM] (pluggable authentication modules), as their authentication system. The [http://www.padl.com/OSS/pam_ldap.html pam_ldap] module was also written by PADL, uses a similar file (in some cases the same file) as the LDAP NSS module, and comes in the `libpam-ldap` package.

Install them both, then answer the debconf questions:
{{{apt-get install libnss-ldap}}}

 LDAP server Uniform Resource Identifier:: ldap://quoll.daa.com.au
 Distinguished name of the search base:: dc=daa,dc=com,dc=au
 LDAP version to use:: 3
 Make local root Database admin:: Yes
 Does the LDAP database require login?:: No
 LDAP account for root:: cn=admin,dc=daa,dc=com,dc=au
 LDAP root account password:: PASSWORD

This will write to `/etc/libnss-ldap.conf` and `/etc/pam_ldap.conf` under Debian, and `/etc/ldap.conf` under Ubuntu (since 7.10). I don't recommend keeping these files under debconf, so edit them and remove the first line. Note that `/etc/ldap.conf` and `/etc/ldap/ldap.conf` are different files - the first is for lib{nss,pam}-ldap, while the second is for all programs that link against libldap (which confusingly includes lib{nss,pam}-ldap, however `/etc/ldap.conf` takes precedence for them).

Now, while the NSS and PAM LDAP modules are installed, they're not yet being used. To activate them, run {{{auth-client-config -a -p ldap_example}}} under Ubuntu and it should update everything automagically. On Debian you need to update various files manually. Edit `/etc/nsswitch.conf` and replace the passwd and group lines with the following:{{{
passwd: files ldap
group: files ldap}}}

This means `/etc/passwd` and `/etc/group` will be checked for users and groups first, then LDAP will be searched.

Then edit the common-* files in `/etc/pam.d` and replace the `required pam_unix.so` with the following:

`/etc/pam.d/common-account`:{{{
account sufficient pam_unix.so
account required pam_ldap.so}}}

`/etc/pam.d/common-auth`:{{{
auth [success=1 default=ignore] pam_unix.so nullok_secure
auth required pam_ldap.so try_first_pass
auth required pam_permit.so}}}

`/etc/pam.d/common-password`:{{{
password sufficient pam_unix.so nullok obscure md5
password required pam_ldap.so use_authtok}}}

Only if you're not sharing home directories by NFS: `/etc/pam.d/common-session`:{{{
session required pam_unix.so
session required pam_mkhomedir.so skel=/etc/skel/ umask=0022}}}

PAM configuration files are very sensitive to variations in ordering and words - the examples above should work, but if not you should read the [http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/sag-configuration-file.html PAM configuration file syntax] to learn the difference between 'requisite', 'required', 'suffficient' and 'optional'.

Right now we have a basic LDAP setup working. However, it's missing two things - reliability and security.

= Replication =

Reliability is handled by replication of the database to another machine running slapd. Just about every LDAP client can handle being given more than one LDAP servers to use if the primary is down. The OpenLDAP Administrator's Guide [http://www.openldap.org/doc/admin24/replication.html#Pull%20Based chapter on replication] is pretty good and goes into the background of how it works, so you should read it, but if you don't have time, here's the short version.

Syncrepl is the new hotness for replication, ignore any OpenLDAP guide that mentions `slurpd`, particularly since it's been removed in 2.4. You can do all kinds of cool replication setups with syncrepl, including multi-master and mirror-mode, but for simplicity I'll only cover basic replication to a read-only slave that forwards updates to the master.

First the server's `slapd.conf` needs a few extra items. Add `moduleload syncprov` after `moduleload back_hdb`, then add the following lines after the existing indexes:{{{
index entryUUID,entryCSN eq

# replication
overlay syncprov
syncprov-checkpoint 100 10
syncprov-sessionlog 100}}} and run {{{/etc/init.d/slapd stop
slapindex
chown -R openldap:openldap /var/lib/ldap
/etc/init.d/slapd start}}} to generate the new indexes. Further down, add these lines before the `access to attrs=userPassword,shadowLastChange` section: {{{
# SASL auth for syncagent
authz-regexp
        uid=([^,]*),cn=digest-md5,cn=auth
        cn=$1,ou=profile,dc=ucc,dc=gu,dc=uwa,dc=edu,dc=au

# replication sync
access to *
     by dn.base="cn=syncagent,ou=profile,dc=daa,dc=com,dc=au" read
     by * break

limits dn.exact="cn=syncagent,ou=profile,dc=daa,dc=com,dc=au" size=unlimited time=unlimited}}}

Since a rootdn is required for syncrepl, uncomment the `rootdn` line (just after the `database` and `suffix` lines in the default config) and delete all the `by dn="cn=admin,dc=daa,dc=com,dc=au" write` lines in the `access to` statements, otherwise you'll get warnings on startup.

Those of you who are reading closely will note that I'm using SASL to authenticate the synchronisation agent, despite my earlier statement I wouldn't cover it. In this case since the password is only used for synchronisation, not general authentication, there's no downside to using SASL. Add the user with the following LDIF (again, use `pwgen` to generate a password) :{{{
dn: ou=profile,dc=daa,dc=com,dc=au
ou: profile
objectClass: top
objectClass: organizationalUnit

dn: cn=syncagent,ou=profile,dc=daa,dc=com,dc=au
cn: syncagent
objectClass: top
objectClass: person
sn: syncagent
userPassword: {CLEARTEXT}PASSWORD}}}

Next, install `slapd` on the slave server. It doesn't matter what you answer to the debconf questions as we'll be replacing the config immediately. Stop slapd, delete all files in /var/lib/ldap and copy across the `slapd.conf` from the primary. We need to make a few changes - remove the syncproc and syncagent lines, and add the following in their place:

{{{syncrepl rid=19
        provider=ldap://quoll.daa.com.au
        type=refreshAndPersist
        searchbase="dc=daa,dc=com,dc=au"
        bindmethod=sasl
        authcid=syncagent
        saslmech=DIGEST-MD5
        credentials=PASSWORD
        retry="10 3 300 +"

updateref ldap://quoll.daa.com.au}}}

''rid'' is a unique identifier for replication, change it on each server if you have multiple slaves. ''refreshAndPersist'' means the slave will receive changes as soon as they are made on the server. Most of the other arguments are obvious, ''retry''="10 3 300 +" means if an error occurs (eg the connection is lost), it will retry every 10 seconds the first three times, then every 300 seconds afterwards (the + means forever - see `slapd.conf(5)` for full details).

You should now be able to start `slapd` on the slave and it will sync across your entire database immediately. You can now replace any LDAP URIs (eg in NSS/PAM configs) with `ldap://master.example.com/ ldap://slave.example.com/` and they will automatically fail over if the master goes down.

The main thing you have to watch out for with a replication setup is keeping the config files in sync. Replication will function fine even if ACLs, overlays and indexes differ, which can lead to poor performance, missing features or unintended data disclosure. For a small setup it's not too hard to do it by hand. Larger sites will probably want to use the include directive (warning: it's [http://www.openldap.org/its/index.cgi/Software%20Bugs?id=5276 broken in <2.3.40 and <2.4.8]) or use syncrepl on cn=config, but that's getting into serious voodoo so I won't cover it here - see the [http://www.openldap.org/doc/admin24/replication.html#N-Way%20Multi-Master N-Way Multi-Master] section of the administrator's guide for an example.

= SSL/TLS =

First, some background. X.509 a standard for public-key infrastructure, and is part of the X.500 series of standards which LDAP is descended from. SSL/TLS are protocols that use X.509 certificates to provide a secure transport layer for higher-level protocols, like LDAP and HTTP. SSL is the predecessor to TLS, and they are largely the same, however TLS offers a few more features. One of these is StartTLS, which allows you to open an unencrypted connection, then negotiate TLS and upgrade to a secure connection. The benefit of this is you only need one port open instead of two, but the disadvantage is it's much harder to guarantee all traffic is encrypted. When talking about LDAP, TLS is often used as a term for StartTLS, vs SSL which refers to LDAPS on a dedicated port (usually 636).

There's much more to SSL than that - it's a topic deserving of an entire HOWTO by itself. Unfortunately most suffer from cargo-cult syndrome; [http://www.zytrax.com/tech/survival/ssl.html Survival Guide - SSL/TLS and X.509 Certificates] is pretty good, apart from its use of CA.pl which is still a bit manual for my liking. Fortunately OpenVPN includes a set of scripts called [http://openvpn.net/index.php/documentation/howto.html#pki easy-rsa] that lives up to its name. By default it creates certificates valid for 10 years, by which time updating the PKI should be SEP.

First, install `openvpn` and copy the easy-rsa scripts under `/etc/ssl`:

{{{apt-get install openvpn
cp -a /usr/share/doc/openvpn/examples/easy-rsa/2.0/ /etc/ssl/CA}}}

Edit `/etc/ssl/CA/vars` and set the bottom five `KEY_` variables, although these are mainly only aesthetically important. Initialise the environment and create the CA:

{{{. vars
./clean-all
./build-ca --pass}}}

You'll be asked for a password and then some details about the CA, defaulting to the values you just set in `vars`. The CA will then be created in the `keys/` subdirectory, and consists of 4 files:
 ca.crt:: The CA certificate. Used by clients as an authority that identifies servers, and hence is public and copied around lots.
 ca.key:: The CA private key. Encrypted by the password, and should only be readable by admins.
 index.txt:: A list of signed keys. Not private, but doesn't need to be copied.
 serial:: The serial number used for signing the next key. Also not private but doesn't need to be copied.

Next, create a certificate for the LDAP server:

{{{./build-key-server ldap}}}

Again the fields are mostly unimportant, with the exception of the Common Name, which is vital - it must match the hostname used to access the LDAP server. If it doesn't match, then the TLS client will reject the certificate and not establish a connection.

This will have created several files in `keys/`, of which the pertinent ones are `ldap.crt` and `ldap.key`. Similarly to the CA, `ldap.key` is secret and `ldap.crt` is not. Now, copy the required files to `/etc/ldap` and set the appropriate permissions:

{{{cp keys/{ca.crt,ldap.key,ldap.crt} /etc/ldap
chgrp openldap /etc/ldap/ldap.key
chmod 640 /etc/ldap/ldap.key}}}

To tell the OpenLDAP server about the certificates and keys, edit `/etc/ldap/slapd.conf` and add the following lines at the bottom:

{{{# SSL support
TLSCertificateFile /etc/ldap/ldap.crt
TLSCertificateKeyFile /etc/ldap/ldap.key
TLSCACertificateFile /etc/ldap/ca.crt}}}

LDAP clients just need to know about the CA, which is done by adding this line to `/etc/ldap/ldap.conf`:

{{{TLS_CACERT /etc/ldap/ca.crt}}}

Finally, edit `/etc/default/slapd` and add (or uncomment) the following line:

{{{SLAPD_SERVICES="ldap://127.0.0.1:389/ ldaps:/// ldapi:///"}}}

This tells slapd to only accept unencrypted connections from localhost, and TLS-secured connections from anywhere. ldapi uses a Unix domain socket and won't be covered here.

OK, now everything should be set, `/etc/init.d/slapd restart` and test it like so:

{{{ldapsearch -x -H ldaps://quoll.daa.com.au/}}}

If it works, you can update `/etc/ldap.conf`/`/etc/libnss-ldap.conf`/`/etc/pam_ldap.conf` with the `ldaps://` URI, and add (or uncomment) the following lines:
{{{ssl on
tls_cacertfile /etc/ldap/ca.crt}}} (technically the second line isn't needed as they inherit TLS_CACERT from `/etc/ldap.conf`)

Also needing updating will be the config on the slave server - copy across `ca.crt`, edit the uris in `slapd.conf` but you'll need to generate a second server key with the appropriate common name to match the slave's hostname.

TLSCipherSuite HIGH:MEDIUM fucknuckles

= Access Control =

http://www.openldap.org/doc/admin24/access-control.html

= Solaris =

ldapclient, certutil

= Tru64 =

ldapcd, certutil
[http://h30097.www3.hp.com/docs/base_doc/DOCUMENTATION/V51B_HTML/ARH95ETE/PPLDPXXX.HTM Tru64 LDAP Documentation]
[http://forums11.itrc.hp.com/service/forums/questionanswer.do?admit=109447626+1218017020582+28353475&threadId=1084988]

= OS X =

Directory Utility or whatever it's called. screenshots.

= Samba/Windows =

This covers using Samba 3 as an NT4 Domain Controller using the same n*x accounts and passwords already stored in LDAP. If you want to do something else, like joining an existing Windows domain or Active Directory tree, or using Samba 4 (currently in alpha) to be an Active Directory domain controller, this is not the guide for you. Samba is pretty flexible and there's more than one way to do many things, including using winbind to authenticate n*x servers from it, but for this guide it's assumed n*x servers use plain LDAP and Samba is just for Windows clients.

= FreeRADIUS =

Why bother setting up FreeRADIUS to authenticate from an LDAP server? Because if you have NTPasswords thanks to samba, you can trivially set up the PoPToP PPTP server so Windows users can VPN in using the same password.

= Adding users =

cpu-ldap

= Tuning and monitoring OpenLDAP =

http://www.openldap.org/doc/admin24/monitoringslapd.html http://www.openldap.org/doc/admin24/tuning.html

= Getting Help =

There are several places you can get help if something isn't working. You could try #ldap or #openldap on [http://freenode.net/ Freenode] where I and others lurk. There's the [http://www.openldap.org/lists/ OpenLDAP] mailing lists which I don't read, but the authors of OpenLDAP do. Don't email me privately, I won't answer your question. As for distribution support, Ubuntu is pretty abysmal, Debian's ok.

= References =

 * [http://techpubs.spinlocksolutions.com/dklar/ldap.html Debian GNU: Setting up OpenLDAP]
 * [http://www.zytrax.com/books/ldap/ LDAP for Rocket Scientists]
 * [http://www.openldap.org/doc/admin24/ OpenLDAP Software 2.4 Administrator's Guide]
 * [https://wiki.ubuntu.com/AuthClientConfig AuthClientConfig]
 * [http://developers.sun.com/identity/reference/techart/opends-namesvcs.html Setting Up OpenDS 1.0.0 as a Naming Service for the OpenSolaris OS, Part 1 of 2: Basic Steps]

[2] discuss what can be specified in schema
[3] including a binary attribute, say jpegPhoto, leading to a DN thousands of characters long
[4] itself in core.schema - a common mistake is include core.schema but not cosine.schema leading to objectClass unknown errors.
MNW2TU <a href="http://ytdexkcrduen.com/">ytdexkcrduen</a>, [url=http://guzpscdadxgx.com/]guzpscdadxgx[/url], [link=http://fcrezeeiqcyt.com/]fcrezeeiqcyt[/link], http://vyxrrzmnlkko.com/
Line 681: Line 3:
CategorySystemAdministration CategoryHomepage