Step by step set up a mail server
Creating a Mail Server on Ubuntu (Postfix, Courier, SSL/TLS,SpamAssassin, ClamAV, Amavis)
Easy to follow howto on setting up a mail server with unlimited users and domains
with IMAP/Pop access, anti-spam, anti-virus, secure authentication, encrypted traffic, web mail interface and more.
Based on an Ubuntu distribution platform, but instructions are distro generic.
Examples are run on Amazon AWS ec2, but only for demonstration purposes.
We’ll be building a mail server made up of the following components:
- Postfix is the mail transfer agent (MTA) responsible for accepting new messages and storing them on your server
as well as allowing authorised users to send e-mail. - Courier sits in front of Postfix and provides an IMAP and POP3 interface for clients to connect to.
- SASL with SSL and TLS allows you to authenticate and communicate with the mail server securely.
- SpamAssassin will analyse your e-mails as they arrive and will filter out what it thinks is spam.
- ClamAV will scan e-mails for viruses before delivering it to your inbox.
- Amavis ties SpamAssasin and ClamAV together, and is itself hooked into Postfix.
- MySQL will be used to manage user accounts and e-mail forwarding.
Installation
First, switch to the root user unless, of course, you like typing sudo
sudo su -
For simplicity, we’ll install all the software in one go:
apt-get update apt-get install -y mysql-server postfix postfix-mysql libsasl2-modules libsasl2-modules-sql libgsasl7 libauthen-sasl-cyrus-perl sasl2-bin libpam-mysql clamav-base libclamav6 clamav-daemon clamav-freshclam amavisd-new spamassassin spamc courier-base courier-authdaemon courier-authlib-mysql courier-imap courier-imap-ssl courier-pop courier-pop-ssl courier-ssl
During the installation of MySQL you will be prompted for the root user password, as shown:
Enter a secure password, and don’t forget it!
Similarly, during the installation of Courier you will be presented with the following configuration prompts:
Similarly, during the installation of Courier you will be presented with the following configuration prompts:

Choose No

Choose OK

Choose Internet Site

Enter your mail server name (e.g. replace example.com with your own domain).
Make sure you have this subdomain configured in your DNS records.

Choose OK
I won’t walk you through the parameters we’re using when configuring Postfix as I want to keep this guide light.
If you’re interested, you can find more information from the main pages.
mv /etc/postfix/main.cf{,.default} vi /etc/postfix/main.cf |
Copy/paste the following (change all instances of mail.example.com):
myorigin = /etc/mailname smtpd_banner = $myhostname ESMTP $mail_name biff = no append_dot_mydomain = no readme_directory = no mydestination = relayhost = mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mynetworks_style = host mailbox_size_limit = 0 virtual_mailbox_limit = 0 recipient_delimiter = + inet_interfaces = all message_size_limit = 0 # SMTP Authentication (SASL) smtpd_sasl_auth_enable = yes broken_sasl_auth_clients = yes smtpd_sasl_security_options = noanonymous smtpd_sasl_local_domain = # Encrypted transfer (SSL/TLS) smtp_use_tls = yes smtpd_use_tls = yes smtpd_tls_cert_file = /etc/ssl/private/mail.example.com.crt smtpd_tls_key_file = /etc/ssl/private/mail.example.com.key smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache # Basic SPAM prevention smtpd_helo_required = yes smtpd_delay_reject = yes disable_vrfy_command = yes smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject # Force incoming mail to go through Amavis content_filter = amavis:[127.0.0.1]:10024 receive_override_options = no_address_mappings # Virtual user mappings alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases virtual_mailbox_base = /var/spool/mail/virtual virtual_mailbox_maps = mysql:/etc/postfix/maps/user.cf virtual_uid_maps = static:5000 virtual_gid_maps = static:5000 virtual_alias_maps = mysql:/etc/postfix/maps/alias.cf virtual_mailbox_domains = mysql:/etc/postfix/maps/domain.cf |
mv /etc/postfix/master.cf{,.default} vi /etc/postfix/master.cf |
Copy/paste the following (no changes required):
# # # Postfix master process configuration file. For details on the format # of the file, see the master(5) manual page (command: "man 5 master"). # # Do not forget to execute "postfix reload" after editing this file. # # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) # ========================================================================== smtp inet n - - - - smtpd smtps inet n - - - - smtpd -o smtpd_tls_wrappermode=yes submission inet n - - - - smtpd pickup fifo n - - 60 1 pickup -o content_filter= -o receive_override_options=no_header_body_checks cleanup unix n - - - 0 cleanup qmgr fifo n - n 300 1 qmgr tlsmgr unix - - - 1000? 1 tlsmgr rewrite unix - - - - - trivial-rewrite bounce unix - - - - 0 bounce defer unix - - - - 0 bounce trace unix - - - - 0 bounce verify unix - - - - 1 verify flush unix n - - 1000? 0 flush proxymap unix - - n - - proxymap proxywrite unix - - n - 1 proxymap smtp unix - - - - - smtp # When relaying mail as backup MX, disable fallback_relay to avoid MX loops relay unix - - - - - smtp -o smtp_fallback_relay= showq unix n - - - - showq error unix - - - - - error retry unix - - - - - error discard unix - - - - - discard local unix - n n - - local virtual unix - n n - - virtual lmtp unix - - - - - lmtp anvil unix - - - - 1 anvil scache unix - - - - 1 scache # # ==================================================================== # Interfaces to non-Postfix software. Be sure to examine the manual # pages of the non-Postfix software to find out what options it wants. # # Many of the following services use the Postfix pipe(8) delivery # agent. See the pipe(8) man page for information about ${recipient} # and other message envelope options. # ==================================================================== # # maildrop. See the Postfix MAILDROP_README file for details. # Also specify in main.cf: maildrop_destination_recipient_limit=1 # maildrop unix - n n - - pipe flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient} # # See the Postfix UUCP_README file for configuration details. # uucp unix - n n - - pipe flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient) # # Other external delivery methods. # ifmail unix - n n - - pipe flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient) bsmtp unix - n n - - pipe flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient scalemail-backend unix - n n - 2 pipe flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension} mailman unix - n n - - pipe flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py ${nexthop} ${user} amavis unix - - - - 2 smtp -o smtp_data_done_timeout=1200 -o smtp_send_xforward_command=yes -o disable_dns_lookups=yes -o max_use=20 127.0.0.1:10025 inet n - - - - smtpd -o content_filter= -o local_recipient_maps= -o relay_recipient_maps= -o smtpd_restriction_classes= -o smtpd_delay_reject=no -o smtpd_client_restrictions=permit_mynetworks,reject -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o smtpd_data_restrictions=reject_unauth_pipelining -o smtpd_end_of_data_restrictions= -o mynetworks=127.0.0.0/8 -o smtpd_error_sleep_time=0 -o smtpd_soft_error_limit=1001 -o smtpd_hard_error_limit=1000 -o smtpd_client_connection_count_limit=0 -o smtpd_client_connection_rate_limit=0 -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks |
As all our mail users are going to be virtual (i.e. we’re not going to create physical user accounts for each user),
we only need to create one mail directory and one user account.
we only need to create one mail directory and one user account.
groupadd virtual -g 5000 useradd -r -g "virtual" -G "users" -c "Virtual User" -u 5000 virtual mkdir /var/spool/mail/virtual chown virtual:virtual /var/spool/mail/virtual |
Make sure that, if the UID or GID differs from 5000, you update the virtual_uid_maps andvirtual_gid_maps values in
/etc/postfix/main.cf
, and MYSQL_UID_FIELD andMYSQL_GID_FIELD in /etc/courier/authmysqlrc
(later in this guide).
Now we’ll create the database which will store the mail user configuration and forwarding rules.
mysql -uroot -p |
Enter the password you created during the MySQL installation.
Copy/paste the following (change mailuserpassword, example.com and change the admin’s password to something more secure):
CREATE DATABASE mail; GRANT ALL ON mail.* TO mail@localhost IDENTIFIED BY 'mailuserpassword'; FLUSH PRIVILEGES; USE mail; CREATE TABLE IF NOT EXISTS `alias` ( `source` varchar(255) NOT NULL, `destination` varchar(255) NOT NULL default '', `enabled` tinyint(1) unsigned NOT NULL default '1', PRIMARY KEY (`source`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `domain` ( `domain` varchar(255) NOT NULL default '', `transport` varchar(255) NOT NULL default 'virtual:', `enabled` tinyint(1) unsigned NOT NULL default '1', PRIMARY KEY (`domain`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `user` ( `email` varchar(255) NOT NULL default '', `password` varchar(255) NOT NULL default '', `name` varchar(255) default '', `quota` varchar(255) default NULL, `enabled` tinyint(1) unsigned NOT NULL default '1', PRIMARY KEY (`email`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; INSERT INTO `alias` (`source`, `destination`, `enabled`) VALUES ('@localhost', 'admin@example.com', 1); INSERT INTO `alias` (`source`, `destination`, `enabled`) VALUES ('@localhost.localdomain', '@localhost', 1); INSERT INTO `domain` (`domain`, `transport`, `enabled`) VALUES ('localhost', 'virtual:', 1); INSERT INTO `domain` (`domain`, `transport`, `enabled`) VALUES ('localhost.localdomain', 'virtual:', 1); INSERT INTO `domain` (`domain`, `transport`, `enabled`) VALUES ('example.com', 'virtual:', 1); INSERT INTO `user` (`email`, `password`, `name`, `quota`, `enabled`) VALUES ('admin@example.com', ENCRYPT('changeme'), 'Administrator', NULL, 1); |
Note that we’re encrypting the password. Some guides will recommend storing the password in clear text so that you can configure Postfix to support CRAM-* (e.g. CRAM-MD5) authentication methods. I think it’s much more secure to store these passwords encrypted and use SSL/TLS to encrypt your authentication requests. For that reason, we don’t need to store clear text passwords and we don’t need to provide CRAM-* support.
Now that the database is in place we can create the map files to tell Postfix how to communicate with it.
mkdir /etc/postfix/maps vi /etc/postfix/maps/alias.cf |
Copy/paste the following (change mailuserpassword):
user=mail password=mailuserpassword dbname=mail table=alias select_field=destination where_field=source hosts=127.0.0.1 additional_conditions=and enabled = 1 |
vi /etc/postfix/maps/domain.cf |
Copy/paste the following (change mailuserpassword):
user = mail password = mailuserpassword dbname = mail table = domain select_field = domain where_field = domain hosts = 127.0.0.1 additional_conditions = and enabled = 1 |
vi /etc/postfix/maps/user.cf |
Copy/paste the following (change mailuserpassword):
user = mail password = mailuserpassword dbname = mail table = user select_field = CONCAT(SUBSTRING_INDEX(email,'@',-1),'/',SUBSTRING_INDEX(email,'@',1),'/') where_field = email hosts = 127.0.0.1 additional_conditions = and enabled = 1 |
Set restrictive read permissions as these files contain the MySQL mail user’s password.
chmod 700 /etc/postfix/maps/* chown postfix:postfix /etc/postfix/maps/* |
The final part of configuring Postfix is to configure the authentication mechanism. SASL is a authentication layer that provides the ability to receive a user’s credentials in a variety of formats.
mkdir -p /var/spool/postfix/var/run/saslauthd mkdir /etc/postfix/sasl adduser postfix sasl vi /etc/postfix/sasl/smtpd.conf |
Copy/paste the following (change mailuserpassword):
pwcheck_method: saslauthd auxprop_plugin: sql mech_list: plain login sql_engine: mysql sql_hostnames: 127.0.0.1 sql_user: mail sql_passwd: mailuserpassword sql_database: mail sql_select: SELECT password FROM user WHERE email='%u@%r' AND enabled = 1 |
chmod -R 700 /etc/postfix/sasl/smtpd.conf mv /etc/default/saslauthd{,.default} vi /etc/default/saslauthd |
Copy/paste the following (no changes required):
START=yes DESC="SASL Authentication Daemon" NAME="saslauthd" MECHANISMS="pam" MECH_OPTIONS="" THREADS=5 OPTIONS="-r -c -m /var/spool/postfix/var/run/saslauthd" |
vi /etc/pam.d/smtp |
Copy/paste the following (change all instances of mailuserpassword):
auth required pam_mysql.so user=mail passwd=mailuserpassword host=127.0.0.1 db=mail table=user usercolumn=email passwdcolumn=password crypt=1 account sufficient pam_mysql.so user=mail passwd=mailuserpassword host=127.0.0.1 db=mail table=user usercolumn=email passwdcolumn=password crypt=1 |
chmod 700 /etc/pam.d/smtp |
Now let’s configure Courier.
with part 2
Comments
Post a Comment