Setting user_attrs in Dovecot/LDAP

Introduction

I’m using Dovecot on Debian 10 and switching the userdb/passdb backend from NIS to LDAP, mainly because I want to learn about LDAP.

If users’ home directories are not in the standard place, then Dovecot needs to be told how to extract their locations out of LDAP. We do this in dovecot-ldap.conf (or dovecot-ldap.conf.ext depending on your setup) with something like:

user_attrs = homeDirectory=home

This means “a user’s home can be found in the homeDirectory attribute within the user’s entry in the regular DIT”. So we need to give dovecot access to this attribute in LDAP. Later I realised that Dovecot knows about this location by default, but there are other things Dovecot may need to know (e.g. a user’s mail quota) that it does not know the location of in the LDAP database by default, so it remained a useful excercise to do.

The dovecot wiki explains how it can be done using slapd.conf, but I wanted to do it using the ‘cn=config’/ldapmodify method.

This page exists in the hope that it helps people googling about this problem.

What I tried

I started by examining what the RTC DIT says about the regular DIT:

ziti# ldapsearch -Y EXTERNAL -H ldapi:/// -W -b 'cn=config' -LLL "(objectClass=olcDatabaseConfig)"
...
dn: olcDatabase={1}mdb,cn=config
...
olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {1}to attrs=shadowLastChange by self write by * read
olcAccess: {2}to * by * read
...
ziti#

I’m unsure if that  to * by * read applied to all users or only authenticated users so I tested by running:

ziti# ldapwhoami -x
anonymous
ziti# ldapsearch -x -b uid=alexis,ou=Users,dc=pasta,dc=net | grep homeDirectory
homeDirectory: /home/alexis
ziti#

which shows that I login (“bind”) to LDAP anonymously (using -x without -W or -w <passwd>) and that I can read an attribute not explicity treated in a special way by the above ACLs. So, in fact Dovecot already has access to all attributes (and future attributes).

As a reference point, at the start, my dovecot-ldap.conf contains:

base = ou=Users,dc=pasta,dc=net
uris = ldap://192.168.1.21/

auth_bind = yes
auth_bind_userdn = uid=%u,ou=Users,dc=pasta,dc=net

So now I added to dovecot-ldap.conf:

user_attrs = homeDirectory=home

and restarted Dovecot, started my mail client (mutt) and now dovecot complains:

Error: Couldn't drop privileges: User is missing UID (see mail_uid setting)

So, to tell Dovecot to authenticate with LDAP, I added the following to dovecot.conf (not dovecot-ldap.conf):

mail_uid = dovecot
mail_gid = mail

Now dovecot complains:

dovecot: imap(alexis): Error: Mail access for users with UID 111 not permitted (see first_valid_uid in config file, uid from mail_uid setting).

Eh? I’m not trying to access mail as UID 111! On the assumption that Dovecot had decided it couldn’t get the information it wanted anonymously any more and was now trying authenticate itself, I did the following:

  1. Create file /tmp/dovecot.ldif containing:
    dn: cn=dovecot,dc=pasta,dc=net
    cn: dovecot
    objectClass: top
    objectClass: simpleSecurityObject
    objectClass: organizationalRole
    userPassword: <password-in-cleartext>
    
  2. Run:
    ldapadd -x -D cn=admin,dc=pasta,dc=net -W -f /tmp/dovecot.ldif
    
  3. In dovecot.conf set:
    mail_uid=dovecot
    mail_gid=mail
    
  4. In dovecot-ldap.conf set:
    dn = cn=dovecot,dc=pasta,dc=net
    dnpass = <password-in-cleartext>
  5. Restart dovecot.

But that didn’t work and I undid it.

Then I wondered if it was because I hadn’t provided a UID for cn=dovecot,dc=pasta,dc=net in LDAP, so I redid those last steps, but this time using LDIF data:

dn: cn=dovecot,ou=Groups,dc=pasta,dc=net
objectClass: posixGroup
cn: dovecot
gidNumber: 5000
description: Group account

dn: uid=dovecot,ou=Users,dc=pasta,dc=net
objectClass: account
objectClass: posixAccount
cn: dovecot
uid: dovecot
uidNumber: 5000
gidNumber: 5000
homeDirectory: /home/dovecot
loginShell: /bin/bash
gecos: dovecot
description: User account

But that made no difference, but it gave me an idea!

Perhaps Dovecot “Couldn’t drop privileges” because my “User is missing UID”, and that because user_attrs was specifying only the home directory attribute mapping!

The solution

Set user_attrs to specify where all of home, uid and gid are in the users’ LDAP records as follows:

user_attrs = homeDirectory=home,uidNumber=uid,gidNumber=gid

See also