18. May, 2003
18. May, 2003
in by Michael Neumann
This article describes the steps neccessary to setup Postfix acting as SMTP client, using SASL authentification and SSL/TLS encryption.

Nowadays, most modern mail clients like Evolution or Mozilla-Mail include a full-featured SMTP client to send email to a central mail-relay (usually your providers’ one). And they usually support SASL authentification and encryption over SSL/TLS, as your provider will (hopefully) not accept mail for relay by everyone. But other email clients still exist - the famous mutt for example - that do not come with a SMTP client at all.

Installation

Build Postfix with Cyrus-SASLv2 and SSL/TLS support.

Configuration

Edit /usr/local/etc/postfix/main.cf where you add the following lines:

relayhost = mailhost
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_password_maps=static:username:password
smtp_use_tls = yes

sender_canonical_maps=hash:/usr/local/etc/postfix/sender_canonical

Replace mailhost with your provider’s mail server, username and password with your own mail account settings.

Further create the sender_canonical file with following content:

@your.localhost.domain your-real-address

On my system that looks this way:

@michael.neumann.all mneumann@ntecs.de

This will rewrite the envelope address from mneumann@michael.neumann.all (that’s my local address) to mneumann@ntecs.de (that’s my remote address). You still need to build the hash database:

postmap /usr/local/etc/postfix/sender_canonical

Finally, make sure the main.cf file is not world readable (chmod 640), as it contains your password!

Notes

If you are your own provider (I am), and you’re using Postfix as your mail-relay, make sure that the permit_sasl_authentificated directive comes before any of the reject_xxx directives. This makes live easier with mail programs that produce mails with missing headers.

11. May, 2003
11. May, 2003
in by Michael Neumann
Written by Michael Neumann (mneumann@ntecs.de).

Acknowledgements: This howto relies heavily on Kirby Menzel and Lucas Peet’s great Postfix+Courier-IMAP+MySQL for multiple domains HOWTO (kirb.insanegenius.net/postfix.html). In the underlying howto, I list the steps I’ve used for my own setup. It has an additional section about SMTP authentification using Cyrus SASL (and Mysql) and some more information not found in the above howto.

Required Software

  • (FreeBSD 4.8)
  • Postfix
  • Courier-IMAP
  • MySQL
  • OpenSSL (SSL/TLS)
  • Cyrus SASL v2

Installing Mysql Database

cd /usr/ports/databases/mysql40-server
make install BUILD_OPTIMIZED=yes
make clean

A group mysql and user mysql has been added automatically, as well as a startup script /usr/local/etc/rc.d/mysql-server.sh.

After starting the mysqld server, modify the password with:

/usr/local/bin/mysqladmin -u root password 'new-password'

When calling mysqladmin later, pass the -p option, which will ask you for the current password, otherwise the access will be denied.

TIP: use security/apg, the automated password generator, to create a password.

Installing postfix

cd /usr/ports/mail/postfix
make
  (choose "Cyrus SASLv2", "SSL and TLS" and "MySQL map lookups" as options,
   then in SASLv2 menu, choose "MySQL password Authentification")
make install
make clean

Note that this will additionally install the Mysql-323 client libraries (if you’ve installed MySQL 4.x).

Modify /etc/mail/mailer.conf to use Postfix, or let the install program do that for you (make install will ask you).

For automatic startup when system starts, disable sendmail in /etc/rc.conf:

sendmail_enable="NONE"

Then make the following symbolic link:

cd /usr/local/etc/rc.d
ln -s /usr/local/sbin/postfix postfix.sh

Important: The MySQL server should be started before postfix (rename mysql-server.sh into 100.mysql-server.sh or something alike; number go before characters).

Then create /etc/periodic.conf with following content:

daily_clean_hoststat_enable="NO"
daily_status_mail_rejects_enable="NO"
daily_status_include_submit_mailq="NO"
daily_submit_queuerun="NO"

This will disable some Sendmail-specific daily maintenance routines.

Check /etc/mail/aliases, then run newaliases, otherwise Postfix will not be able to deliver mail locally (unless you setup virtual domains etc.) Local mail will be usually delivered to /var/mail.

You can disable sending email locally (using sendmail) by setting the following option in main.cf.

mydestination =

But make sure you know what you are doing!

Setting up the Mysql tables

mysql -p -u root

create database maildb;

CREATE TABLE transport (
  domain varchar(128) NOT NULL,
  transport varchar(128) NOT NULL,
  UNIQUE KEY domain (domain)
) TYPE=MyISAM;

CREATE TABLE users (
    id varchar(128) NOT NULL,
    address varchar(128) NOT NULL,
    clear varchar(128) NOT NULL,
    crypt varchar(128) NOT NULL,
    name varchar(128) NOT NULL default '',
    uid smallint(5) unsigned NOT NULL default 5000,
    gid smallint(5) unsigned NOT NULL default 5000,
    home varchar(128) NOT NULL,
    domain varchar(128) NOT NULL,
    maildir varchar(255) NOT NULL,
    quota integer unsigned NOT NULL,
    imapok tinyint(3) unsigned NOT NULL default '1',
    PRIMARY KEY  (id),
    UNIQUE KEY id (id),
    UNIQUE KEY address (address),
    KEY id_2 (id),
    KEY address_2 (address)
  ) TYPE=MyISAM;

CREATE TABLE virtual (
    address varchar(255) NOT NULL,
    goto varchar(255) NOT NULL,
    UNIQUE KEY address (address)
) TYPE=MyISAM;

GRANT SELECT
ON maildb.*
TO maildb_user@localhost
IDENTIFIED BY '****chose a password here****'
;

The user maildb_user will be used by both Postfix and Courier.

Modify Postfix’s main.cf

Add these lines:

transport_maps=mysql:/usr/local/etc/postfix/mysql_transport.cf
virtual_mailbox_maps=mysql:/usr/local/etc/postfix/mysql_virtual_mbox.cf
virtual_uid_maps=mysql:/usr/local/etc/postfix/mysql_uids.cf
virtual_gid_maps=mysql:/usr/local/etc/postfix/mysql_gids.cf
virtual_mailbox_base=/var/spool/postfix/virtual/
virtual_maps=mysql:/usr/local/etc/postfix/mysql_virtual.cf
# 100 MB
virtual_mailbox_limit=102400000
virtual_minimum_uid=100

…and create the mysql_XXX.cf files:

# mysql_transport.cf
user=maildb_user
password=******
dbname=maildb
table=transport
select_field=transport
where_field=domain
hosts=localhost

# mysql_virtual_mbox.cf
user=maildb_user
password=*****
dbname=maildb
table=users
select_field=maildir
where_field=address
hosts=localhost

# mysql_uids.cf
user=maildb_user
password=*****
dbname=maildb
table=users
select_field=uid
where_field=address
hosts=localhost

# mysql_gids.cf
user=maildb_user
password=*****
dbname=maildb
table=users
select_field=gid
where_field=address
hosts=localhost

# mysql_virtual.cf
user=maildb_user
password=******
dbname=maildb
table=virtual
select_field=goto
where_field=address
hosts=localhost

Important: Don’t forget that the mysql_xxx.cf files should not be world readable (mode 400) and chown postfix:postfix them, due to the fact that they contain a mysql password, and the password allows to get the users password out of the table! Whenever you modify these files, make sure it is readable by postfix:postfix (e.g. if you check the file out of a RCS repository, beeing the root user, the file will be owned by root and not by postfix, so that the postfix daemon cannot read the file anymore!)

Also add $transport_maps to mydestination.

Finally Steps

Create /var/spool/postfix/virtual/ and chown it to user/group postfix. For each user, create a mailbox directory and chown it to them.

Filling Data into the Mysql tables

For creating the crytped password, use Mysql’s encrypt function.

A simple example (one domain, one user, multiple aliases):

mysql> select * from transport;
+----------+-----------+
| domain   | transport |
+----------+-----------+
| ntecs.de | virtual:  |
  +----------+-----------+
1 row in set (0.01 sec)

mysql> select * from users;
+---------+------------------+-------+---------------+-----------------+------+------+...
| id      | address          | clear | crypt         | name            | uid  | gid  |
+---------+------------------+-------+---------------+-----------------+------+------+...
| michael | michael@ntecs.de | abc   | 9GZRP/ADKguPk | Michael Neumann | 5000 | 5000 |
+---------+------------------+-------+---------------+-----------------+------+------+...
...+----------------------------+----------+-------------------+-----------+--------+
   | home                       | domain   | maildir           | quota     | imapok |
...+----------------------------+----------+-------------------+-----------+--------+
   | /var/spool/postfix/virtual | ntecs.de | ntecs.de/michael/ | 102400000 |      1 |
...+----------------------------+----------+-------------------+-----------+--------+

1 row in set (0.00 sec)

mysql> select * from virtual;
+-------------------+------------------+
| address           | goto             |
+-------------------+------------------+
| mneumann@ntecs.de | michael@ntecs.de |
| @ntecs.de         | michael@ntecs.de |
+-------------------+------------------+
2 rows in set (0.00 sec)

TODO: It might be a good idea to rename table users to mailbox, virtual to aliases and perhaps transport to domain.

NOTE: Each email that should be deliverable, must have an entry in the virtual table, even if it points to itself (e.g. mneumann@ntecs.de => mneumann@ntecs.de).

For more information about Postfix and Mysql read /usr/local/share/doc/postfix/MYSQL_README.

Courier Imap

cd /usr/ports/mail/courier-imap
make WITHOUT_PAM=yes WITH_MYSQL=yes
make install

After installation, we first create a certificate for imapd-ssl. First edit /usr/local/etc/courier-imap/imapd.cnf (copy from imapd.cnf.dist):

RANDFILE = /usr/local/share/courier-imap/imapd.rand

[ req ]
default_bits = 1024
encrypt_key = yes
distinguished_name = req_dn
x509_extensions = cert_type
prompt = no

[ req_dn ]
C=DE
L=Frankfurt
O=Courier Mail Server
OU=Automatically-generated IMAP SSL key
CN=localhost
emailAddress=postmaster@ntecs.de

[ cert_type ]
nsCertType = server

Edit the [ req_dn ] section as you like.

Then, create the certificate with:

/usr/local/share/courier-imap/mkimapdcert

Now we’ll copy the example configuration files to their real name:

cd /usr/local/etc/courier-imap
mv authdaemonrc.dist authdaemonrc
mv authmysqlrc.dist authmysqlrc
mv imapd.dist imapd
mv imapd-ssl.dist imapd-ssl

Edit authdaemonrc and remove all but "authmysql" from authmodulelist.

Edit authmysqlrc:

MYSQL_SERVER            localhost
MYSQL_USERNAME          maildb_user
MYSQL_PASSWORD          *****
MYSQL_SOCKET            /tmp/mysql.sock
MYSQL_DATABASE          maildb
MYSQL_USER_TABLE        users
MYSQL_CRYPT_PWFIELD     crypt
MYSQL_UID_FIELD         uid
MYSQL_GID_FIELD         gid
MYSQL_LOGIN_FIELD       id
MYSQL_HOME_FIELD        home
MYSQL_NAME_FIELD        name
MYSQL_MAILDIR_FIELD     maildir
MYSQL_QUOTA_FIELD       quota
MYSQL_WHERE_CLAUSE      imapok=1

Edit imapd:

nothing to edit for now

In imapd-ssl there are two important options:

IMAPDSSLSTART=NO     (enables imapd over SSL)
IMAPDSTARTTLS=YES    (imap via TLS: TLS is application level encryption)

Both should be set to these values by default, so no editing is needed here.

Setting IMAPDSSLSTART=YES and IMAPDSTARTTLS=NO would work too, but TLS is newer, so we’ll use it here, and I believe it transmits unsensitive data in clear text (the connection establishing part, until password and data is transfered, then switchs to encryption). Again I think, TLS is easier to cope with in terms of logging etc.

Do some cleanup:

cd /usr/local/etc/rc.d
rm courier-imap-imapd.sh.sample    (we don't need this)
rm courier-imap-pop3d.sh.sample    (we don't need this either)
ln -s /usr/local/libexec/courier-imap/imapd-ssl.rc courier-imap-imapd-ssl.sh

And start the daemon (or reboot):

sh /usr/local/etc/rc.d/courier-imap-imapd-ssl.sh start

Postfix SMTP Setup

By default, everyone from everywhere (untrusted client) is allowed to send your postfix server mail he is responsible for (mydestination paramter, or relay_domains), whereas relaying to other hosts is not allowed.

Only allow relaying on the host itself by setting:

mynetworks_style = host

Remove saslauthd.sh from /usr/local/etc/rc.d as we don’t need it:

cd /usr/local/etc/rc.d
rm saslauthd.sh

In /usr/local/lib/sasl2 create file smtpd.conf:

# smtpd.conf
pwcheck_method:auxprop
mech_list: plain login

mysql_user: maildb_user
mysql_passwd: *******
mysql_hostnames: localhost
mysql_database: maildb
mysql_statement: select clear from users where id = '%u'
# mysql_verbose: 1

After everything works as expected, we can turn "mysql_verbose" off (comment it out).

Make this file chmod 400, as it contains the mysql database password.

Note that for SASL authentification we need the clear-text password (column clear), whereas for IMAP autentification the crypted-password (column crypt) is required. This is a bit strange.

Again, modify postfix’s main.cf configuration file (in /usr/local/etc/postfix):

smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain =
broken_sasl_auth_clients = yes

smtpd_recipient_restrictions =
  permit_mynetworks,
  permit_sasl_authenticated,
  reject_unknown_sender_domain,
  reject_unauth_pipelining,
  reject_unknown_recipient_domain,
  reject_non_fqdn_sender,
  reject_non_fqdn_recipient,
  reject_non_fqdn_hostname,
  check_relay_domains

Enable TLS/SSL

We need a certificate first. But as we still have one for the Courier-IMAP server, we’ll reuse that. We should give it 400 permissions first:

cd /usr/local/share/courier-imap
chmod 400 imapd.pem

Then again, we edit Postfix’s main.cf configuration file, where we add the following:

smtp_use_tls = yes
smtpd_use_tls = yes
smtp_tls_note_starttls_offer = yes
smtpd_tls_key_file = /usr/local/share/courier-imap/imapd.pem
smtpd_tls_cert_file = /usr/local/share/courier-imap/imapd.pem
smtpd_tls_CAfile = /usr/local/share/courier-imap/imapd.pem
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom

# allow authentification (e.g. PLAIN/LOGIN) only in TLS mode
smtpd_tls_auth_only = yes

The last directive (smtpd_tls_auth_only) is very important, as it only allows authentification to be performed when TLS is established. This protects the client, to accidentially send their passwords in clear text.

Todo’s

  • run Postfix in chroot’ed environment.
  • run Courier-Imap as non-root user
  • change /tmp/mysql.sock to /var/mysql/mysql.sock
  • use mail/messagewall to protect even more against spam