Comment on shelldap's edit command in the LDAP post.
[blog.git] / posts / LDAP.mdwn
1 I'm using [LDAP][] ([RFC 4510][rfc4510]) to maintain a centralized
2 address book at home.  Here are my setup notes, mostly following
3 Gentoo's [LDAP howto][howto].
4
5 Install [OpenLDAP][] with the `ldap` USE flag enabled:
6
7     # emerge -av openldap
8
9 If you get complaints about a `cyrus-sasl` ↔ `openldap` dependency
10 cycle, you should temporarily (or permanently) disable the `ldap` USE
11 flag for `cyrus-sasl`:
12
13     # echo 'dev-libs/cyrus-sasl -ldap' > /etc/portage/package.use/ldap
14     # -ldap" emerge -av1 cyrus-sasl
15     # emerge -av openldap
16
17 Generate an administrative password:
18
19     $ slappasswd 
20     New password: 
21     Re-enter new password: 
22     {SSHA}EzP6I82DZRnW+ou6lyiXHGxSpSOw2XO4
23
24 Configure the `slapd` LDAP server.  Here is a very minimal
25 configuration, read the [OpenLDAP Admin Guide][admin] for details:
26
27     # emacs /etc/openldap/slapd.conf
28     # cat /etc/openldap/slapd.conf
29     include         /etc/openldap/schema/core.schema
30     include         /etc/openldap/schema/cosine.schema
31     include         /etc/openldap/schema/inetorgperson.schema
32     pidfile         /var/run/openldap/slapd.pid
33     argsfile        /var/run/openldap/slapd.args
34     database        hdb
35     suffix          "dc=example,dc=com"
36     checkpoint      32      30
37     rootdn          "cn=Manager,dc=example,dc=com"
38     rootpw          {SSHA}EzP6I82DZRnW+ou6lyiXHGxSpSOw2XO4
39     directory       /var/lib/openldap-data
40     index   objectClass     eq
41
42 Note that [inetorgperson][] is huge, but it's standardized.  I think
43 it's better to pick a big standard right off, than to outgrow
44 something smaller and need to migrate.
45
46 Gentoo creates the default database directory for you, so you can
47 ignore warnings about needing to create it yourself.
48
49 Configure LDAP client access.  Again, read the docs for details on
50 adapting this to your particular situation:
51
52     # emacs /etc/openldap/ldap.conf
53     $ cat /etc/openldap/ldap.conf
54     BASE    dc=example,dc=com
55     URI     ldap://ldapserver.example.com
56
57 You can edit '/etc/conf.d/slapd' if you want command line options
58 passed to `slapd` when the service starts, but the defaults looked
59 fine to me.
60
61 Start `slapd`:
62
63     # /etc/init.d/slapd start
64
65 Add it to your default runlevel:
66
67     # eselect rc add /etc/init.d/slapd default
68
69 Test the server with
70
71     $ ldapsearch -x -b '' -s base '(objectclass=*)'
72
73 Build a hierarchy in your database (this will depend on your
74 organizational structure):
75
76     $ emacs /tmp/people.ldif
77     $ cat /tmp/people.ldif
78     version: 1
79
80     dn: dc=example, dc=com
81     objectClass: dcObject
82     objectClass: organization
83     o: Example, Inc.
84     dc: example
85
86     dn: ou=people, dc=example,dc=com
87     objectClass: organizationalUnit
88     ou: people
89     description: All people in organisation
90
91     dn: cn=Manager, dc=example,dc=com
92     objectClass: organizationalRole
93     cn: Manager
94     description: Directory Manager
95     $ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f /tmp/people.ldif
96     $ rm /tmp/people.ldif
97
98 abook
99 -----
100
101 If you currently keep your addresses in [abook][], you can export them
102 to [LDIF][] with:
103
104     $ abook --convert --infile ~/.abook/addressbook --outformat ldif \
105       | abook-ldif-cleanup.py --basedn 'ou=people,dc=example,dc=com' > dump.ldif
106
107 where [[abook-ldif-cleanup.py]] does some compatibility processing
108 using the [python-ldap][] module.
109
110 Add the people to your LDAP database:
111
112     $ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f dump.ldif
113
114 To check if that worked, you can list all the entries in your
115 database:
116
117     $ ldapsearch -x -b 'dc=example,dc=com' '(objectclass=*)'
118
119 Then remove the temporary files:
120
121     $ rm -rf dump.ldif
122
123 Aliases
124 -------
125
126 Ok, we've put lots of people into the `people` OU, but what if we want
127 to assign them to another department?  We can use aliases ([RFC
128 4512][rfc4512]), the symlinks of the LDAP world.  To see how this
129 works, lets create a test OU to play with:
130
131     $ emacs /tmp/test.ldif
132     $ cat /tmp/test.ldif
133     version: 1
134     dn: ou=test, dc=example,dc=com
135     objectClass: organizationalUnit
136     ou: testing
137     $ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f /tmp/test.ldif
138     $ rm /tmp/test.ldif
139
140 Now assign one of your people to that group:
141
142     $ emacs /tmp/alias.ldif
143     $ cat /tmp/alias.ldif
144     version: 1
145     dn: cn=Jane Doe, ou=test,dc=example,dc=com
146     objectClass: alias
147     aliasedObjectName: cn=Jane Doe, ou=people,dc=example,dc=com
148     $ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f /tmp/alias.ldif
149     $ rm /tmp/alias.ldif
150
151 The `extensibleObject` class allows us to add the DN field, without it
152 you get:
153
154     $ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f /tmp/alias.ldif
155     Enter LDAP Password: 
156     adding new entry "cn=Jane Doe, ou=test,dc=example,dc=com"
157     ldap_add: Object class violation (65)
158             additional info: attribute 'cn' not allowed
159
160 You can search for all entries (including aliases) with
161
162     $ ldapsearch -x -b 'ou=test, dc=example,dc=com' '(objectclass=*)'
163     ...
164     dn: cn=Jane Doe,ou=test,dc=example,dc=com
165     objectClass: alias
166     objectClass: extensibleObject
167     aliasedObjectName:: Y249TWljaGVsIFZhbGxpw6hyZXMsb3U9cGVvcGxlLGRjPXRyZW1pbHksZGM9dXM=
168     ...
169
170 You can control dereferencing with the `-a` option:
171
172     $ ldapsearch -x -a always -b 'ou=test, dc=example,dc=com' '(objectclass=*)'
173     ...
174     dn: cn=Jane Doe,ou=people,dc=example,dc=com
175     cn: Jane Doe
176     sn: Doe
177     ...
178
179 Once you've played around, you can remove the `test` OU and its
180 descendants:
181
182     $ ldapdelete -D "cn=Manager,dc=example,dc=com" -xW -r ou=test,dc=example,dc=com
183
184 shelldap
185 --------
186
187 There are a number of tools to make it easier to manage LDAP
188 databases.  Command line junkies will probably like [shelldap][]:
189
190     $ shelldap --server ldapserver.example.com
191     ~ > ls
192     cn=Manager
193     ou=people
194     ~ > cat cn=Manager 
195     
196     dn: cn=Manager,dc=example,dc=com
197     objectClass: organizationalRole
198     cn: Manager
199     
200     ~ > cd ou=people 
201     ou=people,~ > ls
202
203 Shelldap's `edit` command spawns your `EDITOR` on a temporary file
204 populated by the entry you're editing.  You can either alter the entry
205 as you see fit, or try something fancier in [LDIF][].
206
207 Mutt
208 ----
209
210 If you use the [[Mutt]] email client (or just want a simple way to
211 query email addresses from the command line) there are a [number of
212 scripts][mutts] available.  Pick whichever sounds most appealing to
213 you.  I wrote up [[mutt-ldap.py]], which lets you configuration the
214 connection details via a config file (`~/.mutt-ldap.rc`) rather than
215 editing the script itself.  Usage details are available in the
216 docstring.
217
218 Apple Address Book
219 ------------------
220
221 You can configure Apple's [Address Book][aab] to search an LDAP
222 directory.  See [[Humanizing_OS_X]] for details.
223
224 SSL/TLS
225 -------
226
227 It took me a bit of work to get [SSL/TLS][] working with my
228 [[GnuTLS]]-linked OpenLDAP.  First, you'll probably need to generate
229 new SSL/TLS keys (`/etc/openldap/ssl/*`) with [certtool][] (see
230 [[X.509_certificates]]).  Then add the following lines to
231 `/etc/openldap/slapd.conf`:
232
233     TLSCipherSuite NORMAL
234     TLSCACertificateFile /etc/openldap/ssl/ca.crt
235     TLSCertificateFile /etc/openldap/ssl/ldap.crt
236     TLSCertificateKeyFile /etc/openldap/ssl/ldap.key
237     TLSVerifyClient never
238
239 Where `ca.crt`, `ldap.crt`, and `ldap.key` are your new CA,
240 certificate, and private key.  If you want to disable unencrypted
241 connections completely, remove the `ldap://` entry from your `slapd`
242 command line by editing (on Gentoo) `/etc/conf.d/slapd` so it has
243
244     OPTS="-h 'ldaps:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock'"
245
246 Now you should be able to restart `slapd` so it will use the new
247 configuration.
248
249 Have clients running on your server use the local socket by editing
250 `/etc/openldap/ldap.conf` to set:
251
252     URI     ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock
253
254 Test your server setup by running (on the server)
255
256     $ ldapsearch -x -b '' -s base '(objectclass=*)'
257
258 Copy your CA over to any client machines (I put it in
259 `/etc/openldap/ssl/ldapserver.crt`), and set them up with the
260 following two lines in `/etc/openldap/ldap.conf`:
261
262     URI         ldaps://ldapserver.example.com
263     TLS_CACERT  /etc/openldap/ssl/ldapserver.crt
264
265 Test your client setup by running (on the client)
266
267     $ ldapsearch -x -b '' -s base '(objectclass=*)'
268
269 You can configure `shelldap` with the following lines in
270 `~/.shelldap.rc`:
271
272     server: ldaps://ldapserver.example.com
273     tls: yes
274     tls_cacert: /etc/openldap/ssl/ldapserver.crt
275
276 You can configure `mutt-ldap.py` with the following lines in
277 `~/.mutt-ldap.rc`:
278
279     port = 636
280     ssl = yes
281
282 Debian-based systems
283 --------------------
284
285 I wanted to mirror my home LDAP info on my public Ubuntu server.
286 Here's a quick rundown of the Ubuntu setup.  Install OpenLDAP:
287
288     $ sudo apt-get install slapd ldap-utils
289
290 Don't serve in the clear:
291
292     $ cat /etc/default/slapd
293     ...
294     SLAPD_SERVICES="ldaps:/// ldapi:///"
295     ...
296
297 Avoid `Unrecognized database type (hdb)` by loading the `hdb` backend
298 module before declaring `hdb` databases:
299
300     $ sudo cat /etc/ldap/slapd.conf
301     ...
302     moduleload back_hdb
303     database hdb
304     ...
305
306 Convert the old school `slapd.conf` to the new [slapd.d][]:
307
308     $ sudo mv slapd.d{,.bak}
309     $ sudo mkdir slapd.d
310     $ sudo slaptest -f slapd.conf -F slapd.d
311     ...
312     hdb_db_open: database "dc=example,dc=com": db_open(/var/lib/slapd/id2entry.bdb) failed: No such file or directory (2).
313     ...
314     slap_startup failed (test would succeed using the -u switch)
315     ...
316     $ sudo chown -R openldap.openldap slapd.d
317
318 Don't worry about that `db_open` error, the conversion to `slapd.d`
319 will have completed successfully.
320
321 Set permissions on the database directory (note that the databases
322 should be under `/var/lib/ldap` to match Ubuntu's default apparmor
323 config.  Otherwise you'll see `invalid path: Permission denied` errors
324 when `slapd` tries to initialize the databaes).
325
326     $ sudo chown openldap.openldap /var/lib/ldap/
327     $ sudo chmod 750 /var/lib/ldap/
328
329 Configure your clients
330
331     $ cat /etc/ldap/ldap.conf
332     BASE    dc=example,dc=com
333     URI     ldaps://example.com
334     TLS_CACERT  /etc/ldap/ssl/ldapserver.crt
335
336 Start `slapd` and add it to your default runlevel:
337
338     $ sudo /etc/init.d/slapd start
339     $ sudo update-rc.d slapd defaults
340
341 Finally, import your directory data.  Dump the data on your master
342 server:
343
344     master$ sudo slapcat -b 'dc=example,dc=com' > database.ldif
345
346 Load the data on your slave:
347
348     $ sudo /etc/init.d/slapd stop
349     $ sudo slapadd -l database.ldif
350     $ sudo /etc/init.d/slapd start
351
352 References
353 ----------
354
355 There's a [good overview][schema] of schema and objectclasses by Brian
356 Jones on O'Reilly.  If you want to use inetOrgPerson but also include
357 the countryName attribute, ...
358
359 [LDAP]: http://en.wikipedia.org/wiki/LDAP
360 [rfc4510]: http://tools.ietf.org/html/rfc4510
361 [howto]: http://www.gentoo.org/doc/en/ldap-howto.xml
362 [OpenLDAP]: http://www.openldap.org/
363 [admin]: http://www.openldap.org/doc/admin/
364 [inetorgperson]: http://www.apps.ietf.org/rfc/rfc2798.html
365 [abook]: http://abook.sourceforge.net/
366 [LDIF]: http://en.wikipedia.org/wiki/LDAP_Data_Interchange_Format
367 [python-ldap]: http://www.python-ldap.org/
368 [rfc4512]: http://tools.ietf.org/html/rfc4512
369 [shelldap]: http://projects.martini.nu/shelldap/
370 [mutts]: http://wiki.mutt.org/?QueryCommand
371 [aab]: http://support.apple.com/kb/ht2486
372 [SSL/TLS]: http://en.wikipedia.org/wiki/Transport_Layer_Security
373 [certtool]:http://www.gnu.org/software/gnutls/manual/html_node/Invoking-certtool.html#Invoking-certtool
374 [slapd.d]: http://www.openldap.org/doc/admin24/slapdconf2.html
375 [schema]: http://www.oreillynet.com/pub/a/sysadmin/2006/11/09/demystifying-ldap-data.html
376
377 [[!tag tags/linux]]
378 [[!tag tags/tools]]