mkogg.py: Fix 'self.get_mp4_metadata(self, source)'
[blog.git] / posts / LDAP.mdwn
index f108262fe72119cb59cceecf6d3ffcff3a026866..52f8a35e393da7ff3003d047b5543f90ea70b696 100644 (file)
@@ -39,11 +39,12 @@ 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.
+Gentoo creates the default database directory for you, so you can
+ignore warnings about needing to create it yourself.
 
 Configure LDAP client access.  Again, read the docs for details on
 adapting this to your particular situation:
@@ -159,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:
@@ -199,16 +200,50 @@ databases.  Command line junkies will probably like [shelldap][]:
     ~ > cd ou=people 
     ou=people,~ > ls
 
+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
 ----
 
 If you use the [[Mutt]] email client (or just want a simple way to
 query email addresses from the command line) there are a [number of
 scripts][mutts] available.  Pick whichever sounds most appealing to
-you.  I wrote up [[mutt-ldap.py]], which lets you configuration the
-connection details via a config file (`~/.mutt-ldap.rc`) rather than
-editing the script itself.  Usage details are available in the
-docstring.
+you.  I wrote up [[mutt-ldap.py|mutt-ldap]], which has since seen
+contributions from others and been pulled out into its own repository.
 
 Apple Address Book
 ------------------
@@ -274,6 +309,254 @@ 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   objectClass     eq
+    index   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
+    …
+    ldap_int_sasl_open: host=some.dynamic.canonical.host.net
+    …
+    $ ldapwhoami -d 1 -Y GSSAPI -N
+    …
+    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 (included in
+version 2.4.32, 2012-07-31), but there is still a reverse DNS call
+happening at some point.
+
+Debian-based systems
+--------------------
+
+I wanted to mirror my home LDAP info on my public Ubuntu server.
+Here's a quick rundown of the Ubuntu setup.  Install OpenLDAP:
+
+    $ sudo apt-get install slapd ldap-utils
+
+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`
+will have completed successfully.
+
+Set permissions on the database directory (note that the databases
+should be under `/var/lib/ldap` to match Ubuntu's default apparmor
+config.  Otherwise you'll see `invalid path: Permission denied` errors
+when `slapd` tries to initialize the databaes).
+
+    $ sudo chown openldap.openldap /var/lib/ldap/
+    $ sudo chmod 750 /var/lib/ldap/
+
+Configure your clients
+
+    $ cat /etc/ldap/ldap.conf
+    BASE    dc=example,dc=com
+    URI     ldaps://example.com
+    TLS_CACERT  /etc/ldap/ssl/ldapserver.crt
+
+Start `slapd` and add it to your default runlevel:
+
+    $ sudo /etc/init.d/slapd start
+    $ sudo update-rc.d slapd defaults
+
+Finally, import your directory data.  Dump the data on your master
+server:
+
+    master$ sudo slapcat -b 'dc=example,dc=com' > database.ldif
+
+Load the data on your slave:
+
+    $ sudo /etc/init.d/slapd stop
+    $ sudo slapadd -l database.ldif
+    $ sudo /etc/init.d/slapd start
+
 References
 ----------
 
@@ -286,16 +569,29 @@ 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
 
 [[!tag tags/linux]]