LDAP virtual user username being rewritten:
Hi all,
I’m working on migrating a dovecot instance that formerly had its only user (me) stored in a SQL database to a new instance that uses LDAP so I don’t have to maintain a handful of different passwords. However, I’m ending up getting sent to an incorrect mailbox because the %d part of the username I enter is getting stripped somewhere along the lines. Dovecot debug logs show this happening here:
May 21 18:36:53 auth: Debug: client in: AUTH 1 PLAIN service=imap secured session=itEO5InfPJusEgKD lip=172.18.2.131 rip=172.18.2.131 lport=143 rport=39740 resp=<hidden> May 21 18:36:53 auth: Debug: ldap(hile@coyhile.com,172.18.2.131,<itEO5InfPJusEgKD>): Performing passdb lookup May 21 18:36:53 auth: Debug: ldap(hile@coyhile.com,172.18.2.131,<itEO5InfPJusEgKD>): pass search: base=ou=People,dc=coyhile,dc=com scope=subtree filter=(&(objectClass=posixAccount)(uid=hile)) fields=uid,userPassword May 21 18:36:53 auth: Debug: ldap(hile@coyhile.com,172.18.2.131,<itEO5InfPJusEgKD>): result: uid=hile userPassword=<hidden>; uid,userPassword unused May 21 18:36:53 auth: Debug: ldap(hile@coyhile.com,172.18.2.131,<itEO5InfPJusEgKD>): username changed hile@coyhile.com -> hile May 21 18:36:53 auth: Debug: ldap(hile,172.18.2.131,<itEO5InfPJusEgKD>): Finished passdb lookup May 21 18:36:53 auth: Debug: auth(hile,172.18.2.131,<itEO5InfPJusEgKD>): Auth request finished May 21 18:36:53 auth: Debug: client passdb out: OK 1 user=hile original_user=hile@coyhile.com May 21 18:36:53 auth: Debug: master in: REQUEST 473956353 9409 1 2c1f94ded4de2c343425c90eeee8d094 session_pid=9412 request_auth_token May 21 18:36:53 auth: Debug: ldap(hile,172.18.2.131,<itEO5InfPJusEgKD>): Performing userdb lookup May 21 18:36:53 auth: Debug: ldap(hile,172.18.2.131,<itEO5InfPJusEgKD>): user search: base=ou=People,dc=coyhile,dc=com scope=subtree filter=(&(objectClass=posixAccount)(uid=hile)) fields=homeDirectory,uidNumber,gidNumber May 21 18:36:53 auth: Debug: ldap(hile,172.18.2.131,<itEO5InfPJusEgKD>): result: homeDirectory=/home/hile uidNumber=10000 gidNumber=10000; homeDirectory,uidNumber,gidNumber unused May 21 18:36:53 auth: Debug: ldap(hile,172.18.2.131,<itEO5InfPJusEgKD>): Finished userdb lookup May 21 18:36:53 auth: Debug: master userdb out: USER 473956353 hile home=/data/mail/vmail//hile uid=998 gid=998 auth_token=3a262da408d33ce2c51ecb1ddd943203fdbb17a3 auth_user=hile@coyhile.com
Note the “username changed” line above. Clearly I’ve misconfigured something, butI am unsure what. My configuration is as follows:
root@basement-imap01:/var/log# doveconf -n
2.3.7.2 (3c910f64b): /etc/dovecot/dovecot.conf
Pigeonhole version 0.5.7.2 ()
OS: Linux 5.4.0-110-generic x86_64 Ubuntu 20.04.4 LTS
Hostname: basement-imap01.coyhile.com
auth_debug = yes debug_log_path = /var/log/dovecot-debug.log doveadm_password = # hidden, use -P to show it mail_location = maildir:~/Maildir mail_privileged_group = mail namespace inbox { inbox = yes location = mailbox Drafts { special_use = \Drafts } mailbox Junk { special_use = \Junk } mailbox Sent { special_use = \Sent } mailbox "Sent Messages" { special_use = \Sent } mailbox Trash { special_use = \Trash } prefix = } passdb { args = /etc/dovecot/dovecot-ldap.conf.ext driver = ldap } protocols = " imap lmtp" service aggregator { fifo_listener replication-notify-fifo { mode = 0666 user = vmail } unix_listener replication-notify { mode = 0666 user = vmail } } service doveadm { inet_listener { port = 12345 } user = vmail } service replicator { unix_listener replicator-doveadm { mode = 0666 } } ssl_cert = </etc/dovecot/private/dovecot.pem ssl_client_ca_dir = /etc/ssl/certs ssl_dh = # hidden, use -P to show it ssl_key = # hidden, use -P to show it userdb { args = /etc/dovecot/dovecot-ldap.conf.ext driver = ldap override_fields = uid=vmail gid=vmail home=/data/mail/vmail/%d/%n } root@basement-imap01:/var/log#
with the dovecot-ldap.conf.ext as follows:
root@basement-imap01:/etc/dovecot# cat dovecot-ldap.conf.ext
This file is commonly accessed via passdb {} or userdb {} section in
conf.d/auth-ldap.conf.ext
This file is opened as root, so it should be owned by root and mode 0600.
http://wiki2.dovecot.org/AuthDatabase/LDAP
NOTE: If you're not using authentication binds, you'll need to give
dovecot-auth read access to userPassword field in the LDAP server.
With OpenLDAP this is done by modifying /etc/ldap/slapd.conf. There should
already be something like this:
access to attribute=userPassword
by dn="<dovecot's dn>" read # add this
by anonymous auth
by self write
by * none
Space separated list of LDAP hosts to use. host:port is allowed too.
#hosts = hosts = ldap.coyhile.com
LDAP URIs to use. You can use this instead of hosts list. Note that this
setting isn't supported by all LDAP libraries.
#uris =
Distinguished Name - the username used to login to the LDAP server.
Leave it commented out to bind anonymously (useful with auth_bind=yes).
#dn = dn = uid=dovecotquery,ou=people,dc=coyhile,dc=com
Password for LDAP server, if dn is specified.
#dnpass = dnpass = [REDACTED]
Use SASL binding instead of the simple binding. Note that this changes
ldap_version automatically to be 3 if it's lower.
#sasl_bind = no
SASL mechanism name to use.
#sasl_mech =
SASL realm to use.
#sasl_realm =
SASL authorization ID, ie. the dnpass is for this "master user", but the
dn is still the logged in user. Normally you want to keep this empty.
#sasl_authz_id =
Use TLS to connect to the LDAP server.
#tls = no
TLS options, currently supported only with OpenLDAP:
#tls_ca_cert_file = #tls_ca_cert_dir = #tls_cipher_suite =
TLS cert/key is used only if LDAP server requires a client certificate.
#tls_cert_file = #tls_key_file =
Valid values: never, hard, demand, allow, try
#tls_require_cert =
Use the given ldaprc path.
#ldaprc_path =
LDAP library debug level as specified by LDAP_DEBUG_* in ldap_log.h.
-1 = everything. You may need to recompile OpenLDAP with debugging enabled
to get enough output.
#debug_level = 0
Use authentication binding for verifying password's validity. This works by
logging into LDAP server using the username and password given by client.
The pass_filter is used to find the DN for the user. Note that the pass_attrs
is still used, only the password field is ignored in it. Before doing any
search, the binding is switched back to the default DN.
#auth_bind = no
If authentication binding is used, you can save one LDAP request per login
if users' DN can be specified with a common template. The template can use
the standard %variables (see user_filter). Note that you can't
use any pass_attrs if you use this setting.
If you use this setting, it's a good idea to use a different
dovecot-ldap.conf.ext for userdb (it can even be a symlink, just as long as
the filename is different in userdb's args). That way one connection is used
only for LDAP binds and another connection is used for user lookups.
Otherwise the binding is changed to the default DN before each user lookup.
For example:
auth_bind_userdn = cn=%u,ou=people,o=org
#auth_bind_userdn =
LDAP protocol version to use. Likely 2 or 3.
#ldap_version = 3
LDAP base. %variables can be used here.
For example: dc=mail, dc=example, dc=org
base = ou=People,dc=coyhile,dc=com
Dereference: never, searching, finding, always
#deref = never
Search scope: base, onelevel, subtree
#scope = subtree scope = subtree
User attributes are given in LDAP-name=dovecot-internal-name list. The
internal names are:
uid - System UID
gid - System GID
home - Home directory
mail - Mail location
There are also other special fields which can be returned, see
http://wiki2.dovecot.org/UserDatabase/ExtraFields
#user_attrs = homeDirectory=home,uidNumber=uid,gidNumber=gid
Filter for user lookup. Some variables can be used (see
http://wiki2.dovecot.org/Variables for full list):
%u - username
%n - user part in user@domain, same as %u if there's no domain
%d - domain part in user@domain, empty if user there's no domain
#user_filter = (&(objectClass=posixAccount)(uid=%u)) user_filter = (&(objectClass=posixAccount)(uid=%u))
Password checking attributes:
user: Virtual user name (user@domain), if you wish to change the
user-given username to something else
password: Password, may optionally start with {type}, eg. {crypt}
There are also other special fields which can be returned, see
http://wiki2.dovecot.org/PasswordDatabase/ExtraFields
#pass_attrs = uid=user,userPassword=password
If you wish to avoid two LDAP lookups (passdb + userdb), you can use
userdb prefetch instead of userdb ldap in dovecot.conf. In that case you'll
also have to include user_attrs in pass_attrs field prefixed with "userdb_"
string. For example:
#pass_attrs = uid=user,userPassword=password,\
homeDirectory=userdb_home,uidNumber=userdb_uid,gidNumber=userdb_gid
Filter for password lookups
#pass_filter = (&(objectClass=posixAccount)(uid=%u)) pass_filter = (&(objectClass=posixAccount)(uid=%n))
Attributes and filter to get a list of all users
#iterate_attrs = uid=user iterate_attrs = maildrop=user iterate_filter = (objectClass=posixAccount)
Default password scheme. "{scheme}" before password overrides this.
List of supported schemes is in: http://wiki2.dovecot.org/Authentication
#default_pass_scheme = CRYPT
By default all LDAP lookups are performed by the auth master process.
If blocking=yes, auth worker processes are used to perform the lookups.
Each auth worker process creates its own LDAP connection so this can
increase parallelism. With blocking=no the auth master process can
keep 8 requests pipelined for the LDAP connection, while with blocking=yes
each connection has a maximum of 1 request running. For small systems the
blocking=no is sufficient and uses less resources.
#blocking = no root@basement-imap01:/etc/dovecot#
-- Coy Hile coy.hile@coyhile.com
Replying to myself…
Empirically, changing the following two lines in the LDAP configuration seems to have worked:
user_filter = (&(objectClass=posixAccount)(uid=%n))
and
pass_filter = (&(objectClass=posixAccount)(uid=%n))
My question remains, though, why was the original username getting rewritten by default?
-c
On May 21, 2022, at 3:18 PM, Coy Hile <coy.hile@coyhile.com> wrote:
Hi all,
I’m working on migrating a dovecot instance that formerly had its only user (me) stored in a SQL database to a new instance that uses LDAP so I don’t have to maintain a handful of different passwords. However, I’m ending up getting sent to an incorrect mailbox because the %d part of the username I enter is getting stripped somewhere along the lines. Dovecot debug logs show this happening here:
May 21 18:36:53 auth: Debug: client in: AUTH 1 PLAIN service=imap secured session=itEO5InfPJusEgKD lip=172.18.2.131 rip=172.18.2.131 lport=143 rport=39740 resp=<hidden> May 21 18:36:53 auth: Debug: ldap(hile@coyhile.com,172.18.2.131,<itEO5InfPJusEgKD>): Performing passdb lookup May 21 18:36:53 auth: Debug: ldap(hile@coyhile.com,172.18.2.131,<itEO5InfPJusEgKD>): pass search: base=ou=People,dc=coyhile,dc=com scope=subtree filter=(&(objectClass=posixAccount)(uid=hile)) fields=uid,userPassword May 21 18:36:53 auth: Debug: ldap(hile@coyhile.com,172.18.2.131,<itEO5InfPJusEgKD>): result: uid=hile userPassword=<hidden>; uid,userPassword unused May 21 18:36:53 auth: Debug: ldap(hile@coyhile.com,172.18.2.131,<itEO5InfPJusEgKD>): username changed hile@coyhile.com -> hile May 21 18:36:53 auth: Debug: ldap(hile,172.18.2.131,<itEO5InfPJusEgKD>): Finished passdb lookup May 21 18:36:53 auth: Debug: auth(hile,172.18.2.131,<itEO5InfPJusEgKD>): Auth request finished May 21 18:36:53 auth: Debug: client passdb out: OK 1 user=hile original_user=hile@coyhile.com May 21 18:36:53 auth: Debug: master in: REQUEST 473956353 9409 1 2c1f94ded4de2c343425c90eeee8d094 session_pid=9412 request_auth_token May 21 18:36:53 auth: Debug: ldap(hile,172.18.2.131,<itEO5InfPJusEgKD>): Performing userdb lookup May 21 18:36:53 auth: Debug: ldap(hile,172.18.2.131,<itEO5InfPJusEgKD>): user search: base=ou=People,dc=coyhile,dc=com scope=subtree filter=(&(objectClass=posixAccount)(uid=hile)) fields=homeDirectory,uidNumber,gidNumber May 21 18:36:53 auth: Debug: ldap(hile,172.18.2.131,<itEO5InfPJusEgKD>): result: homeDirectory=/home/hile uidNumber=10000 gidNumber=10000; homeDirectory,uidNumber,gidNumber unused May 21 18:36:53 auth: Debug: ldap(hile,172.18.2.131,<itEO5InfPJusEgKD>): Finished userdb lookup May 21 18:36:53 auth: Debug: master userdb out: USER 473956353 hile home=/data/mail/vmail//hile uid=998 gid=998 auth_token=3a262da408d33ce2c51ecb1ddd943203fdbb17a3 auth_user=hile@coyhile.com
Note the “username changed” line above. Clearly I’ve misconfigured something, butI am unsure what. My configuration is as follows:
root@basement-imap01:/var/log# doveconf -n
2.3.7.2 (3c910f64b): /etc/dovecot/dovecot.conf
Pigeonhole version 0.5.7.2 ()
OS: Linux 5.4.0-110-generic x86_64 Ubuntu 20.04.4 LTS
Hostname: basement-imap01.coyhile.com
auth_debug = yes debug_log_path = /var/log/dovecot-debug.log doveadm_password = # hidden, use -P to show it mail_location = maildir:~/Maildir mail_privileged_group = mail namespace inbox { inbox = yes location = mailbox Drafts { special_use = \Drafts } mailbox Junk { special_use = \Junk } mailbox Sent { special_use = \Sent } mailbox "Sent Messages" { special_use = \Sent } mailbox Trash { special_use = \Trash } prefix = } passdb { args = /etc/dovecot/dovecot-ldap.conf.ext driver = ldap } protocols = " imap lmtp" service aggregator { fifo_listener replication-notify-fifo { mode = 0666 user = vmail } unix_listener replication-notify { mode = 0666 user = vmail } } service doveadm { inet_listener { port = 12345 } user = vmail } service replicator { unix_listener replicator-doveadm { mode = 0666 } } ssl_cert = </etc/dovecot/private/dovecot.pem ssl_client_ca_dir = /etc/ssl/certs ssl_dh = # hidden, use -P to show it ssl_key = # hidden, use -P to show it userdb { args = /etc/dovecot/dovecot-ldap.conf.ext driver = ldap override_fields = uid=vmail gid=vmail home=/data/mail/vmail/%d/%n } root@basement-imap01:/var/log#
with the dovecot-ldap.conf.ext as follows:
root@basement-imap01:/etc/dovecot# cat dovecot-ldap.conf.ext
This file is commonly accessed via passdb {} or userdb {} section in
conf.d/auth-ldap.conf.ext
This file is opened as root, so it should be owned by root and mode 0600.
http://wiki2.dovecot.org/AuthDatabase/LDAP
NOTE: If you're not using authentication binds, you'll need to give
dovecot-auth read access to userPassword field in the LDAP server.
With OpenLDAP this is done by modifying /etc/ldap/slapd.conf. There should
already be something like this:
access to attribute=userPassword
by dn="<dovecot's dn>" read # add this
by anonymous auth
by self write
by * none
Space separated list of LDAP hosts to use. host:port is allowed too.
#hosts = hosts = ldap.coyhile.com
LDAP URIs to use. You can use this instead of hosts list. Note that this
setting isn't supported by all LDAP libraries.
#uris =
Distinguished Name - the username used to login to the LDAP server.
Leave it commented out to bind anonymously (useful with auth_bind=yes).
#dn = dn = uid=dovecotquery,ou=people,dc=coyhile,dc=com
Password for LDAP server, if dn is specified.
#dnpass = dnpass = [REDACTED]
Use SASL binding instead of the simple binding. Note that this changes
ldap_version automatically to be 3 if it's lower.
#sasl_bind = no
SASL mechanism name to use.
#sasl_mech =
SASL realm to use.
#sasl_realm =
SASL authorization ID, ie. the dnpass is for this "master user", but the
dn is still the logged in user. Normally you want to keep this empty.
#sasl_authz_id =
Use TLS to connect to the LDAP server.
#tls = no
TLS options, currently supported only with OpenLDAP:
#tls_ca_cert_file = #tls_ca_cert_dir = #tls_cipher_suite =
TLS cert/key is used only if LDAP server requires a client certificate.
#tls_cert_file = #tls_key_file =
Valid values: never, hard, demand, allow, try
#tls_require_cert =
Use the given ldaprc path.
#ldaprc_path =
LDAP library debug level as specified by LDAP_DEBUG_* in ldap_log.h.
-1 = everything. You may need to recompile OpenLDAP with debugging enabled
to get enough output.
#debug_level = 0
Use authentication binding for verifying password's validity. This works by
logging into LDAP server using the username and password given by client.
The pass_filter is used to find the DN for the user. Note that the pass_attrs
is still used, only the password field is ignored in it. Before doing any
search, the binding is switched back to the default DN.
#auth_bind = no
If authentication binding is used, you can save one LDAP request per login
if users' DN can be specified with a common template. The template can use
the standard %variables (see user_filter). Note that you can't
use any pass_attrs if you use this setting.
If you use this setting, it's a good idea to use a different
dovecot-ldap.conf.ext for userdb (it can even be a symlink, just as long as
the filename is different in userdb's args). That way one connection is used
only for LDAP binds and another connection is used for user lookups.
Otherwise the binding is changed to the default DN before each user lookup.
For example:
auth_bind_userdn = cn=%u,ou=people,o=org
#auth_bind_userdn =
LDAP protocol version to use. Likely 2 or 3.
#ldap_version = 3
LDAP base. %variables can be used here.
For example: dc=mail, dc=example, dc=org
base = ou=People,dc=coyhile,dc=com
Dereference: never, searching, finding, always
#deref = never
Search scope: base, onelevel, subtree
#scope = subtree scope = subtree
User attributes are given in LDAP-name=dovecot-internal-name list. The
internal names are:
uid - System UID
gid - System GID
home - Home directory
mail - Mail location
There are also other special fields which can be returned, see
http://wiki2.dovecot.org/UserDatabase/ExtraFields
#user_attrs = homeDirectory=home,uidNumber=uid,gidNumber=gid
Filter for user lookup. Some variables can be used (see
http://wiki2.dovecot.org/Variables for full list):
%u - username
%n - user part in user@domain, same as %u if there's no domain
%d - domain part in user@domain, empty if user there's no domain
#user_filter = (&(objectClass=posixAccount)(uid=%u)) user_filter = (&(objectClass=posixAccount)(uid=%u))
Password checking attributes:
user: Virtual user name (user@domain), if you wish to change the
user-given username to something else
password: Password, may optionally start with {type}, eg. {crypt}
There are also other special fields which can be returned, see
http://wiki2.dovecot.org/PasswordDatabase/ExtraFields
#pass_attrs = uid=user,userPassword=password
If you wish to avoid two LDAP lookups (passdb + userdb), you can use
userdb prefetch instead of userdb ldap in dovecot.conf. In that case you'll
also have to include user_attrs in pass_attrs field prefixed with "userdb_"
string. For example:
#pass_attrs = uid=user,userPassword=password,\
homeDirectory=userdb_home,uidNumber=userdb_uid,gidNumber=userdb_gid
Filter for password lookups
#pass_filter = (&(objectClass=posixAccount)(uid=%u)) pass_filter = (&(objectClass=posixAccount)(uid=%n))
Attributes and filter to get a list of all users
#iterate_attrs = uid=user iterate_attrs = maildrop=user iterate_filter = (objectClass=posixAccount)
Default password scheme. "{scheme}" before password overrides this.
List of supported schemes is in: http://wiki2.dovecot.org/Authentication
#default_pass_scheme = CRYPT
By default all LDAP lookups are performed by the auth master process.
If blocking=yes, auth worker processes are used to perform the lookups.
Each auth worker process creates its own LDAP connection so this can
increase parallelism. With blocking=no the auth master process can
keep 8 requests pipelined for the LDAP connection, while with blocking=yes
each connection has a maximum of 1 request running. For small systems the
blocking=no is sufficient and uses less resources.
#blocking = no root@basement-imap01:/etc/dovecot#
-- Coy Hile coy.hile@coyhile.com
-- Coy Hile coy.hile@coyhile.com
participants (1)
-
Coy Hile