IMAP and POP3 session proxying
NOTE: This page was written quite a long time ago and may not match reality anymore. See here for a newer description of how to use the proxy feature.
Dovecot can act as IMAP and POP3 proxy by forwarding incoming sessions transparently to a administrator-specified mailhost.
Proxy can provide only plaintext authentication to clients, forwarded sessions are non-SSL connections transfering the username and password to the remote server using normal USER + PASS or LOGIN commands.
Another way to do proxying would be to let Dovecot proxy perform the authentication and login to remote server using proxy's password. This should be possible with master password feature, but isn't explained here.
Contents
Benefits of IMAP and POP3 session proxying
With IMAP and POP3 session proxying you can scale up your email infrastructure almost infinitely but still having it simple from helpdesk and users point of view. This is accomplished by having multiple mail storage servers but single mail access point to your users. With help of some load balancing appliances you can have multiple Dovecot proxy servers also, add to that multiple replicated SQL servers and your proxy setup is fully fault tolerant.
You can forward sessions to any IMAP/POP3 server, they do not need to be running Dovecot [remark: CAPABILITY list needs some attention, update documentation about that later]. By deploying Dovecot proxy you can bring SSL/TLS and IPv6 features to your existing email infrastructure even if your current IMAP/POP3 server software wouldn't support those.
Performance
Following tests were conducted with Dovecot 1.0-test60.
Non-SSL POP3 session proxying performance of Dell Poweredge 2450, Pentium3 733MHz CPU, 768M RAM, Linux 2.4.28 on Debian Sarge:
- With "login_process_per_connection = yes": 7551 connections per minute (125/sec).
- With "login_process_per_connection = no": 45152 connections per minute (752/sec).
For comparison Perdition proxy: 13500 connections per minute (225/sec).
"login_process_per_connection = yes" is considered to be more secure, this is because every user has its own proxy process it is not possible to eavesdrop other users sessions in case of some unfortunate bug in login code. Downside of this method is high cost of forking new process for every incoming session. Perdition works like Dovecot with "login_process_per_connection = yes", forking new process for each user.
768M RAM was totally overkill for this workload, 128M would have done the same.
Benchmarking was done with rabid, included in Postal.
Proxying scenarios
There are two possible proxy setups:
- Proxy only server(s) in front of actual IMAP/POP3 servers.
- IMAP/POP3 server with proxy feature.
Proxying uses three attributes from passdb; proxy with value of Y or N representing whether session should be forwarded or not, host which is a IP-address of a server where session is forwarded and optional destuser which is user's username on destination server.
Proxy only server
On proxy only setup you have one or more Dovecot proxy servers in front of your actual IMAP/POP3 servers. Proxy server is doing only session forwarding, local mail access is disabled. Benefit of this setup is having the session forwarding load away from IMAP/POP3 servers. I also tend to think that this is a bit more secure since mail storage servers do not need to have any direct services open to the Internet.
In this document I assume that Dovecot is installed under /opt/dovecot, by default it is installed under /usr/local when compiling from source. Examples in this document are for MySQL but configs do not differ much with PostgreSQL.
SQL table structure
Create SQL table like
CREATE TABLE proxy ( user varchar(255) NOT NULL, host varchar(16) default NULL, destuser varchar(255) default NULL, PRIMARY KEY (user) );
Insert data to SQL corresponding your users.
Working data could look like this:
mysql> select * from proxy; +----------+---------------+----------------------+ | user | host | destuser | +----------+---------------+----------------------+ | john | 192.168.0.1 | | +----------+---------------+----------------------+ | joe | 192.168.0.2 | joe@domain.net | +----------+---------------+----------------------+
Dovecot configs
Open your preferred editor and save config below as /opt/dovecot/etc/dovecot.conf.
## Dovecot 1.0 configuration file
base_dir = /var/run/dovecot/
protocols = pop3 pop3s imap imaps
disable_plaintext_auth = no
# SSL/TLS settings
# Comment these out if you do not wish to provide SSL secured connections.
ssl_cert_file = /opt/dovecot/etc/mail.domain.net.cert
ssl_key_file = /opt/dovecot/etc/mail.domain.net.key
# Disable SSL/TLS support.
ssl_disable = no
# Login processes
login_dir = /var/run/dovecot/login
login_process_per_connection = no
login_processes_count = 3
# Authentication processes
# If you are not moving mailboxes from host to one on daily basis you can
# use authentication cache pretty safely.
auth_cache_size = 4096
auth_cache_ttl = 7200
# Set limit for MySQL lookup processes
auth_worker_max_count = 30
auth default {
mechanisms = plain
# Userdb settings are not used with proxy but there need to be something.
userdb static {
args = static uid=5000 gid=5000 home=/dev/null
}
passdb sql {
args = /opt/dovecot/etc/sql.conf
}
user = root
count = 1
}
# eofCreate /opt/dovecot/etc/sql.conf
## SQL passdb configuration # Database driver: mysql, pgsql driver = mysql # Database options # Only MySQL driver support multiple hosts for now. connect = host=sqlhost1 host=sqlhost2 dbname=mail user=dovecot password=hush # Query password_query = SELECT NULL AS password, host, destuser, 'Y' AS nologin, 'Y' AS nodelay, 'Y' AS proxy, 'Y' AS nopassword FROM proxy WHERE user = '%u' # eof
Be sure to set 0600 permissions (as root) for sql.conf since it contains password to your SQL!
# chmod 0600 /opt/dovecot/etc/sql.conf
Campus Dovecot proxy
We will proxy user based on the realm they provide with their login.
In newer versions of Dovecot following structure is allowed:
Dovecot
Create /opt/dovecot/etc/sql.conf
## SQL passdb configuration # Database driver: mysql, pgsql driver = mysql # Database options # Only MySQL driver support multiple hosts for now. connect = host=sqlhost1 host=sqlhost2 dbname=mail user=dovecot password=hush # Query password_query = SELECT NULL AS password, NULL AS destuser, host, 'Y' AS nologin, 'Y' AS nodelay, 'Y' AS nopassword, 'Y' AS proxy FROM proxy_domain WHERE domain = '%d' # eof
SQL table structure
Create SQL table like
CREATE TABLE proxy_domain ( domain varchar(255) NOT NULL, host varchar(16) default NULL, PRIMARY KEY (domain) );
Insert data to SQL corresponding your users.
Working data could look like this:
mysql> select * from proxy_domain; +--------------+---------------+ | domain | host | +--------------+---------------+ | domain1.com | 192.168.0.1 | +--------------+---------------+ | domain2.com | 192.168.0.2 | +--------------+---------------+
Older versions of Dovecot have to use a workaround:
Dovecot
Create /opt/dovecot/etc/sql.conf
# Database driver
driver = pgsql
# Database options
connect = host=localhost dbname=dovecotdb user=dovecotdba password=dovecotpw
# Query
password_query = SELECT * FROM domain_mapping('%u')
# eof
PostgreSQL
We create a table that will do dynamic mappings between realms and mail servers upon which we will proxy our users.
CREATE SCHEMA dovecot;
CREATE TABLE dovecot.proxy_domain_mappings (
domain VARCHAR(32) NOT NULL,
mail_server_ip inet NOT NULL,
PRIMARY KEY (domain)
);CREATE TYPE dovecot.mapping_t AS (
password varchar(8),
host varchar(16),
destuser varchar(255),
nologin varchar(1),
nodelay varchar(1),
proxy varchar(1)
);CREATE FUNCTION dovecot.domain_mapping(mail_user varchar(32))
RETURNS SETOF dovecot.mapping_t
AS '
DECLARE
_mapping dovecot.mapping_t;
_destuser varchar;
_realm varchar;
_password varchar;
_nologin varchar;
_nodelay varchar;
_proxy varchar;
_delimiter text;
BEGIN
_delimiter:= ''@'';
_password := NULL;
_nologin := ''Y'';
_nodelay := ''Y'';
_proxy := ''Y'';
SELECT split_part(mail_user,_delimiter,1) INTO _destuser;
SELECT split_part(mail_user,_delimiter,2) INTO _realm;
FOR _mapping IN
SELECT _password, mail_server_ip, _destuser, _nologin, _nodelay, _proxy
FROM dovecot.proxy_domain_mappings
WHERE domain = lower(_realm)
LOOP
RETURN NEXT _mapping;
END LOOP;
END;
' LANGUAGE PLPGSQL;
Want to do some tests?
dovecotdb=> INSERT INTO dovecot.proxy_domain_mappings VALUES ( 'labo1', '192.168.0.1' );
dovecotdb=> INSERT INTO dovecot.proxy_domain_mappings VALUES ( 'labo2', '192.168.0.2' );
dovecotdb=> SELECT * FROM dovecot.domain_mapping('someuser@labo1');
password | host | destuser | nologin | nodelay | proxy
----------+---------------+----------+---------+---------+-------
| 192.168.0.1 | someuser | Y | Y | Y
(1 row)
dovecotdb=> SELECT * FROM dovecot.domain_mapping('someuser@labo2');
password | host | destuser | nologin | nodelay | proxy
----------+---------------+----------+---------+---------+-------
| 192.168.0.2 | someuser | Y | Y | Y
(1 row)
dovecotdb=> SELECT * FROM dovecot.domain_mapping('someuser@unknownlabo');
password | host | destuser | nologin | nodelay | proxy
----------+---------------+----------+---------+---------+-------
| | | | |
(0 rows)
Start Dovecot proxy
Start proxy by running the Dovecot master process as root.
# /opt/dovecot/sbin/dovecot
IMAP/POP3 server with proxy feature
In this setup you can do session forwarding on your actual IMAP/POP3 servers. When user logs in Dovecot compares local IP-address on user's current connection to user's host attribute in passdb, if those differ user is forwarded to correct mailhost, otherways user is logged to local mailbox.
This model is still a bit unfinished in current code.
SQL table structure
Dovecot configs
Start Dovecot IMAP server
Logging
To be documented...
Technical details
Proxying is done by imap- and pop3-login processes, sharing a lot of code with Dovecot SSL wrapper. Mailbox host is queried through dovecot-auth with single SQL connection per dovecot-auth process.
If you kill or restart Dovecot master process open proxy sessions aren't affected, corresponding login processes will keep running as long as they are handling open sessions.
To flush the authentication cache send HUP signal to dovecot-auth process(es).
