From e00a171a02f017b10c00fa88d0556e8f5877acfe Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 14 May 2012 10:05:41 -0400 Subject: [PATCH] Discuss jpegPhoto, access controls, Kerberos, and StartTLS in the LDAP post. --- posts/LDAP.mdwn | 250 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 235 insertions(+), 15 deletions(-) diff --git a/posts/LDAP.mdwn b/posts/LDAP.mdwn index 3cd2fb0..4d89ee2 100644 --- a/posts/LDAP.mdwn +++ b/posts/LDAP.mdwn @@ -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/@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=,cn=,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/@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 -- 2.26.2