Discuss jpegPhoto, access controls, Kerberos, and StartTLS in the LDAP post.
authorW. Trevor King <wking@tremily.us>
Mon, 14 May 2012 14:05:41 +0000 (10:05 -0400)
committerW. Trevor King <wking@tremily.us>
Mon, 14 May 2012 14:05:41 +0000 (10:05 -0400)
posts/LDAP.mdwn

index 3cd2fb07060060b1f036953b628c4d1b5fbffdde..4d89ee2f6b873d4dc9d9a7bcf76dd741be7caf5d 100644 (file)
@@ -39,9 +39,9 @@ configuration, read the [OpenLDAP Admin Guide][admin] for details:
     directory       /var/lib/openldap-data
     index   objectClass     eq
 
-Note that [inetorgperson][] is huge, but it's standardized.  I think
-it's better to pick a big standard right off, than to outgrow
-something smaller and need to migrate.
+[inetOrgPerson][] is huge, but it's standardized.  I think it's better
+to pick a big standard right off, than to outgrow something smaller
+and need to migrate.
 
 Gentoo creates the default database directory for you, so you can
 ignore warnings about needing to create it yourself.
@@ -160,21 +160,21 @@ you get:
 You can search for all entries (including aliases) with
 
     $ ldapsearch -x -b 'ou=test, dc=example,dc=com' '(objectclass=*)'
-    ...
+    …
     dn: cn=Jane Doe,ou=test,dc=example,dc=com
     objectClass: alias
     objectClass: extensibleObject
     aliasedObjectName:: Y249TWljaGVsIFZhbGxpw6hyZXMsb3U9cGVvcGxlLGRjPXRyZW1pbHksZGM9dXM=
-    ...
+    …
 
 You can control dereferencing with the `-a` option:
 
     $ ldapsearch -x -a always -b 'ou=test, dc=example,dc=com' '(objectclass=*)'
-    ...
+    …
     dn: cn=Jane Doe,ou=people,dc=example,dc=com
     cn: Jane Doe
     sn: Doe
-    ...
+    …
 
 Once you've played around, you can remove the `test` OU and its
 descendants:
@@ -204,6 +204,38 @@ Shelldap's `edit` command spawns your `EDITOR` on a temporary file
 populated by the entry you're editing.  You can either alter the entry
 as you see fit, or try something fancier in [LDIF][].
 
+JPEG photos and binary data
+---------------------------
+
+[inetOrgPerson][] has a [jpegPhoto][] attribute, which holds a base64
+encoded JPEG.  The easiest way to set this attribute is to use the
+`:<` delimiter mentioned in `ldif(5)` and [RFC 2849][rfc2849]:
+
+    $ cat thumb.ldif
+    version: 1
+    dn: cn=Jane Doe,ou=people,dc=example,dc=com
+    changetype: modify
+    add: jpegPhoto
+    jpegPhoto:< file:///tmp/jdoe.jpeg
+    -
+    $ ldapmodify -f thumb.ldif
+
+You can extract the thumbnail from the database using:
+
+    $ ldapsearch -tT /tmp "cn=Jane Doe"
+    …
+    jpegPhoto:< file:///tmp/ldapsearch-jpegPhoto-Vvg2Ot
+    …
+
+Which dumps non-printable values (like our `jpegPhoto`) to temporary
+files.
+
+If you just want to look up someone's picture, take a look at my
+[[ldap-jpeg.py]] script.  It searches for a query string in any of
+[cn][], [uid][], or [mail][], and for matching entries with a
+`jpegPhoto` attribute, it uses your [[mailcap]]-specified viewer to
+display the photo.
+
 Mutt
 ----
 
@@ -279,6 +311,182 @@ You can configure `mutt-ldap.py` with the following lines in
     port = 636
     ssl = yes
 
+Access control and authentication
+---------------------------------
+
+There are a number of possible approaches to authentication for LDAP,
+so read the [admin manual][admin] for details.  I've got [[Kerberos]]
+setup on my home system, and I'll walk through this setup here.
+
+### Server side
+
+I expose the LDAPS port to the external world through my router, and I
+don't want anonymous users to be able to download all my contact
+information.  The solution to this is to implement [access
+control][access].  For my situation, the following
+`/etc/openldap/slapd.conf` directives seemed appropriate:
+
+    access to attrs=uid
+        by anonymous auth
+        by * read
+
+    access to *
+        by self write
+        by anonymous auth
+        by * read
+
+The first directive allows anonymous users to use the [uid][]
+attribute when authenticating, and allows authenticated users to read
+anyone's `uid` attribute.  This keeps users from being able to change
+their own `uid`.
+
+The second directive allows authenticated users to update their own
+entry and to read every entry.  Anonymous are allowed to authenticate
+themselves, but have no other privileges.
+
+Alright, so how should user's go about authenticating][security]?
+We'll want to set `slapd` up as a Kerberos service, and have clients
+authenticate using [GSSAPI][].
+
+For the LDAP service, we'll need a `ldap/<fqdn>@REALM` principal.
+Because we want that service to start automatically at boot, we need
+to keep its key in a keytab file.
+
+    # kadmin.local -p jdoe/admin
+    Authenticating as principal jdoe/admin with password.
+    Password for jdoe/admin@R.EDU: 
+    kadmin.local:  add_principal -randkey ldap/ldapserver.example.com
+    WARNING: no policy specified for ldap/ldapserver.example.com@R.EDU; defaulting to no policy
+    Principal "ldap/ldapserver.example.com@R.EDU" created.
+    kadmin.local:  ktadd -k /etc/openldap/krb5-ldap.keytab ldap/ldapserver.example.com
+    Entry for principal kdap/ldapserver.example.com...
+    …
+    kadmin.local:  quit
+    # chown ldap:ldap /etc/openldap/krb5-ldap.keytab
+
+You need use `kadmin.local` here (instead of `kadmin`) so the process
+has premission to create and edit the keytab file.
+
+You'll need to point your `slapd` server to the new keytab.  On
+[[Gentoo]], you do this by uncommenting
+
+    KRB5_KTNAME=/etc/openldap/krb5-ldap.keytab
+
+in `/etc/conf.d/slapd`.  On Red Hat, you add
+
+    export KRB5_KTNAME=/etc/openldap/ldap.keytab
+
+to `/etc/sysconfig/ldap`.
+
+You should also configure your realm and hostname in
+`/etc/openldap/slapd.conf`:
+
+    sasl-realm      R.EDU
+    sasl-host       ldapserver.example.com
+
+You'll also want to associate user's Kerberos principles to LDAP DNs.
+The template `slapd` uses is:
+
+    uid=<primary[/instance]>,cn=<realm>,cn=gssapi,cn=auth
+
+so `jdoe@R.EDU` is associated with
+
+    uid=jdoe,cn=r.edu,cn=gssapi,cn=auth
+
+and `jdoe/admin@R.EDU` is associated with
+
+    uid=jdoe/admin,cn=r.edu,cn=gssapi,cn=auth
+
+You'll probably want to [map these authentication DNs][map] to the
+appropriate directory entry, for example:
+
+    cn=Jane Doe,ou=people,dc=r,dc=edu
+
+There are a number of ways to this, but I chose
+
+    authz-regexp
+        uid=([^,]*),cn=r.edu,cn=gssapi,cn=auth
+        ldap:///ou=people,dc=r,dc=edu??one?(uid=$1)
+
+From the manual:
+
+> This will initiate an internal search of the LDAP database inside
+> the slapd server. If the search returns exactly one entry, it is
+> accepted as being the DN of the user. If there are more than one
+> entries returned, or if there are zero entries returned, the
+> authentication fails and the user's connection is left bound as the
+> authentication request DN.
+
+[Indexing][index] sounds like a good idea, so we turn it on with
+
+    index cn,sn,mail,uid eq
+    index cn,mail sub
+
+If you change your index configuration, you'll have to stop `slapd`
+and run `slapindex` to regenerate the indexes.
+
+### Client side
+
+Users will have to do the usual `kinit` to get their Ticket Granting
+Ticket (TGT), and then instruct their client software to use GSSAPI
+(`-Y GSSAPI` with the OpenLDAP client tools).  If you don't want to
+type `-Y GSSAPI`, you can add
+
+    SASL_MECH GSSAPI
+
+to your `~/.ldaprc`.  If you're on Gentoo, you'll want the `kerberos`
+and `sasl` `USE` flags set when you emerge `openldap`.
+
+#### Reverse DNS issues
+
+Because my SLAPD server runs on a dynamic IP address, I ran into
+trouble with reverse DNS.  The client would resolve the server address
+into an IP, then resolve that IP address to its canonical name, and
+asks the ticket granting server (TGS) for authorization to use
+`ldap/<canonical>@REALM`.  Because the dynamic canonical name doesn't
+match the hostname, the TGS denies the request, leading to output
+like:
+
+    $ ldapwhoami -Y GSSAPI
+    ldap_sasl_interactive_bind_s: Local error (-2)
+        additional info: SASL(-1): generic failure: GSSAPI Error: Unspecified GSS failure.  Minor code may provide more information (Server krbtgt/EDU@R.EDU not found in Kerberos database)
+
+And messages like:
+
+    … krb5kdc[15239](info): TGS_REQ (4 etypes {18 17 16 23}) …: UNKNOWN_SERVER: authtime 0,  jdoe@R.EDU for host/some.dynamic.canonical.host.net@R.EDU, Server not found in Kerberos database
+
+in the server's KDC log.
+
+I tried disabling the reverse DNS lookup with both the `-N` command
+line option to `ldapwhoami` and the `SASL_NOCANON true` option in
+`~/.ldaprc`.  I also added:
+
+   [libdefaults]
+       rdns = false
+
+to my client's `/etc/krb5.conf`.  Even with all of these, I was still
+getting reverse DNS attempts, so I gave up and just added an entry to
+`/etc/hosts` to ensure I got the right hostname when the client tried
+to resolve it.
+
+You can get more detailed messages from `ldapwhoami` by increasing the
+debuglevel (for example, with the `-d 1` option), which helps when
+you're troubleshooting these kinds of issues.  For example:
+
+    $ ldapwhoami -d 1 -Y GSSAPI -d 1
+    …
+    ldap_int_sasl_open: host=some.dynamic.canonical.host.net
+    …
+    $ ldapwhoami -d 1 -Y GSSAPI -N -d 1
+    …
+    ldap_int_sasl_open: host=ldapserver.example.com
+    …
+
+Currently, `ldapwhoami` and friends will ignore the `SASL_NOCANON`
+configuration option and only respect the `-N` command line option.
+I've submitted [an OpenLDAP bug][7271] fixing this, but there is still
+a reverse DNS call happening at some point.
+
 Debian-based systems
 --------------------
 
@@ -290,29 +498,29 @@ Here's a quick rundown of the Ubuntu setup.  Install OpenLDAP:
 Don't serve in the clear:
 
     $ cat /etc/default/slapd
-    ...
+    …
     SLAPD_SERVICES="ldaps:/// ldapi:///"
-    ...
+    …
 
 Avoid `Unrecognized database type (hdb)` by loading the `hdb` backend
 module before declaring `hdb` databases:
 
     $ sudo cat /etc/ldap/slapd.conf
-    ...
+    …
     moduleload back_hdb
     database hdb
-    ...
+    …
 
 Convert the old school `slapd.conf` to the new [slapd.d][]:
 
     $ sudo mv slapd.d{,.bak}
     $ sudo mkdir slapd.d
     $ sudo slaptest -f slapd.conf -F slapd.d
-    ...
+    …
     hdb_db_open: database "dc=example,dc=com": db_open(/var/lib/slapd/id2entry.bdb) failed: No such file or directory (2).
-    ...
+    …
     slap_startup failed (test would succeed using the -u switch)
-    ...
+    …
     $ sudo chown -R openldap.openldap slapd.d
 
 Don't worry about that `db_open` error, the conversion to `slapd.d`
@@ -361,16 +569,28 @@ the countryName attribute, ...
 [howto]: http://www.gentoo.org/doc/en/ldap-howto.xml
 [OpenLDAP]: http://www.openldap.org/
 [admin]: http://www.openldap.org/doc/admin/
-[inetorgperson]: http://www.apps.ietf.org/rfc/rfc2798.html
+[inetOrgPerson]: http://tools.ietf.org/html/rfc2798
 [abook]: http://abook.sourceforge.net/
 [LDIF]: http://en.wikipedia.org/wiki/LDAP_Data_Interchange_Format
 [python-ldap]: http://www.python-ldap.org/
 [rfc4512]: http://tools.ietf.org/html/rfc4512
 [shelldap]: http://projects.martini.nu/shelldap/
+[jpegPhoto]: http://tools.ietf.org/html/rfc2798#section-2.6
+[cn]: http://tools.ietf.org/html/rfc2798#section-9.1.2
+[uid]: http://tools.ietf.org/html/rfc2798#page-16
+[mail]: http://tools.ietf.org/html/rfc2798#section-9.1.3
+[jpegPhoto]: http://tools.ietf.org/html/rfc2798#section-2.6
+[rfc2849]: http://tools.ietf.org/html/rfc2849
 [mutts]: http://wiki.mutt.org/?QueryCommand
 [aab]: http://support.apple.com/kb/ht2486
 [SSL/TLS]: http://en.wikipedia.org/wiki/Transport_Layer_Security
 [certtool]:http://www.gnu.org/software/gnutls/manual/html_node/Invoking-certtool.html#Invoking-certtool
+[access]: http://www.openldap.org/doc/admin24/access-control.html
+[security]: http://www.openldap.org/doc/admin24/security.html
+[GSSAPI]: http://en.wikipedia.org/wiki/Generic_Security_Services_Application_Program_Interface
+[map]: http://www.openldap.org/doc/admin24/sasl.html#Mapping%20Authentication%20Identities
+[index]: http://www.openldap.org/doc/admin24/tuning.html#Indexes
+[7271]: http://www.openldap.org/its/index.cgi?findid=7271
 [slapd.d]: http://www.openldap.org/doc/admin24/slapdconf2.html
 [schema]: http://www.oreillynet.com/pub/a/sysadmin/2006/11/09/demystifying-ldap-data.html