9683
Comment: structure
|
64172
|
Deletions are marked like this. | Additions are marked like this. |
Line 1: | Line 1: |
#acl Known:read,write,delete,revert All:read | |
Line 2: | Line 3: |
LDAP for the Lazy Sysadmin |
|
Line 6: | Line 5: |
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. 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. 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... |
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... |
Line 94: | Line 34: |
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. = OpenLDAP setup = |
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 these 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 = |
Line 111: | Line 56: |
unfriendly to newbies. The admin guide for 2.4 is good, but still requires a certain level of background knowledge. So I'll take the traditional method of |
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 |
Line 115: | Line 59: |
<insert slapd.conf> = LDAP as a name service = == What is a name service? == A naming 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>. The account entries are located in a subtree, typically called ou=People. Groups are located in ou=Group. Together with the base entry, the DIT initially looks like this: dn: dc=example,dc=com dc: example objectClass: domain |
== 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 `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. Another good one is `shelldap` (not yet in Debian, but [[http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=548271|will be soon]]) which is a shell-like interface for browsing and editing an LDAP tree as if it were a filesystem. 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`: {{{ |
Line 139: | Line 385: |
u: People objectClass: organizationalUnit dn: ou=Group,dc=example,dc=com |
|
Line 146: | Line 388: |
= LDAP 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. |
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: 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}}} |
Line 169: | Line 499: |
Most Linux distributions (Slackware being the notable exception, but I doubt many lazy sysadmins use it) use Linux-PAM, Pluggable Authentication Modules, as their authentication system. The LDAP PAM module was also written by PADL, and uses a similar file (in some cases the same file) as the LDAP NSS module. |
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'. You may have noticed I haven't mentioned how to set up `nscd` - the name service caching daemon. That's because it's the devil and worse than useless for the sort of environment this guide is aimed at. All it will do is cause confusion and pain. If you are performing thousands of name service lookups per second, then `nscd` is something you should look at, but very few servers do anywhere near that amount. 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 identifier for replication, which must be unique per `slapd`. ''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 - 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. == Single-master with `cn=config` replication == What the manual does not cover is single-master, multiple-slave configuration with `cn=config` replication, instead referring you to the source code for the test suite, of all things. This is mostly useful because it allows you to maintain all the schema, ACLs, overlays and indexes in a single source, without the loss of the advantages of single-master mode. However, it does reduce the amount of flexibility you have in configuring the slave servers - for example, all your servers must use the same file names for their SSL certificates. To edit cn=config (/!\ should be covered in a new section), use `ldapvi --host ldapi:/// -Y EXTERNAL -D cn=config -Y EXTERNAL -b cn=config` (or `ldapmodify` instead of `ldapvi` or whatever; `shelldap` does not support SASL). First, configure a syncagent profile as above ( /!\ should be explained in `cn=config` terms) Edit the master config with the following LDIF (e.g. by feeding it to `ldapmodify -H ldapi:/// -Y EXTERNAL -D cn=config`: {{{ version: 1 # First, add an identifier for each server. dn: cn=config changetype: modify add: olcServerID olcServerID: 001 ldap://master.example.com olcServerID: 002 ldap://slave.example.com # you can use whatever numbers you like as long as they are less than 0xFFF; we use the last octet of the IP address of the host. # Then add the syncprov module (note: you can add but not remove modules while the slapd process is running) dn: cn=module{0},cn=config changetype: modify add: olcModuleLoad olcModuleLoad: syncprov # Enable the syncprov overlay for cn=config dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config changetype: add objectClass: olcOverlayConfig objectClass: olcSyncProvConfig olcOverlay: syncprov # Allow access by the agent profile you are going to create # and also set up sync replication; you don't actually need # this in the master config but this way it will be congruent # across servers dn: olcDatabase={0}config,cn=config changetype: modify add: olcAccess add: olcSyncrepl olcAccess: to * by dn.base="cn=syncagent,ou=profile,dc=example,dc=com" read by * +0 break olcSyncrepl: {0}rid=1 provider=ldap://master.example.com type=refreshAndPersist bindmethod=sasl authcid=syncagent saslmech=DIGEST-MD5 credentials=PASSWORD retry="10 3 300 +" searchbase="cn=config" }}} Now, on your newly-installed slave, in `olcDatabase={0}config,cn=config`, add {{{ olcSyncrepl: {0}rid=1 provider=ldap://master.example.com type=refreshAndPersist bindmethod=sasl authcid=syncagent saslmech=DIGEST-MD5 credentials=PASSWORD retry="10 3 300 +" searchbase="cn=config" }}} This sets up replication of all the config. If you make a change to the schema, for example, you will see that reflected on the slave. NOTE: this step may fail if you have any extra modules loaded in the server; you should synchronise the module list manually first. If you have any unusual schema changes in the base schema, you should also synchronise those manually. Back on the master, initialise replication of the database: In olcDatabase={1}hdb,cn=config, add {{{ olcAccess: {0}to * by dn.base="cn=syncagent,ou=profile,dc=example,dc=com" read by * +0 break olcSyncrepl: {0}rid=1 provider=ldap://master.example.com bindmethod=sasl credentials="PASSWORD" saslmech=DIGEST-MD5 authcID="syncagent" searchbase="dc=example,dc=com" type=refreshAndPersist retry="10 3 300 +" }}} The `rid` in the olcSyncrepl line MUST match the rid for the master server; otherwise the master will try to get updates from somewhere else, and you don't want that. Now that you've added this on the master, you should be able to connect to the `dc=example,dc=com` database on the slave and updates will flowing down to the slave = 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. TODO: TLSCipherSuite HIGH:MEDIUM or however it's done with gnutls. = Access Control = http://www.openldap.org/doc/admin24/access-control.html |
Line 176: | Line 765: |
Solaris' LDAP client is a bit special, and not in a good way. The standard "search for the username's DN anonymously, then bind using that DN" method doesn't work for arcane reasons, so you need a proxy agent to do the search. The supplied `idsconfig` tool is only really for Sun's (originally Netscape's) LDAP server, and while there are patches around to make it work with OpenLDAP, they're outdated and not actually needed. What need to do is download the solaris specific schemas, [[attachment:DUAConfigProfile.schema]] [[attachment:solaris.schema]] and put them in `/etc/ldap/schema`. Next, add the profiles and proxy account. Full details to be added, but for the moment you could do worse than use the appropriate ldif files at http://developers.sun.com/identity/reference/techart/opends-namesvcs.html#5 Once you have a profile set up, the rest is fairly easy. First, edit `/etc/nsswitch.ldap` and replace the hosts, ipnodes etc. lines with the following: {{{ # Sun are fucking idiots for thinking hosts should go over LDAP hosts: files dns # Note that IPv4 addresses are searched for in all of the ipnodes databases # before searching the hosts databases. ipnodes: files dns networks: files ldap protocols: files ldap rpc: files ldap ethers: files ldap netmasks: files ldap bootparams: files ldap publickey: files ldap }}} If you don't do this, Solaris will try and use LDAP for hosts, which is a fucking stupid idea given TLS requires hostnames. Next, run `ldapclient init -v -a profileName=default -a defaultServerList=ldap.daa.com.au -a domainName=daa.com.au -a proxyDN=cn=proxyagent,ou=profile,dc=daa,dc=com,dc=au` which will ask for the bind password, then configure the system to use LDAP for NSS (including copying `/etc/nsswitch.ldap` to `/etc/nsswitch.conf`). The final step is to configure PAM, so edit `/etc/pam.conf` - either apply [[attachment:pam.conf.diff]] or make the following changes manually: Old: {{{ # login service (explicit because of pam_dial_auth) # login auth requisite pam_authtok_get.so.1 login auth required pam_dhkeys.so.1 login auth required pam_unix_cred.so.1 login auth required pam_unix_auth.so.1 login auth required pam_dial_auth.so.1 }}} New: {{{ # login service (explicit because of pam_dial_auth) # login auth requisite pam_authtok_get.so.1 login auth required pam_dhkeys.so.1 login auth required pam_unix_cred.so.1 login auth binding pam_unix_auth.so.1 server_policy login auth required pam_dial_auth.so.1 login auth required pam_ldap.so.1 }}} Old: {{{ # Default definitions for Authentication management # Used when service name is not explicitly mentioned for authentication # other auth requisite pam_authtok_get.so.1 other auth required pam_dhkeys.so.1 other auth required pam_unix_cred.so.1 other auth required pam_unix_auth.so.1 }}} New: {{{ # Default definitions for Authentication management # Used when service name is not explicitly mentioned for authentication # other auth requisite pam_authtok_get.so.1 other auth required pam_dhkeys.so.1 other auth required pam_unix_cred.so.1 other auth binding pam_unix_auth.so.1 server_policy other auth required pam_ldap.so.1 }}} Old: {{{ # Default definition for Account management # Used when service name is not explicitly mentioned for account management # other account requisite pam_roles.so.1 other account required pam_unix_account.so.1 }}} New: {{{ # Default definition for Account management # Used when service name is not explicitly mentioned for account management # other account requisite pam_roles.so.1 other account binding pam_unix_account.so.1 server_policy other account required pam_ldap.so.1 }}} Add this at the bottom: {{{ # SSH publickey support: # see http://lists.ucc.gu.uwa.edu.au/pipermail/tech/2007-February/003146.html sshd-pubkey account requisite pam_roles.so.1 sshd-pubkey account required pam_unix_account.so.1 }}} certutil http://blog.maniac.nl/setting-up-ldap-with-openldap-server-solaris-10-aix-61-and-linux-clients/ http://docs.lucidinteractive.ca/index.php/Solaris_LDAP_client_with_OpenLDAP_server |
|
Line 178: | Line 866: |
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]] |
|
Line 180: | Line 872: |
Directory Utility or whatever it's called. screenshots. |
|
Line 182: | Line 876: |
[2] discuss what can be specified in schema[[BR]] [3] including a binary attribute, say jpegPhoto, leading to a DN thousands of characters long[[BR]] |
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. /!\ To do. == smbk5pwd == smbk5pwd is a nifty little overlay that updates both the sambaLMPassword, sambaNTPassword and regular userPassword attributes. This means if you change your n*x password using `passwd` the Windows password is updated, and changing your password in Windows' Ctrl-Alt-Del dialog changes the n*x password. Compiling it is a bit of a pain, if you're using Debian use the [[http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=85;filename=smbk5pwd.diff;att=1;bug=443073|patch]] from [[http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=443073|Debian bug 443073]] and rebuild slapd: {{{ cd /usr/src apt-get source slapd apt-get build-dep slapd wget "http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=85;filename=smbk5pwd.diff;att=1;bug=443073" \ -O smbk5pwd.diff cd openldap-2.4.11 dch -i "Rebuild including smbk5pwd." debuild -us -uc -b cd .. dpkg -i libldap-2.4-2_2.4.11-2_i386.deb ldap-utils_2.4.11-2_i386.deb slapd_2.4.11-2_i386.deb slapd-smbk5pwd_2.4.11-2_i386.deb }}} For Ubuntu, the easiest way is to use the packages from the [[http://ppa.launchpad.net/ian-sigma-uk/ppa/ubuntu|PPA]] mentioned in the [[https://bugs.launchpad.net/ubuntu/+source/openldap/+bug/82853/|Ubuntu bug]]. Once this is done, add `moduleload smbk5pwd` and `overlay smbk5pwd` in `/etc/ldap/slapd.conf` below the other `moduleload` and `overlay` directives, and if using the Ubuntu PPA, `smbk5pwd-enable samba` (as we don't want Heimdal support) and restart slapd. You don't need to do any of this on the slave, as all writes are directed to the master, including password changes. `pam_ldap` should already be configured to use the LDAP password change exteneded operation, check for `pam_password exop` in `/etc/pam_ldap.conf` (Debian) or `/etc/ldap.conf` (Ubuntu). For samba, set `ldap passwd sync = only` in `/etc/samba/smb.conf`. /!\ Since I haven't written the Samba part yet, you'll need to install the Samba schema on both the master and slave: {{{ apt-get install samba-doc cd /etc/ldap/schema zcat /usr/share/doc/samba-doc/examples/LDAP/samba.schema > samba.schema }}} and edit `slapd.conf` and add `include /etc/ldap/schema/samba.schema` after the other schema include lines. = FreeRADIUS = Why bother setting up FreeRADIUS to authenticate from an LDAP server? Because if you have NTPasswords thanks to samba, you can set up the PoPToP PPTP server so Windows users can VPN in using the same password, or use an 802.1x authenticator to do WPA{,2}-Enterprise wireless authentication or port-based wired authentication. RADIUS is yet another enormously complex protocol, again deserving of its own HOWTO, but the changes required to a stock FreeRADIUS install for PPTP and 802.11x are thankfully minor. Like OpenLDAP, the [[http://freeradius.org/doc/|official FreeRADIUS documentation]] warns against using HOWTOs that are not maintained by the FreeRADIUS team, so YMMV. The [[http://wiki.freeradius.org/PopTop|official documentation on PPTP]] is a bit vague, unfortunately. The [[http://tldp.org/HOWTO/8021X-HOWTO/|802.1X Port-Based Authentication HOWTO]] is old, but good. RADIUS deals with the three As: authentication, authorization and accounting. Only the first two will be dealt with here. '''Authentication''' means determining who a user really is, and is the most obvious use of LDAP - as a password store. Beware trying to use LDAP as the `Auth-Type` module itself, as this will try and authenticate by binding to the LDAP server. This only works if you have cleartext passwords. Instead, what you want to do is instruct the RADIUS server to use LDAP to retrieve hashes for the real authentication module. RADIUS defines a massive suite of authentication protocols, all of which can be layered on top of each other. For PPTP, you want to use EAP (extensible authentication protocol) with MS-CHAPv2. This means not using MS-CHAPv2 on its own - EAP provides the framework for the authentication conversation, and the actual password exchange protocol is MS-CHAPv2. On the other hand, 802.11x is even more complicated than this - the EAP-MSCHAPv2 combination is itself encapsulated in a TLS secure tunnel, forming PEAPv0/MS-CHAPv2. (Windows Vista and above clients can support PEAP/MSCHAPv2 on the PPTP link, but luckily there seems to be an acceptable degree of autonegotiation of encryption protocol.) '''Authorization''' is checking whether a given user is allowed to connect. The documentation for the RADIUS LDAP module is in the [[http://wiki.freeradius.org/Rlm_ldap|rlm_ldap(5)]] manpage. * Set up the LDAP connection first. In `radius.conf`, find the `ldap` block (part of the modules block), and set the variables as follows: {{{ server = "ldapserver.your.domain" identity = "admin_dn" password = admin_password basedn = "search_base_dn" }}} /!\ do these variables need explaining? * If you're using SSL as described above, you should also find the `tls` block for the `ldap` module, and set the appropriate certificate variables, usually just `cacertfile`. * While you're editing the `ldap` module, change the `access_attr` setting. This controls whether an LDAP object can be used for RADIUS lookups; the simplest way to enable access for all users is to set this to `uid`. There are more complex options - see the documentation if you really need that. * The final change in `radius.conf` is in the `mschap` section, which deals with general options for the MS-CHAP and MS-CHAPv2 modules. Enabling the use of MPPE attributes (the encryption parameters for PPTP connections) is the only change required here: {{{ use_mppe = on }}} * Next, you need to fix the attributes that the LDAP modules uses to get the Samba passwords. Make the following changes to the `ldap.attrmap` file: From {{{ checkItem LM-Password lmPassword checkItem NT-Password ntPassword }}} to {{{ checkItem LM-Password sambaLMPassword checkItem NT-Password sambaNTPassword }}} * Next, edit the `eap.conf` file, which is the behemoth that controls the EAP system. Luckily, you only need a simple change here: change the `default_eap_type` from `md5` to `mschapv2`. * The above changes will allow the default configuration (`sites-enabled/default`) to ''authenticate'' against LDAP. You now need to turn on ''authorization'' using the `access-allowed` settings as described above, by editing `sites-enabled/default` and uncommenting the `ldap` option in the `authorize` module. That's it for PPTP - you should now be able to point your PPTP server at the RADIUS server (doing so is beyond the scope of this document.) For 802.1x and WPA-Enterprise, a few more changes are necessary. * If you are using the Debian packages of FreeRADIUS, you have to rebuild them. PEAP requires FreeRADIUS built against OpenSSL, which contravenes Debian guidelines. A few changes to the package configuration are required, and are well-summarised at [[http://www.linuxinsight.com/building-debian-freeradius-package-with-eap-tls-ttls-peap-support.html|Building Debian FreeRadius package with EAP/TLS/TTLS/PEAP support]]. {{{ cd /usr/src apt-get source freeradius cd freeradius-$version # apply the changes in the page above debuild -us -uc -b dpkg -i {freeradius,freeradius-common,freeradius-ldap,freeradius-utils,libfreeradius2}-$version-$arch.deb }}} * Set up server-side TLS certificates for the encrypted tunnel. You can probably just use the LDAP certificates you've already generated, or create some different ones using the process above. Edit `eap.conf` and find the `tls` block, then change the following variables as appropriate: {{{ private_key_file = ${certdir}/radius.key certificate_file = ${certdir}/radius.crt CA_file = /etc/ssl/CA.crt random_file = /dev/urandom }}} * The default configuration uses a different "virtual server" for the EAP exchange in the TLS tunnel. Edit `sites-enabled/inner-tunnel` and make the same changes as you did to the `default` file - i.e. uncommenting the `ldap` option in the `authorize` module. You should now configure your wireless access point or 802.1x-enabled switch to access the RADIUS server. = Adding users = cpu-ldap = Tuning and monitoring OpenLDAP = http://www.openldap.org/doc/admin24/monitoringslapd.html http://www.openldap.org/doc/admin24/tuning.html = Debugging = So, what do you do when things go wrong? For NSS, one of the easiest ways is `strace id username`. Understanding `strace` output can be difficult, but what you're looking for is connect() lines featuring the IP address of the LDAP server. If you don't see them, LDAP isn't being queried at all, so look for a local misconfiguration. For other LDAP clients, turn up their logging if possible or strace a running process then perform the LDAP operation. Again, you're looking for connect() calls to your LDAP server. If the program is successfully connecting to the LDAP server, next is to look at the server logs. They're in `/var/log/debug` by default, or wherever you put them if you edited `/etc/syslog.conf`. You'll need `stats` in the `loglevel` line of `/etc/ldap/slapd.conf`, which will show the LDAP binds and searches. Check to see that the searches are what you expect, and if so, perform them yourself from the command line with `ldapsearch(1)`. Another approach is to use Wireshark or another packet sniffer to examine the operations over the wire, although this won't work for SSL encrypted connections. Finally, if you are running `nscd`, stop it while debugging otherwise you'll be confused by stale results. = 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 eg [[https://lists.ubuntu.com/archives/ubuntu-server/2009-August/003179.html|making big changes in 9.10]] but [[https://bugs.launchpad.net/ubuntu/+source/openldap/+bug/442498|not updating the documentation]], 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://docs.sun.com/app/docs/doc/816-4556/ldapsetup-1?l=en&a=view|Solaris 10 LDAP Naming Services Setup and Administration]] * [[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 |
Line 185: | Line 1020: |
---- CategorySystemAdministration |
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. 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 lack of support for an assembly mutex, although this might be fixed now. On the client side, configuration for Linux, Solaris, OS X, Tru64 and Windows (using Samba) will be discussed.
Contents
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 these 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 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 hdb) 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 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 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 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 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 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. Another good one is shelldap (not yet in Debian, but will be soon) which is a shell-like interface for browsing and editing an LDAP tree as if it were a filesystem.
There are many graphical LDAP clients about, but my recommendation is 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 MigrationTools or 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:
1 import pwd,spwd,grp,ldif,sys,socket
2 # Get user/groups range. If you have users outside this range, just set {fir,la}st[ug]id manually
3 adduser = file("/etc/adduser.conf",'r')
4 for line in adduser:
5 if line[:9]=='FIRST_UID':
6 firstuid=int(line[10:])
7 if line[:8]=='LAST_UID':
8 lastuid=int(line[9:])
9 if line[:9]=='FIRST_GID':
10 firstgid=int(line[10:])
11 if line[:8]=='LAST_GID':
12 lastgid=int(line[10:])
13 adduser.close()
14 # If this doesn't detect it correctly, check your /etc/hosts file or just set it manually
15 domain = socket.getfqdn().split('.')[1:]
16 # Set the base DN.
17 base = ','.join(['dc=%s' % i for i in domain])
18 domain = '.'.join(domain)
19 pwall = pwd.getpwall()
20 grpall = grp.getgrall()
21 ldifw = ldif.LDIFWriter(sys.stdout)
22
23 for user in pwall:
24 if firstuid <= user.pw_uid <= lastuid:
25 shadow = spwd.getspnam(user.pw_name)
26 surname = ''
27 given = ''
28 # This ignores any GECOS data
29 name = user.pw_gecos.split(',')[0]
30 names = name.split()
31 if len(names) > 1:
32 surname = names[-1]
33 given = ' '.join(names[:-1])
34 dn = 'uid=%s,ou=People,%s' % (user.pw_name,base)
35 entry = {
36 'cn': [name],
37 'objectClass': ['inetOrgPerson', 'posixAccount', 'shadowAccount', 'top'],
38 'uid': [user.pw_name],
39 'displayName': [name],
40 'uidNumber': [str(user.pw_uid)],
41 'gidNumber': [str(user.pw_gid)],
42 'givenName': [given],
43 'sn': [surname],
44 'mail': ['%s@%s' % (user.pw_name,domain)],
45 'gecos': [name],
46 'homeDirectory': [user.pw_dir],
47 'loginShell': [user.pw_shell],
48 'shadowLastChange': [str(shadow.sp_lstchg)],
49 'shadowMin': [str(shadow.sp_min)],
50 'shadowMax': [str(shadow.sp_max)],
51 'shadowWarning': [str(shadow.sp_warn)],
52 'shadowInactive': [str(shadow.sp_inact)],
53 'shadowExpire': [str(shadow.sp_expire)],
54 'shadowFlag': [str(shadow.sp_flag)],
55 'userPassword': [ '{CRYPT}' + shadow.sp_pwd] }
56 # clean up whitespace
57 for i in entry.keys():
58 entry[i] = [t.strip() for t in entry[i]]
59 entry[i] = [t for t in entry[i] if t]
60 # sn and cn are required by schema
61 if not entry.get('sn', None):
62 entry['sn'] = ["No surname"]
63 if not entry.get('cn', None):
64 entry['cn'] = ["No name"]
65 ldifw.unparse(dn, entry)
66
67 for group in grpall:
68 if firstgid <= group.gr_gid <= lastgid:
69 dn = 'cn=%s,ou=Group,%s' % (group.gr_name, base)
70 entry = {
71 'cn': [group.gr_name],
72 'objectClass': ['posixGroup', 'top'],
73 'gidNumber': [str(group.gr_gid)] }
74 if group.gr_mem:
75 entry['memberUid'] = group.gr_mem
76 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 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 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 Linux-PAM (pluggable authentication modules), as their authentication system. The 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 PAM configuration file syntax to learn the difference between 'requisite', 'required', 'suffficient' and 'optional'.
You may have noticed I haven't mentioned how to set up nscd - the name service caching daemon. That's because it's the devil and worse than useless for the sort of environment this guide is aimed at. All it will do is cause confusion and pain. If you are performing thousands of name service lookups per second, then nscd is something you should look at, but very few servers do anywhere near that amount.
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 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 identifier for replication, which must be unique per slapd. 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 broken in <2.3.40 and <2.4.8) or use syncrepl on cn=config, but that's getting into serious voodoo - see the N-Way Multi-Master section of the administrator's guide for an example.
Single-master with `cn=config` replication
What the manual does not cover is single-master, multiple-slave configuration with cn=config replication, instead referring you to the source code for the test suite, of all things. This is mostly useful because it allows you to maintain all the schema, ACLs, overlays and indexes in a single source, without the loss of the advantages of single-master mode. However, it does reduce the amount of flexibility you have in configuring the slave servers - for example, all your servers must use the same file names for their SSL certificates.
To edit cn=config (/!\ should be covered in a new section), use ldapvi --host ldapi:/// -Y EXTERNAL -D cn=config -Y EXTERNAL -b cn=config (or ldapmodify instead of ldapvi or whatever; shelldap does not support SASL).
First, configure a syncagent profile as above ( should be explained in cn=config terms) Edit the master config with the following LDIF (e.g. by feeding it to ldapmodify -H ldapi:/// -Y EXTERNAL -D cn=config:
version: 1 # First, add an identifier for each server. dn: cn=config changetype: modify add: olcServerID olcServerID: 001 ldap://master.example.com olcServerID: 002 ldap://slave.example.com # you can use whatever numbers you like as long as they are less than 0xFFF; we use the last octet of the IP address of the host. # Then add the syncprov module (note: you can add but not remove modules while the slapd process is running) dn: cn=module{0},cn=config changetype: modify add: olcModuleLoad olcModuleLoad: syncprov # Enable the syncprov overlay for cn=config dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config changetype: add objectClass: olcOverlayConfig objectClass: olcSyncProvConfig olcOverlay: syncprov # Allow access by the agent profile you are going to create # and also set up sync replication; you don't actually need # this in the master config but this way it will be congruent # across servers dn: olcDatabase={0}config,cn=config changetype: modify add: olcAccess add: olcSyncrepl olcAccess: to * by dn.base="cn=syncagent,ou=profile,dc=example,dc=com" read by * +0 break olcSyncrepl: {0}rid=1 provider=ldap://master.example.com type=refreshAndPersist bindmethod=sasl authcid=syncagent saslmech=DIGEST-MD5 credentials=PASSWORD retry="10 3 300 +" searchbase="cn=config"
Now, on your newly-installed slave, in olcDatabase={0}config,cn=config, add
olcSyncrepl: {0}rid=1 provider=ldap://master.example.com type=refreshAndPersist bindmethod=sasl authcid=syncagent saslmech=DIGEST-MD5 credentials=PASSWORD retry="10 3 300 +" searchbase="cn=config"
This sets up replication of all the config. If you make a change to the schema, for example, you will see that reflected on the slave.
NOTE: this step may fail if you have any extra modules loaded in the server; you should synchronise the module list manually first. If you have any unusual schema changes in the base schema, you should also synchronise those manually.
Back on the master, initialise replication of the database:
In olcDatabase={1}hdb,cn=config, add
olcAccess: {0}to * by dn.base="cn=syncagent,ou=profile,dc=example,dc=com" read by * +0 break olcSyncrepl: {0}rid=1 provider=ldap://master.example.com bindmethod=sasl credentials="PASSWORD" saslmech=DIGEST-MD5 authcID="syncagent" searchbase="dc=example,dc=com" type=refreshAndPersist retry="10 3 300 +"
The rid in the olcSyncrepl line MUST match the rid for the master server; otherwise the master will try to get updates from somewhere else, and you don't want that.
Now that you've added this on the master, you should be able to connect to the dc=example,dc=com database on the slave and updates will flowing down to the slave
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; 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 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.
TODO: TLSCipherSuite HIGH:MEDIUM or however it's done with gnutls.
Access Control
http://www.openldap.org/doc/admin24/access-control.html
Solaris
Solaris' LDAP client is a bit special, and not in a good way. The standard "search for the username's DN anonymously, then bind using that DN" method doesn't work for arcane reasons, so you need a proxy agent to do the search.
The supplied idsconfig tool is only really for Sun's (originally Netscape's) LDAP server, and while there are patches around to make it work with OpenLDAP, they're outdated and not actually needed. What need to do is download the solaris specific schemas, DUAConfigProfile.schema solaris.schema and put them in /etc/ldap/schema.
Next, add the profiles and proxy account. Full details to be added, but for the moment you could do worse than use the appropriate ldif files at http://developers.sun.com/identity/reference/techart/opends-namesvcs.html#5
Once you have a profile set up, the rest is fairly easy. First, edit /etc/nsswitch.ldap and replace the hosts, ipnodes etc. lines with the following:
# Sun are fucking idiots for thinking hosts should go over LDAP hosts: files dns # Note that IPv4 addresses are searched for in all of the ipnodes databases # before searching the hosts databases. ipnodes: files dns networks: files ldap protocols: files ldap rpc: files ldap ethers: files ldap netmasks: files ldap bootparams: files ldap publickey: files ldap
If you don't do this, Solaris will try and use LDAP for hosts, which is a fucking stupid idea given TLS requires hostnames.
Next, run ldapclient init -v -a profileName=default -a defaultServerList=ldap.daa.com.au -a domainName=daa.com.au -a proxyDN=cn=proxyagent,ou=profile,dc=daa,dc=com,dc=au which will ask for the bind password, then configure the system to use LDAP for NSS (including copying /etc/nsswitch.ldap to /etc/nsswitch.conf). The final step is to configure PAM, so edit /etc/pam.conf - either apply pam.conf.diff or make the following changes manually:
Old:
# login service (explicit because of pam_dial_auth) # login auth requisite pam_authtok_get.so.1 login auth required pam_dhkeys.so.1 login auth required pam_unix_cred.so.1 login auth required pam_unix_auth.so.1 login auth required pam_dial_auth.so.1
New:
# login service (explicit because of pam_dial_auth) # login auth requisite pam_authtok_get.so.1 login auth required pam_dhkeys.so.1 login auth required pam_unix_cred.so.1 login auth binding pam_unix_auth.so.1 server_policy login auth required pam_dial_auth.so.1 login auth required pam_ldap.so.1
Old:
# Default definitions for Authentication management # Used when service name is not explicitly mentioned for authentication # other auth requisite pam_authtok_get.so.1 other auth required pam_dhkeys.so.1 other auth required pam_unix_cred.so.1 other auth required pam_unix_auth.so.1
New:
# Default definitions for Authentication management # Used when service name is not explicitly mentioned for authentication # other auth requisite pam_authtok_get.so.1 other auth required pam_dhkeys.so.1 other auth required pam_unix_cred.so.1 other auth binding pam_unix_auth.so.1 server_policy other auth required pam_ldap.so.1
Old:
# Default definition for Account management # Used when service name is not explicitly mentioned for account management # other account requisite pam_roles.so.1 other account required pam_unix_account.so.1
New:
# Default definition for Account management # Used when service name is not explicitly mentioned for account management # other account requisite pam_roles.so.1 other account binding pam_unix_account.so.1 server_policy other account required pam_ldap.so.1
Add this at the bottom:
# SSH publickey support: # see http://lists.ucc.gu.uwa.edu.au/pipermail/tech/2007-February/003146.html sshd-pubkey account requisite pam_roles.so.1 sshd-pubkey account required pam_unix_account.so.1
certutil
http://blog.maniac.nl/setting-up-ldap-with-openldap-server-solaris-10-aix-61-and-linux-clients/ http://docs.lucidinteractive.ca/index.php/Solaris_LDAP_client_with_OpenLDAP_server
Tru64
ldapcd, certutil 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.
To do.
smbk5pwd
smbk5pwd is a nifty little overlay that updates both the sambaLMPassword, sambaNTPassword and regular userPassword attributes. This means if you change your n*x password using passwd the Windows password is updated, and changing your password in Windows' Ctrl-Alt-Del dialog changes the n*x password.
Compiling it is a bit of a pain, if you're using Debian use the patch from Debian bug 443073 and rebuild slapd:
cd /usr/src apt-get source slapd apt-get build-dep slapd wget "http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=85;filename=smbk5pwd.diff;att=1;bug=443073" \ -O smbk5pwd.diff cd openldap-2.4.11 dch -i "Rebuild including smbk5pwd." debuild -us -uc -b cd .. dpkg -i libldap-2.4-2_2.4.11-2_i386.deb ldap-utils_2.4.11-2_i386.deb slapd_2.4.11-2_i386.deb slapd-smbk5pwd_2.4.11-2_i386.deb
For Ubuntu, the easiest way is to use the packages from the PPA mentioned in the Ubuntu bug.
Once this is done, add moduleload smbk5pwd and overlay smbk5pwd in /etc/ldap/slapd.conf below the other moduleload and overlay directives, and if using the Ubuntu PPA, smbk5pwd-enable samba (as we don't want Heimdal support) and restart slapd. You don't need to do any of this on the slave, as all writes are directed to the master, including password changes. pam_ldap should already be configured to use the LDAP password change exteneded operation, check for pam_password exop in /etc/pam_ldap.conf (Debian) or /etc/ldap.conf (Ubuntu). For samba, set ldap passwd sync = only in /etc/samba/smb.conf.
Since I haven't written the Samba part yet, you'll need to install the Samba schema on both the master and slave:
apt-get install samba-doc cd /etc/ldap/schema zcat /usr/share/doc/samba-doc/examples/LDAP/samba.schema > samba.schema
and edit slapd.conf and add include /etc/ldap/schema/samba.schema after the other schema include lines.
FreeRADIUS
Why bother setting up FreeRADIUS to authenticate from an LDAP server? Because if you have NTPasswords thanks to samba, you can set up the PoPToP PPTP server so Windows users can VPN in using the same password, or use an 802.1x authenticator to do WPA{,2}-Enterprise wireless authentication or port-based wired authentication.
RADIUS is yet another enormously complex protocol, again deserving of its own HOWTO, but the changes required to a stock FreeRADIUS install for PPTP and 802.11x are thankfully minor. Like OpenLDAP, the official FreeRADIUS documentation warns against using HOWTOs that are not maintained by the FreeRADIUS team, so YMMV. The official documentation on PPTP is a bit vague, unfortunately. The 802.1X Port-Based Authentication HOWTO is old, but good.
RADIUS deals with the three As: authentication, authorization and accounting. Only the first two will be dealt with here.
Authentication means determining who a user really is, and is the most obvious use of LDAP - as a password store. Beware trying to use LDAP as the Auth-Type module itself, as this will try and authenticate by binding to the LDAP server. This only works if you have cleartext passwords. Instead, what you want to do is instruct the RADIUS server to use LDAP to retrieve hashes for the real authentication module.
RADIUS defines a massive suite of authentication protocols, all of which can be layered on top of each other. For PPTP, you want to use EAP (extensible authentication protocol) with MS-CHAPv2. This means not using MS-CHAPv2 on its own - EAP provides the framework for the authentication conversation, and the actual password exchange protocol is MS-CHAPv2. On the other hand, 802.11x is even more complicated than this - the EAP-MSCHAPv2 combination is itself encapsulated in a TLS secure tunnel, forming PEAPv0/MS-CHAPv2. (Windows Vista and above clients can support PEAP/MSCHAPv2 on the PPTP link, but luckily there seems to be an acceptable degree of autonegotiation of encryption protocol.)
Authorization is checking whether a given user is allowed to connect.
The documentation for the RADIUS LDAP module is in the rlm_ldap(5) manpage.
Set up the LDAP connection first. In radius.conf, find the ldap block (part of the modules block), and set the variables as follows:
server = "ldapserver.your.domain" identity = "admin_dn" password = admin_password basedn = "search_base_dn"
do these variables need explaining?
If you're using SSL as described above, you should also find the tls block for the ldap module, and set the appropriate certificate variables, usually just cacertfile.
While you're editing the ldap module, change the access_attr setting. This controls whether an LDAP object can be used for RADIUS lookups; the simplest way to enable access for all users is to set this to uid. There are more complex options - see the documentation if you really need that.
The final change in radius.conf is in the mschap section, which deals with general options for the MS-CHAP and MS-CHAPv2 modules. Enabling the use of MPPE attributes (the encryption parameters for PPTP connections) is the only change required here:
use_mppe = on
Next, you need to fix the attributes that the LDAP modules uses to get the Samba passwords. Make the following changes to the ldap.attrmap file:
From
checkItem LM-Password lmPassword checkItem NT-Password ntPassword
to
checkItem LM-Password sambaLMPassword checkItem NT-Password sambaNTPassword
Next, edit the eap.conf file, which is the behemoth that controls the EAP system. Luckily, you only need a simple change here: change the default_eap_type from md5 to mschapv2.
The above changes will allow the default configuration (sites-enabled/default) to authenticate against LDAP. You now need to turn on authorization using the access-allowed settings as described above, by editing sites-enabled/default and uncommenting the ldap option in the authorize module.
That's it for PPTP - you should now be able to point your PPTP server at the RADIUS server (doing so is beyond the scope of this document.)
For 802.1x and WPA-Enterprise, a few more changes are necessary.
If you are using the Debian packages of FreeRADIUS, you have to rebuild them. PEAP requires FreeRADIUS built against OpenSSL, which contravenes Debian guidelines. A few changes to the package configuration are required, and are well-summarised at Building Debian FreeRadius package with EAP/TLS/TTLS/PEAP support.
cd /usr/src apt-get source freeradius cd freeradius-$version # apply the changes in the page above debuild -us -uc -b dpkg -i {freeradius,freeradius-common,freeradius-ldap,freeradius-utils,libfreeradius2}-$version-$arch.deb
Set up server-side TLS certificates for the encrypted tunnel. You can probably just use the LDAP certificates you've already generated, or create some different ones using the process above. Edit eap.conf and find the tls block, then change the following variables as appropriate:
private_key_file = ${certdir}/radius.key certificate_file = ${certdir}/radius.crt CA_file = /etc/ssl/CA.crt random_file = /dev/urandom
The default configuration uses a different "virtual server" for the EAP exchange in the TLS tunnel. Edit sites-enabled/inner-tunnel and make the same changes as you did to the default file - i.e. uncommenting the ldap option in the authorize module.
You should now configure your wireless access point or 802.1x-enabled switch to access the RADIUS server.
Adding users
cpu-ldap
Tuning and monitoring OpenLDAP
http://www.openldap.org/doc/admin24/monitoringslapd.html http://www.openldap.org/doc/admin24/tuning.html
Debugging
So, what do you do when things go wrong? For NSS, one of the easiest ways is strace id username. Understanding strace output can be difficult, but what you're looking for is connect() lines featuring the IP address of the LDAP server. If you don't see them, LDAP isn't being queried at all, so look for a local misconfiguration. For other LDAP clients, turn up their logging if possible or strace a running process then perform the LDAP operation. Again, you're looking for connect() calls to your LDAP server.
If the program is successfully connecting to the LDAP server, next is to look at the server logs. They're in /var/log/debug by default, or wherever you put them if you edited /etc/syslog.conf. You'll need stats in the loglevel line of /etc/ldap/slapd.conf, which will show the LDAP binds and searches. Check to see that the searches are what you expect, and if so, perform them yourself from the command line with ldapsearch(1).
Another approach is to use Wireshark or another packet sniffer to examine the operations over the wire, although this won't work for SSL encrypted connections. Finally, if you are running nscd, stop it while debugging otherwise you'll be confused by stale results.
Getting Help
There are several places you can get help if something isn't working. You could try #ldap or #openldap on Freenode where I and others lurk. There's the 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 eg making big changes in 9.10 but not updating the documentation, Debian's ok.
References
[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.