HOWTO make a Mailserver
Running FreeBSD 5.1, Cyrus-imapd with authentication over MySQL (pam-mysql), and Postfix as its master.

Revision history

26/1-2004: Added information about make.conf (In chapter 6).

10/12-2003: Added some alias and virtual aliases information.

4/12-2003: Official release.

10/11-2003: Began the writing of this document.

Table of Contents

Some useful links

my.lostinfo.com, www.cse.ucsc.edu, www.delouw.ch kummefryser.dk, gentoo.org, sqlzoo.net, lists.suse.com.

0. Motivation

I wrote this document because, the only available documentation on the subject, is targeted for linux, and following it, did not give me a working mailserver! You may think of this as a HOWTO port, from linux to FreeBSD.

1. Install FreeBSD 5.1

You can get FreeBSD on their public ftp.
More info on how to install FreeBSD can be found in the FreeBSD Handbook.

Once the installation menu pops up, select Standard

Press ok

In the diskmanager: Use the entire disk (Press a)

NOTE: Don't use a disk smaller than 2GB

Continue (Press q)

Select standard in the Boot Manager menu.

Press ok

In the disklabel editor: Use autodefaults (Press a)

NOTE: I if the /var folder is too small (depending on the number of users on your system) you should make it at least 200mb / user. this folder is where the mails will be stored physsically, when the mailserver starts to run. To make it larger, write down the size of the /tmp label, then delete the /usr, /tmp and /var (Pressing d will do this), select the Top line of the screen (saying Disk: ....) and press c for create. Choose the size of the /var partiotion (You can eitther specify the size in bytes og prepend and M indicating that the size is in megabytes). Choose the mountpoint for this label.
Create like this:
1) /var must be 200M * number of users
2) /tmp must be the same size as before or smaller, but the least 256M
3) /usr should fill out the rest of the disk (just use the sugested number of bytes), but at least 1.5GigaBytes (Othervise you'll have a problm compiling everything later on)

Press q to quit the editor

Select a minimal installation (Mark with space, then select ok)

Choose medium to install from the list

Answer yes to the question of 'point of no return'!

Wait....

Press ok

Answer no to the use of the maschi9ne as a network gateway

Answer no to use inetd

Answer no th euse the machine as a anonymous ftp

Answer no use the machine as a NFS server, and client

Select the medium security profile.

press ok

Answer no to customize your console settings

set the timezone

enable linux executables compatatbility

wait....

Answer no to ps/2 or serial mouse (If you have one, you will not need it)

No browsing the the collection of thousand to run applications (We'll be using ports)

Do not add any innitial accounts (The system will be accountless)

Make the root password

Dont't visit the menu of general options

Back in the main menu ; select Configure

Select Distributions

Mark Ports (with space) and the select ok

Wait....

Select Exit and Exit Install...

Reboot

HINT: If you want to configure the server through ssh (which I highly recommend) you will have to change the /etc/ssh/sshd_config file, to allow root login. This is done by changing the line #PermitRootLogin no into PermitRootLogin yes.
Afterwards, you'll have to restart the sshd.

Then run through these tasks, to ready the system for install:

Now add the ip address and hostname to the /etc/hosts file, ex.

192.168.0.10 mymailserver mymailserver.mydomain.com

where 192.168.0.10 is the ip address of the server, mymailserver is the hostname and mydomain.com is the domain of the mailserver.

Install cvsup

cd /usr/ports/net/cvsup-without-gui
make install clean

Get the supfile

Finally, download this supfile to the the /root directory, and edit line 51 to match a FreeBSD ftp mirror near you. See a list here. And run the command

/usr/local/bin/cvsup /root/upgrade.sup

This will upgrade all the ports to the latest cvs versions (and thereby correcting a problem with pam-mysql).
Now we are readdy to begin installing the software.

Install portupgrade

cd /usr/ports/sysutils/portupgrade
make install clean

After finished install, run

/usr/local/sbin/portupgrade -Cvac

NOTE: Again, if the portupgrade is not the path, try logging out and in again.

To upgrade the installed software to the latest versions.
For maintainance you run these to last steps once in a while, to get the latest security patches installed.

It is important to run cvsup and portupgrade at this early stage, in order to recompile as little as possible. If run later, more software will have to be upgraded.

2 Install the software

2.1 Install MySQL server (and client)

cd /usr/ports/databases/mysql40-server
make install clean

2.3 Install Postfix

cd /usr/ports/mail/postfix
make install clean
  _____________________________________________________________________________
 |                         Postfix configuration options                       |
 |                                                                             |  
 | Please select desired options:                                              |  
 |  _________________________________________________________________________  |  
 | | [ ] NOPCRE      DISABLE Perl Compatible Regular Expressions             | |  
 | | [ ] SASL        Cyrus SASLv1 (Simple Authentication and Security Layer) | |  
 | | [X] SASL2       Cyrus SASLv2 (Simple Authentication and Security Layer) | |  
 | | [ ] SASLKRB     If your SASL requires Kerberos select this option       | |  
 | | [X] TLS         SSL and TLS                                             | |  
 | | [ ] IPv6        IPv6 support                                            | |  
 | | [ ] IPv6TLS     IPv6 support with SSL and TLS                           | |  
 | | [ ] DB3         Berkeley DB3 (required if SASL also built with DB3)     | |  
 | | [ ] DB40        Berkeley DB4.0 (required if SASL also built with DB4.0) | |  
 | | [X] DB41        Berkeley DB4.1 (required if SASL also built with DB4.1) | |  
 | | [X] MySQL       MySQL map lookups                                       | |  
 | | [ ] PgSQL       PostgreSQL v7.3 map lookups                             | |  
 | | [ ] PgSQL72     PostgreSQL v7.2 map lookups                             | |  
 | | [ ] OpenLDAP12  OpenLDAP 1.2 map lookups                                | |  
 | | [ ] OpenLDAP20  OpenLDAP 2.0 map lookups                                | |  
 | | [ ] OpenLDAP21  OpenLDAP 2.1 map lookups                                | |  
 | | [ ] Test        SMTP/LMTP test server and generator                     | |
 | \_________________________________________________________________________/ |  
 |-----------------------------------------------------------------------------| 
 |                           [  OK  ]       Cancel                             |  
 \_____________________________________________________________________________/  

Answer y to the question about adding group mail and user postfix
Answer n to activating postfix in /etc/mail/mailer.conf

2.4 Install Cyrus

cd /usr/ports/mail/cyrus-imapd2
make install clean WITH_BDB_VER=41

2.5 Install saslauthd2

cd /usr/ports/security/cyrus-sasl2-saslauthd
make install clean WITH_BDB_VER=41

2.6 Install pam-mysql

cd /usr/ports/security/pam-mysql
make install clean

3 Configuring the system

3.1 Configuring pam_mysql

Now Copy/move/symlink /usr/local/lib/pam_mysql.so to /usr/lib/pam_mysql.so to be able to use it. Read files in /usr/local/share/doc/pam_mysql for info on how to set up.
This can be done by the following command:

ln -s /usr/local/lib/pam_mysql.so /usr/lib/pam_mysql.so

Add the following line to /etc/pam.d/imap:

auth sufficient  pam_mysql.so user=postfix passwd=postfix host=localhost db=mysql table=user usercolumn=user passwdcolumn=password crypt=1
account required pam_mysql.so user=postfix passwd=postfix host=localhost db=mysql table=user usercolumn=user passwdcolumn=password crypt=1

When done, copy the /etc/pam.d/imap into /etc/pam.d/sieve, /etc/pam.d/pop and /etc/pam.d/smtp, in order to enable pam_mysql for these services too.

NOTE: If using FreeBSD 4.9 comment out the line containing imap and add the following lines to /etc/pam.conf instead
imap auth sufficient  pam_mysql.so user=postfix passwd=postfix host=localhost db=mysql table=user usercolumn=user passwdcolumn=password crypt=1
imap account required pam_mysql.so user=postfix passwd=postfix host=localhost db=mysql table=user usercolumn=user passwdcolumn=password crypt=1
Do the same for pop and sieve

To avoid a tedeous error message in your log file everytime a mail arrives, you must execute the following command (maybe sometime in the future I will find a nicer way to stop this, but for now, this will do):

cd /usr/local/lib/sasl2
ls -l libotp.*
rm libotp.*
The libotp* files are the one-time-password plugin, of which you probably have no use.

3.2 Configuring the MySQL server

Now set the password for the root MySQL user. This is done with:

NOTE: You will have to start the server, before accessing it and changing the root password. This is done, either by rebooting or typing the command /usr/local/etc/rc.d/mysql-server.sh start

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

3.2.1 Securing MySQL

Because you are using MySQL to authenticate users, you need to restrict network access to Port 3306.

I suggest to just bind MySQL only to the loopback interface 127.0.0.1. This makes sure nobody can connect to your MySQL Daemon via the network.

Edit /usr/local/etc/rc.d/mysql-server.sh and change line 10 as following:

Original line:

/usr/local/bin/mysqld_safe --user=mysql --datadir=${DB_DIR} --pid-file=${PIDFILE} > /dev/null &

Changed line:

/usr/local/bin/mysqld_safe --user=mysql --bind-address=127.0.0.1 --datadir=${DB_DIR} --pid-file=${PIDFILE} > /dev/null &

Restart your MySQL-Daemon by issuing /usr/local/etc/rc.d/mysql-server.sh stop; /usr/local/etc/rc.d/mysql-server.sh start

To ensure the configuration change was successful issue: netstat -an | grep LISTEN. The Output should be looking similar to this:

tcp4       0      0  127.0.0.1.3306         *.*                    LISTEN
tcp4       0      0  *.587                  *.*                    LISTEN
tcp4       0      0  *.25                   *.*                    LISTEN
tcp4       0      0  *.22                   *.*                    LISTEN
tcp6       0      0  *.22                   *.*                    LISTEN

The startup script can be called manually with the following parameters:
Usage: /usr/local/etc/rc.d/mysql-server.sh { start | stop }

You can test the MySQL daemon with the benchmarks in the sql-bench directory:
cd sql-bench ; perl run-all-tests

Please report any problems with the /usr/local/bin/mysqlbug script!

3.3 Configuring Cyrus

To activate the Cyrus-IMAPd at system boot time, add the following line to /etc/rc.conf:

cyrus_imapd_enable="YES"

3.3.1 Editing the config files

3.3.1.1 /etc/services

If you like to use sieve (a mail filtering language), you must change an entry in /etc/services. Add or change the following line:

sieve           2000/tcp

3.3.1.2. /usr/local/etc/imapd.conf

You have to change some parameters in /usr/local/etc/imapd.conf.

postmaster: postmaster
configdirectory: /var/imap
partition-default: /data/mail
admins: cyrus
allowanonymouslogin: no
allowplaintext: yes
sasl_mech_list: PLAIN
autocreatequota: 0
reject8bit: no
timeout: 30
poptimeout: 10
dracinterval: 0
drachost: localhost
sasl_pwcheck_method: saslauthd
sievedir: /var/imap/sieve
sendmail: /usr/sbin/sendmail
sieve_maxscriptsize: 32
sieve_maxscripts: 5

3.3.2 Creating the directories

There must be created different directories. Additionally you should change some attributes of the filesystem

3.3.2.1 /var/imap

cd /var
mkdir imap
chown cyrus:mail imap
chmod 750 imap

3.3.2.2. /data/mail

mkdir /data
cd /data
mkdir mail
chown cyrus:mail mail
chmod 750 mail

3.3.2.3. /var/imap/sieve

cd /var/imap
mkdir sieve
chown cyrus:mail sieve
chmod 750 sieve

3.3.2.4. The rest of the directories

The rest of the directories can be created by the tool mkimap (the partition-default directory among others)

su cyrus
/usr/local/cyrus/bin/mkimap
exit

3.3.3. Creating the TLS/SSL Certificate

If you want to enable Cyrus' TLS/SSL facilities, you have to create a certificate first. This requires an OpenSSL installation

cd /var/imap

openssl req -new -nodes -out req.pem -keyout key.pem  
openssl rsa -in key.pem -out new.key.pem
openssl x509 -in req.pem -out ca-cert -req -signkey new.key.pem -days 999 
chmod 600 ca-cert

cat ca-cert >> server.pem

chown cyrus:mail server.pem
chmod 600 server.pem

Now add the following lines to /usr/local/etc/imapd.conf:

tls_ca_file: /var/imap/server.pem
tls_cert_file: /var/imap/server.pem
tls_key_file: /var/imap/server.pem

3.4. Configuring Postfix

Create the file /etc/periodic.conf, with the following contents:

daily_clean_hoststat_enable = "NO"
daily_status_mail_rejects_enable = "NO"
daily_status_include_submnit_mailq = "NO"
daily_submit_queryrun = "NO"

Add the following line to /etc/rc.conf.

sendmail_enable="NONE"                                

Then, to make postfix starte with the other services, make the symbolic link:

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

Make sure, that the postfix user is a member of the mail group (look for a line like mail:*:6:postfix in /etc/group).
Also make sure, that the /usr/local/etc/sasldb* file is reable by the mail group.

Postfix needs two major config files: /usr/local/etc/postfix/main.cf and /usr/local/etc/postfix/master.cf

3.4.1 /usr/local/etc/postfix/master.cf

You need to change just one line:

old:

# Cyrus 2.1.5 (Amos Gouaux)
cyrus     unix  -       n       n       -       -       pipe
  user=cyrus argv=/cyrus/bin/deliver -r ${sender} -e -m ${extension} ${user}

new:

# Cyrus 2.1.5 (Amos Gouaux)
cyrus     unix  -       n       n       -       -       pipe
  user=cyrus argv=/usr/local/cyrus/bin/deliver -r ${sender} -m ${extension} ${user}

What affect that changes?

A look to the cyrus man-pages man deliver clears that issue:

The -e option is no longer used!

3.4.2 /usr/local/etc/postfix/main.cf

Here you need to change some more things like hostname, relaying, alias-lookups etc.

First change hostname and domainname:

myhostname = foo.bar.org
mydomain = bar.org

mydestination

Here you have to put all domainnames that are local (corresponding to sendmail's /etc/mail/sendmail.cw). If you have multiple domains separate them with comma.

mydestination = foo.bar.org, example.com, furchbar-grausam.ch, 
 whatever.domain.tld, mysql:/etc/postfix/mysql-mydestination.cf

The following works for me:

mydestination = $myhostname, localhost.$mydomain, $mydomain,
        localhost, mail.$mydomain, www.$mydomain, ftp.$mydomain

Relayhost

Here you define where to deliver outgoing mails. If you do not provide any host. mails are delivered directly to the destination smtp host. Usually your relayhosts are your providers smtp-server.

relayhost = relay01.foobar.net relay02.foobar.net relay03.foobar.net

Mailtransport

Here you define how the mails accepted for local delivery should be handled. In your situation mails should be delivered by the cyrus delivery-program.

mailbox_transport = cyrus

At the end of file you need to add:

virtual_mailbox_base = /data/mail
virtual_maps = mysql:/usr/local/etc/postfix/remote_aliases.cf
virtual_mailbox_maps = mysql:/usr/local/etc/postfix/aliases.cf
transport_maps = mysql:/usr/local/etc/postfix/transport.cf
virtual_uid_maps = mysql:/usr/local/etc/postfix/vuids.cf
virtual_gid_maps = mysql:/usr/local/etc/postfix/vgids.cf

local_recipient_maps = unix:passwd.byname, $alias_maps, $virtual_mailbox_maps

SMTP Authentication with SASL and PAM

Put the following in your /usr/local/etc/postfix/main.cf

smtpd_sasl_auth_enable = yes
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = example.com
broken_sasl_auth_clients = yes

You also need to create the file /usr/local/lib/sasl2/smtpd.conf with the following content:

pwcheck_method: saslauthd

4 Configure/create the database entries

4.1 Config files

Create the following files:

4.1.1 /usr/local/etc/postfix/transport.cf

user = postfix
password = postfix
dbname = mail
table = transport
select_field = transport
where_field = domain
hosts = localhost

4.1.2 /usr/local/etc/postfix/vuids.cf

user = postfix
password = postfix
dbname = mail
table = aliases
select_field = vuid
where_field = alias
hosts = localhost

4.1.3 /usr/local/etc/postfix/vgids.cf

user = postfix
password = postfix
dbname = mail
table = aliases
select_field = vgid
where_field = alias
hosts = localhost

4.1.4 /usr/local/etc/postfix/aliases.cf

user = postfix                  
password = postfix              
dbname = mail                   
table = aliases
select_field = maildir
where_field = alias
hosts = localhost

4.1.5 /usr/local/etc/postfix/remote_aliases.cf

user = postfix
password = postfix
dbname = mail
table = remote_aliases
select_field = rcpt
where_field = alias
hosts = localhost
NOTE: Also remember that relay_domains needs to contain $transport_maps to lookup local virtual domains. alias_maps does not affect the virtual agent.

Run the following command, to create /etc/aliases.db

postmap /etc/aliases

4.2 MySQL Setup

Create a mysql user/database for postfix:

gaia:~# mysql -p -u root
mysql> use mysql;
mysql> insert into user (Host, User, Password) values('localhost','postfix', password('postfix'));
mysql> insert into db (Host, Db, User, Select_priv) values('localhost','mail','postfix','Y');

mysql> create database mail;

Create the tables needed:

mysql> use mail;
mysql> create table transport (domain varchar(255) PRIMARY KEY, transport char(8));
mysql> create table aliases (vuid int(6), vgid int(6), alias varchar(255) PRIMARY KEY, maildir varchar(255));
mysql> create table remote_aliases (alias varchar(255) PRIMARY KEY, rcpt varchar(255));

Finish off by reloading the mysql server:

gaia:~# mysqladmin -p reload

4.3 Table explanation

4.3.1 The transport table (from the man page)

The optional transport table specifies a mapping from domain hierarchies to message delivery transports and/or relay hosts. The mapping is used by the trivial-rewrite(8) daemon.

Example:

mysql> select * from transport; 
+-----------+-----------+
| domain    | transport |
+-----------+-----------+
| gostil.dk | virtual:  |
+-----------+-----------+

4.3.2 The aliases table

This table is used for 2 things:
Map aliases to mailboxes / maildirs.
Map uid/gids to mailboxes.

Example:

mysql> select * from aliases;
+------+------+---------------+------------------------+
| vuid | vgid | alias         | maildir                |
+------+------+---------------+------------------------+
| 1002 | 1002 | dvp@gostil.dk | gostil.dk/dvp/Maildir/ |
+------+------+---------------+------------------------+

4.3.3 The remote_aliases table

This final table is used to map aliases at one domain to aliases at another domain.

Example:

mysql> select * from remote_aliases;
+----------------+----------------+
| alias          | rcpt           |
+----------------+----------------+
| test@gostil.dk | daniel@rtfm.dk |
+----------------+----------------+

5 Adding the users

# mysql -p
mysql> use mail;
mysql> insert into aliases values([vuid], [vgid], "[account]", "[path]");
mysql> quit

# cyradm -u cyrus localhost
localhost> cm user.[username]
localhost> quit

# saslpasswd2 [username]

Explanation of the above [bracketed] parts:
[vuid] is the virtual user id (an integer).
[vgid] is the virtual group id (an integer).
[account] is the accountname, eg. "dummy@testdomain.com".
[path] is the path of the virtual mailbox, eg. "user/dummy".
[username] is the username, eg. dummy

Repeat the above for every user you want to add to the server.

If you want to use aliases (ie. map several mail addresses to one address) follow the following procedure for each alias

# mysql -p
mysql> use mail;
mysql> insert into remote_aliases values("virtual@mydoamin.com", "actual@mydomain.com");

It is also possible to map addresses from one domain to another:

# mysql -p
mysql> use mail;
mysql> insert into remote_aliases values("virtual@somedomain.com", "actual@mydomain.com");

In both examples mails send to virtual@mydomain.com or virtual@somedomain.com will be forwarded to actual@mydomain.com.

6 Maintainance

You should update the server software once and again (weekly the least).
To do this, follow the steps of chapter 1, on how to run cvsup and portupgrade.

ATTENTION: When portupgrade runs, it does not know what system variables vere declared when you first installed the ports.
And since we were using one during the installation of Cyrus, (WITH_BDB_VER=41) we will have to tell make to use it again.
This is done by adding the system variables we want in air, when running make to the file /etc/make.conf
This should make the file look something like this:
# -- use.perl generated deltas -- #
# Created: Wed Dec  3 22:14:49 2003
# Setting to use base perl from ports:
PERL_VER=5.6.1
PERL_VERSION=5.6.1
PERL_ARCH=mach
NOPERL=yo
NO_PERL=yo
NO_PERL_WRAPPER=yo
WITH_BDB_VER=41

7 Copyright Message

This HOWTO is free to use for anybody. I do not take any responsibility in damages obtained or dataloss by following this HOWTO.

8 Miscellaneous and Acknowledgements

I would like to thank my girlfriend, Tine, for supporting my tedeous self during the writing (and testing) of this document.

I would like to thank Simon Kongshøj for the design and converting routines used to create this document.