Setting up a mail server using Postfix on Ubuntu 10.04

Introduction.

Background.

I had already build a VoIP system using CentOS (another Unix-like OS) as a base with trixbox CE providing the VoIP telephony. It was a fun project and for all the same reasons I wanted to do something similar for our messaging. I naturally (or some may argue un-naturally) started thinking about Microsoft Exchange, but was put off by the cost and all the additional features that I might not use! Then I remembered reading something about Postfix and thought “I can setup a VoIP system, surely it can’t be too hard to setup a mail server on a *nix box?” It also seemed like I nice way to create a rounded “communication” solution for our organisation encompassing telephony and email (my thoughts immediately turned to shared calendaring too).

With the availability of devices like the iPhone and iPad (yes, I’m a fan-boy!), it meant that I could create a mail platform that could be accessed “anywhere”, “anytime” on “any device” (and by any device read: iPad or iPhone ?). This was a plus for management, who wouldn’t need to be restricted to carrying around a laptop and 3G modem in order to access emails!

So I started searching on the internet for a cookbook that would outline the steps and one of the first I came across (and ultimately stayed with) was Ivar Abrahamsen’s step-by-step guide at flurdy.com. It’s a fantastic guide that outlines things very clearly with details on “how” and “why” each step is taken.

This HOWTO is effectively a duplicate (and to some degree a subset) of flurdy’s guide, with some of my own changes/omissions based on my own situation and experiences during the install. It was also to some degree intended for my own reference in case I needed to recover my server one day. All credit goes to Ivar Abrahamsen without whom I’d never have got my server off the ground. I didn’t plan on using my server in the cloud so I removed the references to ec2.

So why would you build your own mail server? There are a number of advantages to having your own mail server, and a cursory check of the MX records of a number of organisations I deal with revealed that almost everyone was doing it! Reason 1: I didn’t want to be left out!! More selfishly, it was a way to gain total control over the server, and with my “portable mail” plan relying on IMAP it meant I couldn’t have an ISP reigning me back on disk space!!

I did have concerns about being a target for spammers and hackers, so it was a relief to see the sections in flurdy’s guide that dealt with spam and antivirus as well as the encryption of the traffic – another plus if the aforementioned management decided to access their mail, using webmail from a internet café while on holiday!

Base OS Distribution

This HOWTO is based on Ubuntu Server 10.04 LTS (Lucid Lynx) 64-bit and its Debian base which uses apt-get (aptitude) to download and install its packages.

Why 10.04 and what is LTS? Lucid Lynx (10.04) is an LTS (Long Term Support) release. It (the server version) will be supported with security updates until April 2015. The long life cycle will hopefully mean a more stable platform, enhanced support and more time before we have to consider upgrading.

Hardware configuration

You know what they say about assumptions… Unfortunately I didn’t do enough research when picking my server hardware, and just went for the highest spec machine I could afford. Little did I know there would be issues around driver support for the RAID controller and GRUB’s inability to recognise GPT partitions for disks larger than 2TB! There are workarounds, but I had other options…

Here’s the specifications of the server I used for this exercise:

  • HP Proliant ML110 G6 with Intel G6950 (Dual-Core 2.8Ghz processor)
  • 4GB of RAM
  • 2 x 250GB SATA drives
  • 2 x 2TB SATA drives

Never one to let road block get in the way of accomplishing a goal, I did the following steps in order to prepare the server for (and install) the OS:

  1. First I disabled the onboard RAID and configured the four drives as individuals making sure the 250GB drives were first in the SATA chain.
  2. Next, I followed the advanced Ubuntu setup here to configure software RAID using “mdadm”. There’s quite a good “cheat-sheet” here if you’re interested, otherwise a handy command is:
    sudo cat /proc/mdstat

    to see the various components of the array and

    sudo mdadm --detail /dev/mdx

    where x is 0,1,2…

  3. When selecting the mount point for the array utilising the partitions on the 250GB drives I selected “/” and “/swap”.
  4. When selecting the mount point for the array utilising the partitions on the 2TG drives I selected “/var” as this is the root for the location of the mail directory storage.
  5. Installation was basically a “follow the prompts” procedure, other than the steps taken when partitioning the drives. I interrupted the network detection to define a fixed IP address, and when prompted didn’t install any additional software.
  6. First boot after install was clean and good! No problems, with a little processing overhead as the RAID sync’d.

Software components

OS: Ubuntu Linux

Ah the age old distro argument… Why Ubuntu? It’s free, simple and slick with solid package management. As Ubuntu is derived from Debian the installations used here will be apt-get based..

MTA: Postfix

Simple, free, proven and slick. I agree with flurdy on this one – I’m a sucker for anything that works easily and like it was designed to. Postfix is all that! It’s powerful, well established, but not too bloated, and is security conscious from the start.

POP/IMAP: Courier IMAP

Simple, free and support for IMAPv4.

Database: MySQL

I haven’t had much exposure to database software other than the development I’ve done using MS SQL 2008 Express. MySQL, however, is well supported for the sort of lookups required in a mail server environment and these packages support it “out-of-the-box”.

Content Check: Amavisd-new

Easy plug-in content interface solution for spam, virus checking etc.

Anti-SPAM: SpamAssassin & Postgrey

  • SpamAssassin – A powerful and renowned SPAM fighting tool.
  • Postgrey – an excellent little script to stop 99% of all SPAM. On first contact for specific “from-to” combinations, it tells the sender server to try again in a little while, which (of course) most spammers can’t afford to do. When the proper servers try again after a few minutes delay, it lets the mail through.

Anti-Virus: ClamAV

A FREE virus scanner this is trusted and proven, which includes an update daemon.

Authentication: Cyrus SASL

Secure and trusted cryptography technology for authentication of SMTP traffic.

Encryption: TLS

Secure and trusted cryptography technology for encryption of SMTP traffic. Not to be confused with client encryption technology like GnuPG and S/MIME. Formerly referenced as SSL.

Webmail: Roundcube

For me interface is everything. It’s all about the “look-and-feel” baby! Roundcube is an “ajaxified” and prettier webmail client than SquirrelMail but not quite as solid… yet!

Software configuration

Repositories

For this install you need the main and universe repositories, but I also throw in the other “safe” ones: restricted and multiverse (and partner, when available) if they are not already defined.

 sudo vi /etc/apt/sources.list

Packages

There are a number of packages that constitute this installation. We will install and configure them individually, but before starting first check your package sources are correctly pointing to the main, multiverse, restricted and universe repositories of your current Ubuntu version as detailed above.

Once complete, install any updates for the base install.

sudo aptitude update
sudo aptitude safe-upgrade

MySQL

The first core component to install is MySQL.

sudo aptitude install mysql-client mysql-server

This will prompt you for a MySQL root password. Choose something secure and easy to remember (Current recommendations are for 12 alpha-numeric characters using case mixes). In this example we’ll set it to rootPASSWORD.

Postfix

Next install Postfix, assuming it wasn’t selected at the end of the Ubuntu install and similar steps taken to configure it.

sudo aptitude install postfix postfix-mysql

During the install it will prompt you to choose the “type” of email server. Select “internet site” as it is assumed this install will connect to the internet to send and receive email. The install will also suggest/request a server name. Alter as required using your FQDN or DynDNS equivalent.

openSSH Server

Because it’s easier to cut-and-paste using SSH telnet on your own PC, than keying it all in on the server console.

sudo aptitude install openssh-server

SASL

sudo aptitude install libsasl2-modules libsasl2-modules-sql libgsasl7 libauthen-sasl-cyrus-perl sasl2-bin libpam-mysql

ClamAV

sudo aptitude install clamav-base libclamav6 clamav-daemon clamav-freshclam

Amavis, SpamAssassin, postgrey

sudo aptitude install amavisd-new
sudo aptitude install spamassassin spamc
sudo aptitude install postgrey

Roundcube

sudo aptitude install roundcube roundcube-mysql php-pear php5-cli

phpMyAdmin

sudo aptitude install phpmyadmin

Select “Yes” to set it up and enter the root MySQL password defined above. Create a phpmyadmin MySQL user password, and repeat to confirm. When asked, accept “apache2” as the web server.

ShoreWall

sudo aptitude install shorewall-common shorewall-perl

Courier

sudo aptitude install courier-base courier-authdaemon courier-authlib-mysql courier-imap courier-imap-ssl courier-ssl

The install will prompt you about web directories. You can say “No” to this. It will also warn you about the certificate location. Ignore this.

Extras

I also install a few other packages to add functionality but have nothing to do with the mail server setup.

sudo aptitude install vim mutt lynx
  • vim – although I use Vi for editing files, VIM comes with more features if required.
  • mutt – a simple email client with support for MIME, IMAP, POP3, message threading etc…
  • lynx – is a text-based web browser for use on cursor-addressable character cell terminals and is very configurable (Yeah, I don’t know why either…)

Package status

At any stage to find out which packages you may have installed, you can use the following commands (substituting “postfix” with the package you’re checking):

sudo dpkg --list | grep postfix sudo aptitude search postfix

Stage One: A simple mail server

Now let’s configure a simple mail server using some of the packages installed previously. This HOWTO makes the assumption that you’re either NOT logged on as the root or root isn’t (or hasn’t) been enabled – hence the use of the “sudo” command.

Firewall

Shorewall

UFW is bundled with the 10.04 Ubuntu distribution, but I still prefer Shorewall for servers I setup. Basically at first you want to only allow SSH. Then SMTP & IMAP from your IP only. When you are confident that the mail server is secure, you can open SMTP and IMAP to the world.

Later on in the install you may wish to open web access to the webmail and admin GUI. Once again, this can be restricted to specific IPs.

SSH only

By default Shorewall in Ubuntu has an empty set up. You can find the default values for Shorewall in /usr/share/doc/shorwall/default-config, and examples of configurations in /usr/share/doc/shorwall/examples. To start, we will create the basic set up.

First configure which network adapters are accessing the net.

sudo cp /usr/share/doc/shorewall/default-config/interfaces /etc/shorewall/
sudo vi /etc/shorewall/interfaces net eth0 detect dhcp,tcpflags,logmartians,nosmurfs

Then we will configure network zones

sudo cp /usr/share/doc/shorewall/default-config/zones /etc/shorewall/
sudo vi /etc/shorewall/zones

Add the firewall if not there and the internet as a zone.

fw firewall
# loc ipv4
net ipv4

Then (if needed) specify hosts in the Shorewall hosts file. i.e. If you want to specify what your home IP is etc.

sudo cp /usr/share/doc/shorewall/default-config/hosts /etc/shorewall/
sudo vi /etc/shorewall/hosts # loc eth0:192.168.0.0/24

Then set what the default policy is for firewall access.

sudo cp /usr/share/doc/shorewall/default-config/policy /etc/shorewall/
sudo vi /etc/shorewall/policy $FW net ACCEPT

net $FW DROP info
net all DROP info
# The FOLLOWING POLICY MUST BE LAST
all all REJECT info

For safety in case it goes down.

sudo cp /usr/share/doc/shorewall/default-config/routestopped /etc/shorewall/
sudo vi /etc/shorewall/routestopped
eth0 0.0.0.0 routeback

For higher security you can use the netmask of your IP range if you’re more concerned.

The next step is to define the main firewall rules. You can find predetermined macro rules for Shorewall in the /usr/share/shorewall directory.

sudo cp /usr/share/doc/shorewall/default-config/rules /etc/shorewall/
sudo vi /etc/shorewall/rules

SSH/ACCEPT net $FW
Open for business?

Once your server is working properly, come back to this step and open up the required protocols to the net.

sudo vi /etc/shorewall/rules

Ping/ACCEPT net $FW
# Permit all ICMP traffic FROM the firewall TO the net zone.
ACCEPT $FW net icmp
# Add these for mail services.
SMTP/ACCEPT net $FW
SMTPS/ACCEPT net $FW
Submission/ACCEPT net $FW
IMAP/ACCEPT net $FW
IMAPS/ACCEPT net $FW
# Access for web clients.
Web/ACCEPT net $FW

Firewall configuring is always a risky business and it is easy to lock yourself out. To test the syntax of your configuration run

sudo shorewall check

Then to switch it on during boot

sudo vi /etc/default/shorewall

startup=1

Restart it with

sudo /etc/init.d/shorewall restart

For more details on IP Tables and Shorewall, reference the author’s website.

MTA – Mail Transfer Agent

Postfix

You should put the name of your server in this file…

sudo vi /etc/mailname

It could be something like smtp.domain.name, where domain.name is replaced with your own domain name.

Now open the main postfix configuration file:

sudo vi /etc/postfix/main.cf

Debian and Ubuntu by default include some sensible values in this file. You may need to comment some of them out if we duplicate with the commands below.

First specify the name of your server.

# This is already done in /etc/mailname
# myhostname= mail.example.com

NOTE:

After running my Postfix installation for a month or two I noticed in the message headers that the originating mail server was not what was defined in /etc/mailname but the local hostname – this was also confirmed by running postconf | grep myhostname. So… In my install I added the following entries in the main.cf file to accomodate.

myhostname = mail.example.com
mydomain = example.com

Next is the origin, which is the domain appended to each email sent from this machine. This can be your full servername, or just the DN.

# myorigin=/etc/mailname
myorigin=example.com

Next, decide what the greeting text will be when Postfix is queried. Provide enough info so it is useful, but not enough to divulge everything to potential hackers!

smtpd_banner = $myhostname ESMTP $mail_name

…or hardcode the server name to avoid a banner/hostname mismatch.

smtpd_banner = smtp.example.com ESMTP $mail_name

Next you need to decide whether to send all outgoing mail via another SMTP server, or send it yourself. I send via my ISP’s server so they have to worry about the queuing etc. If you send it yourself, then you are NOT reliant on a 3rd party’s server, BUT you may risk more exposure, being accidentally blocked by “spam blockers” and providing more work for your own server.

It’s also worth mentioning that many servers block dynamic DNS hosts (DynDNS etc), so you may find your server’s mail gets rejected. Weight up the pros and cons and choose a method that you are most comfortable with.

# leave blank to do it yourself.
relayhost =

# or put it an accessible smtp server.
relayhost = smtp.yourisp.com

Next are network details. We will define accepting connections from anywhere, and that we only trust this machine

inet_interfaces = all
mynetworks_style = host

Next you can masquerade some outgoing addresses. Say your machine’s name is mail.example.com. You may not want outgoing mail to come from username@mail.example.com, as you’d prefer the standard username@example.com. You can state which domain(s) not to masquerade (If you use a dynamic DNS service, then your server address will be a sub domain) and also specify which users not to masquerade.

# masquerade_domains = mail.example.com www.example.com !sub.dyndomain.com
# masquerade_exceptions = root

As this HOWTO describes using virtual domains, these need to be empty.

local_recipient_maps =
mydestination =

I’ve had problems with web based IMAP clients sending messages so I add the local IP subnet for the server (192.168.1.0/24) to the “mynetworks” variable.

mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 192.168.1.0/24

Now we set a few numbers.

# How long if undelivered before sending warning update to sender.
delay_warning_time = 4h
# Will it be a permanent or temporary error.
unknown_local_recipient_reject_code = 450
# How long to keep messages on queue before returning as failed.
maximal_queue_lifetime = 7d
# MAX and MIN time in seconds between retries if connection has failed.
minimal_backoff_time = 1000s
maximal_backoff_time = 8000s
# How long to wait when servers connect before receiving the rest of the data.
smtp_helo_timeout = 60s
# How many addresses can be used in one message - effective stopper to mass spammers or
# accidental copy in of whole address list but may restrict intentional mail shots.
smtpd_recipient_limit = 16
# How many errors before server backs off.
smtpd_soft_error_limit = 3
# How many MAX errors before blocking it.
smtpd_hard_error_limit = 12

Now we can specify some restrictions. Be careful that each of these settings is on one line only.

# Requirements for the HELO statement
smtpd_helo_restrictions = permit_mynetworks, warn_if_reject reject_non_fqdn_hostname, reject_invalid_hostname, permit
# Requirements for the sender details.
smtpd_sender_restrictions = permit_mynetworks, warn_if_reject reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unauth_pipelining, permit
# Requirements for the connecting server.
smtpd_client_restrictions = reject_rbl_client sbl.spamhaus.org, reject_rbl_client blackholes.easynet.nl, reject_rbl_client dnsbl.njabl.org
# Requirement for the recipient address.
smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, permit
smtpd_data_restrictions = reject_unauth_pipelining

Note: No point in including dnsbl.njabl.org (as detailed above), as this was shutdown in 2013…[see here]

Further restrictions that are used to mitigate malicious use.

# Require proper HELO at each connection.
smtpd_helo_required = yes
# Waste spammers time before rejecting them
smtpd_delay_reject = yes
disable_vrfy_command = yes

Next we need to set some maps and lookups for the virtual domains. These will reference files that will contain access information for the MySQL databases that contain the relevant data

# Not sure of the difference of the next two but they are needed for local aliasing
alias_maps = hash:/etc/postfix/aliases
alias_database = hash:/etc/postfix/aliases
# This specifies where the virtual mailbox folders will be located.
virtual_mailbox_base = /var/spool/mail/virtual
# This is for the mailbox location for each user.
virtual_mailbox_maps = mysql:/etc/postfix/mysql_mailbox.cf
# and this is for aliases
virtual_alias_maps = mysql:/etc/postfix/mysql_alias.cf
# and this is for domain lookups
virtual_mailbox_domains = mysql:/etc/postfix/mysql_domains.cf
# This is how to connect to the domains (all virtual, but the option is there)
# ** NOT USED YET **
# transport_maps = mysql:/etc/postfix/mysql_transport.cf

You can use a lookup for the UID and GID of the owner of the mail files but I tend to have one owner (virtual), so instead add this.

virtual_uid_maps = static:5000
virtual_gid_maps = static:5000

You need to set up an alias file. This is only used locally, and not by your own mail domains.

sudo cp /etc/aliases /etc/postfix/aliases

… you may want to view the file to check if OK, especially if the final alias e.g. root goes to a real person…

sudo postalias /etc/postfix/aliases

Next you need to set up the folder where the virtual mail will be stored. This may have already been done by the apt-get. Also, create the user who will own the folders.

Use this to add if there is not a virtual user

sudo mkdir /var/spool/mail/virtual
sudo groupadd --system virtual -g 5000
sudo useradd --system virtual -u 5000 -g 5000
sudo chown -R virtual:virtual /var/spool/mail/virtual

Postfix’s MySQL configuration

We now need to set up the files to access the lookups via the MySQL database. We will only set up the essential files for now, and the rest later if/when needed.

NOTE:

The mailPASSWORD that you choose and define in the files below, will be configured in the next step titled “Database“. It is not the rootPASSWORD you created when installing MySQL.

Create the file that tells Postfix how to find the users mailbox location.

sudo vi /etc/postfix/mysql_mailbox.cf
user=mail
password=mailPASSWORD
dbname=maildb
table=users
select_field=maildir
where_field=id
hosts=127.0.0.1
additional_conditions = and enabled = 1

Create the file that tells Postfix how to find the email alias.

sudo vi /etc/postfix/mysql_alias.cf

user=mail
password=mailPASSWORD
dbname=maildb
table=aliases
select_field=destination
where_field=mail
hosts=127.0.0.1
additional_conditions = and enabled = 1

Create the file that tells Postfix how to find the domains.

sudo vi /etc/postfix/mysql_domains.cf

user=mail
password=mailPASSWORD
dbname=maildb
table=domains
select_field=domain
where_field=domain
hosts=127.0.0.1
additional_conditions = and enabled = 1

If you specify an IP in hosts file (as opposed to ‘localhost’) then it will communicate over TCP and not the MySQL socket (a chroot restriction).

Database

MySQL

The next step now is to create the tables for those lookups we just defined. First you need to create a user to use in MySQL for mail ONLY. Then you need to create the database. Make sure you take note of your chosen mail username and password. You will need the password you specified for root during the MySQL package installation.

If you haven’t already done this in package installation…

mysqladmin -u root password newPassword

Log in as root

mysql -u root -p

Then enter password for the root account when prompted. Now we create the mail database…

CREATE DATABASE maildb;

Then we create a new user called “mail”

GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP ON maildb.* TO 'mail'@'localhost' IDENTIFIED BY 'mailPASSWORD';
GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP ON maildb.* TO 'mail'@'%' IDENTIFIED BY 'mailPASSWORD';
exit;

Obviously replace mailPASSWORD with your chosen password, and the one defined in the Postfix section above!

We’ve talked about them, defined them… now it’s time to actually create them. ‘Create what?’ you ask? The following tables of course:

  • aliases
  • domains
  • users

We will create more later on for further extensions, but for now only these are relevant.

Log into MySQL as the new mail user, connecting to the maildb database.

mysql -u mail -p maildb

Then run the following commands to create the specified tables. Quick side note here: It makes a difference if you use ` vs. so take note of which one is used in the commands below.

CREATE TABLE `aliases` ( `pkid` smallint(3) NOT NULL auto_increment, `mail` varchar(120) NOT NULL default '', `destination` varchar(120) NOT NULL default '', `enabled` tinyint(1) NOT NULL default '1', PRIMARY KEY (`pkid`), UNIQUE KEY `mail` (`mail`));

CREATE TABLE `domains` ( `pkid` smallint(6) NOT NULL auto_increment, `domain` varchar(120) NOT NULL default '', `transport` varchar(120) NOT NULL default 'virtual:', `enabled` tinyint(1) NOT NULL default '1', PRIMARY KEY (`pkid`));

CREATE TABLE `users` ( `id` varchar(128) NOT NULL default '', `name` varchar(128) NOT NULL default '', `uid` smallint(5) unsigned NOT NULL default '5000', `gid` smallint(5) unsigned NOT NULL default '5000', `home` varchar(255) NOT NULL default '/var/spool/mail/virtual', `maildir` varchar(255) NOT NULL default 'blah/', `enabled` tinyint(3) unsigned NOT NULL default '1', `change_password` tinyint(3) unsigned NOT NULL default '1', `clear` varchar(128) NOT NULL default 'ChangeMe', `crypt` varchar(128) NOT NULL default 'sdtrusfX0Jj66', `quota` varchar(255) NOT NULL default '', `procmailrc` varchar(128) NOT NULL default '', `spamassassinrc` varchar(128) NOT NULL default '', PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`));

The last few fields in the ‘users’ table are not required, but useful if you decide to extend functionality later on.

To visualise the tables created, you can use the following command(s) when logged into the MySQL database.

DESCRIBE `aliases`;
DESCRIBE `domains`;
DESCRIBE `users`;
exit;

Next you need to edit the MySQL my.cnf file. In Ubuntu/Debian this is created by default. In Mandrake I had to manually create a blank one in /etc.

sudo vi /etc/mysql/my.cnf

In previous versions you needed to comment out this line #skip-networking. However in the latest version of the file the default is to bind the address to localhost, which is fine.

bind-address = 127.0.0.1

It is very useful at the start to log any SQL calls that make it to MySQL. To do this enable these lines.

general_log_file = /var/log/mysql/mysql.log
general_log = 1

Then in a few weeks when your sure everything is working OK, comment it out as it slows MySQL down

Once the changes are complete, restart MySQL to make sure it picks up the new settings.

sudo /etc/init.d/mysql restart

POP/IMAP

Courier IMAP

You need to change Courier to use MySQL and tell it where to find the information in our databases.

sudo vi /etc/courier/authdaemonrc

Change to MySQL mode.

authmodulelist="authmysql"

Further down in the file enable logging.

WARNING:

This level of logging will show passwords in cleartextin the syslog!

DEBUG_LOGIN=2

Now change/update user, password, database, table names, crypt, mail directory and ‘where’ clause…

sudo vi /etc/courier/authmysqlrc

MYSQL_USERNAME mail
MYSQL_PASSWORD mailPASSWORD
MYSQL_DATABASE maildb
MYSQL_USER_TABLE users
MYSQL_CRYPT_PWFIELD crypt
# MYSQL_CLEAR_PWFIELD clear
MYSQL_MAILDIR_FIELD concat(home,'/',maildir)
MYSQL_WHERE_CLAUSE enabled=1

Lastly you can have a look at the imapd file, but no changes are needed so it’s recommended to leave it as is.

vi /etc/courier/imapd

Stage Two: Advanced mail server

Content check interface

Amavisd-new

Amavisd-new is a high-performance interface between mailer (MTA) and content checkers: virus scanners, and/or SpamAssassin. The default values are a good start and the Ubuntu documentation is pretty clear with its recommendations.

Here is a tweaked version of those recommendations. Initially we will not enable spam or virus detection! This is so we can get amavis set up to receive, check and pass on emails before we go on to over-complicate it.

All of amavis’ configuration files are in /etc/amavisd. They are now broken down into several configuration files in conf.d/.

cd /etc/amavis/conf.d

The 01-debian, 25-amavis_helpers and 30-template-localization defaults are fine and it’s worth while having a look at the following files, but don’t change anything in them.

less 05-domain_id
less 05-node_id
less 15-av_scanners
less 20-debian_defaults
less 21-ubuntu_defaults

Edit content check file.

sudo vi 15-content_filter_mode

Comment out both virus and spam scan lines. (They are commented out by default).

#@bypass_virus_checks_maps = (
#   \%bypass_virus_checks, \@bypass_virus_checks_acl,/$bypass_virus_checks_re);
#@bypass_spam_checks_maps = (
#   \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);

Edit the user file.

sudo vi 50-user

In the middle of the file insert the following:

@local_domains_acl = qw(.);
$log_level = 2;
$syslog_priority = 'debug';
# $sa_tag_level_deflt = 2.0; # add spam info headers if at, or above that level
# $sa_tag2_level_deflt = 6.31; # add 'spam detected' headers at that level
$sa_kill_level_deflt = 8.0; # triggers spam evasive actions
# $sa_dsn_cutoff_level = 10; # spam level beyond which a DSN is not sent
$final_spam_destiny = D_PASS;
# $final_spam_destiny = D_REJECT; # default
# $final_spam_destiny = D_BOUNCE; # debian default
# $final_spam_destiny = D_DISCARD; # ubuntu default, recommended as sender is usually faked

We have now setup amavis to scan and pass along incoming emails. Next we will configure Postfix to talk to Amavis.

sudo vi /etc/postfix/master.cf

Append these lines to the end of the file but make sure they are not already present. (Note the -o lines have spaces in front of them).

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

… then add the following two lines immediately below the “pickup” transport service:

        -o content_filter=
        -o receive_override_options=no_header_body_checks

…and then add the reference to the content_filter in main.cf

sudo vi /etc/postfix/main.cf

content_filter = amavis:[127.0.0.1]:10024

We now enable scanning of amavis’ temporary files by ClamAV.

sudo adduser clamav amavis

This should be it to get Amavis-new working. If emails are picked up by Amavis-new and passed back to Postfix then everything should be okay.

Open for business?

Only when you have finished the next section (SpamAssassin) and testing, do you proceed to uncomment the anti-virus and anti-spam lines in 15-content_filter_mode

sudo vi 15-content_filter_mode

@bypass_virus_checks_maps = (
    \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re);
@bypass_spam_checks_maps = (
    \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);

…and turn down the logging level, and start bouncing/discarding spam.

sudo vi /etc/amavis/conf.d/50-user

@local_domains_acl = qw(.);
$log_level = 1;
$syslog_priority = 'info';
# $sa_tag_level_deflt = 2.0; # add spam info headers if at, or above that level
# $sa_tag2_level_deflt = 6.31; # add 'spam detected' headers at that level
$sa_kill_level_deflt = 8.0; # triggers spam evasive actions
# $sa_dsn_cutoff_level = 10; # spam level beyond which a DSN is not sent
# $final_spam_destiny = D_PASS;
# $final_spam_destiny = D_REJECT; # default
# $final_spam_destiny = D_BOUNCE; # debian default
$final_spam_destiny = D_DISCARD; # ubuntu default, recommended as sender is usually faked

Anti-SPAM

SpamAssassin

The default configuration of SpamAssassin is fine, but you do need to tell SpamAssassin to start “smapd” on boot.

sudo vi /etc/default/spamassassin

ENABLED=1

One configuration option you could tweak is to enable “Bayes” and “Bayes auto-learning“.

sudo vi /etc/spamassassin/local.cf

# Use Bayesian classifier (default: 1)
#
use_bayes 1
# Bayesian classifier auto-learning (default: 1)
#
bayes_auto_learn 1

Postgrey

The default configuration of Postgrey is fine. However you do need to tell Postfix to use it.

sudo vi /etc/postfix/main.cf

…then edit the recipient restrictions setting:

smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, check_policy_service inet:127.0.0.1:10023, permit

You can then tweak whitelisting in /etc/postgrey if you wish. You can also tweak the Postgrey configuration by editing /etc/default/postgrey. For example you could change delay, auto whitelisting, or message rejection.

POSTGREY_OPTS="--inet=10023 --max-age=365"

Anti-VIRUS

ClamAV

ClamAV does not need any setting up. The configuration files are located in /etc/clamav, but they are automatically generated, so do not edit them. By default freshclam, the daemon that updates the virus definition database, is run 24 times a day(!!). That seems a little excessive, so I tend to set that to once a day.

sudo dpkg-reconfigure clamav-freshclam

Running this “reconfigure” will also prompt you whether you want it to be daemon (answer: Yes) and which server is closest to you. If needed, the command below will redefine the entire configuration with a lot of questions/prompting. It’s not needed unless you really need to configure ClamAV to the nth degree.

sudo dpkg-reconfigure clamav-base

Stage Three: Secure your mail server

SSL Certificates… or “How I lost my mind for a week”

Don’t cry little baby…

OK, I confess I had a hell of time getting my head around the certificates for the traffic encryption. I think it was compounded by the fact that I didn’t think I was doing anything out of the ordinary, but I couldn’t seem to find a solid set of steps to follow, and instead had to amalgamate a number of different procedures from different sources to get it working (at least I think it’s working?!). It also doesn’t help when some clients are more forgiving than others about how they handle incorrect certificates.

In the end it was Microsoft’s obtuse Outlook 2007 that helped me the most, refusing to work until I’d got the certificate details sorted. Well, to help save you some pain, hopefully the following steps will assist you (…and your sanity!)

Down to business

Ubuntu will have created it’s own self-signed certificate in /etc/ssl/certs with the key in /etc/ssl/private called ssl-cert-snakeoil.pem and ssl-cert-snakeoil.key respectively. I think these are fine if you’re only going to use the server locally, but once it’s opened up to the internet, clients will complain about the certificate validity (at least mine did!)

The two processes that will use the certificates – in context of this HOWTO – are Postfix and Courier. The instructions are based to some degree on the Ubuntu documentation here. I created a directory called certs in my admin user’s home directory so I could keep everything together.

First we create a file with the encrypted private server key in it and this will form the basis of certificate generation. I’ve used 2048 as the key length based on security recommendations at the time of writing this (Sept 2010). This command will prompt you for a server passphrase – the longer the phrase the better, but the minimum for DES3 is four (4).

cd ~/certs
sudo openssl genrsa -des3 -out server.key 2048

Now create the insecure key, the one without a passphrase, and shuffle the key names.

sudo openssl rsa -in server.key -out server.key.insecure
sudo mv server.key server.key.secure
sudo mv server.key.insecure server.key

The insecure key is now named server.key, and you can use this file to generate the CSR without a passphrase stored in it. To create the CSR, run the following command. You can combine steps within the one command to create the certificate without using a CSR, but if you decide to get an SSL certificate from a CA, having the CSR will come in handy.

sudo openssl req -new -key server.key -out server.csr

This will now prompt you for some certificate information like country, location, company name etc.

IMPORTANT:

Make sure that you use the server’s FQDN when prompted for the common name (CN). In most of the examples we’ve referred to it as smtp.example.com (or similar) and will continue to use that in the following steps.

You can now submit this CSR file to a CA (Verisign, Comodo etc…) for processing. The CA will use this CSR file and issue the certificate but we’re going to use it to create a self-signed certificate. It’s up to you how many days you create make the certificate valid for, the default is 365 days.

sudo openssl x509 -sha256 -req -days 999 -in server.csr -signkey server.key -out server.crt

Fantastic… we have a certificate and a key, but PEM format certificates (a certificate that may include just the public certificate, or may include an entire certificate chain including public key, private key, and root certificates. The name is from Privacy Enhanced Email, a failed method for secure email but the container format it used lives on.) often include the key and the certificate in one file. Combining them is simple…

sudo cat server.key server.crt >> server.pem

Next, we copy some of these files to the relevant locations where they will be used.

sudo cp server.crt /etc/ssl/certs/smtp.example.com.pem
sudo cp server.key /etc/ssl/private/smtp.example.com.key
sudo cp server.pem /etc/courier/imapd.pem

That’s pretty much it for the certificates. The next step is to secure your mail server using the certificate we’ve just created.

Some helpful commands

To view the contents of a PEM (Privacy Enhanced Mail) certificate

sudo openssl x509 -in certname.pem -noout -text

Create a public key, using your private key (using the files created above…)

sudo openssl rsa -in server.pem -pubout -out server.public

Create a certificate file for import in Windows certificate manager (important if the certificate is self-signed).

sudo openssl x509 -in certname.pem -out certname.crt

Create a self-signed certificate with a “auto-generated” key and write it out to a PEM format file.

sudo openssl req -x509 -newkey rsa:2048 -keyout certname.pem -out certname.pem -nodes -days 999

Configuring authentication

SASL

SASL secures the actual authentication (login), by encoding the passwords so that it can not be easily intercepted. The rest of the emails are however in clear plain text.

SASL can be a royal pain to set up, especially as it does not support storing encrypted passwords by default in Ubuntu.

Obviously this is not ideal, so there are ways to combine SASL and storing encrypted passwords. In the future the packages that come with Ubuntu may support the password_format configuration option for SASL, but until then you can configure SASL to ask PAM to compare the passwords.

Install packages if not all installed already…

sudo aptitude install sasl2-bin libpam-mysql libsasl2-modules libsasl2-modules-sql

Enable postfix to access SASL files.

sudo adduser postfix sasl

Create SASL directories which will be chrooted Postfix.

sudo mkdir -p /var/spool/postfix/var/run/saslauthd

Add SASL configurations to Postfix.

sudo vi /etc/postfix/main.cf

# SASL
smtpd_sasl_auth_enable = yes
# If your clients use Outlook Express or other older clients then
# broken_sasl_auth_clients needs to be set to yes
broken_sasl_auth_clients = no
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain =

Modify these existing configurations

# Add permit_sasl_authenticated to you existing smtpd_sender_restrictions
smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks,warn_if_reject reject_non_fqdn_sender, reject_unknown_sender_domain,reject_unauth_pipelining, permit
# Add permit_sasl_authenticated to you existing smtpd_recipient_restrictions
smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks,permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain,reject_unauth_destination, check_policy_service inet:127.0.0.1:10023, permit

Change how saslauthd is run.

sudo vi /etc/default/saslauthd

START=yes

OPTIONS="-r -c -m /var/spool/postfix/var/run/saslauthd"

Now tell Postfix how to interact with SASL.

sudo vi /etc/postfix/sasl/smtpd.conf

pwcheck_method: saslauthd
mech_list: plain login cram-md5 digest-md5
log_level: 7
allow_plaintext: true
auxprop_plugin: mysql
sql_engine: mysql
sql_hostnames: 127.0.0.1
sql_user: mail
sql_passw: mailPASSWORD
sql_database: maildb
sql_select: select crypt from users where id='%u@%r' and enabled = 1

Note: When SASL is working, you can remove the log_level line.

Using Ubuntu 12.04.1 or greater?

I had a few problems sending emails after I upgraded my 10.04 system to 12.04 and finally traced the problems to the smtpd.conf file. It would seem that the definition names had changed slightly from the original documentation, or the Ubuntu build was more sensitive to proper naming convention.

Either way, I was unable to send emails until I made the changes described below:

pwcheck_method: saslauthd
mech_list: plain login cram-md5 digest-md5
log_level: 7
allow_plaintext: true
auxprop_plugin: sql
sql_engine: mysql
sql_hostnames: 127.0.0.1
sql_user: mail
sql_passwd: mailPASSWORD
sql_database: maildb
sql_select: select crypt from users where id='%u@%r' and enabled = 1

You can find more details on the configuration of the smtpd.conf file here.

Tell pam how to to authenticate smtp via mysql.

sudo vi /etc/pam.d/smtp

Note: These must be on 2 lines only.

auth required pam_mysql.so user=mail passwd=mailPASSWORD host=127.0.0.1 db=maildb table=users usercolumn=id passwdcolumn=crypt crypt=1
account sufficient pam_mysql.so user=mail passwd=mailPASSWORD host=127.0.0.1 db=maildb table=users usercolumn=id passwdcolumn=crypt crypt=1

In addition to tailing var/log/mail.log and /var/log/mysql/mysql.log it is quite useful to tail the auth.log as well when you’re testing SASL.

sudo tail -f /var/log/auth.log

Restart Postfix and saslauthd in order to enable SASL for sending emails.

sudo /etc/init.d/saslauthd restart
sudo /etc/init.d/postfix restart

Update 8th February 2011

Interesting point I uncovered while checking into saslauthd. Accouring to the Postfix SASL documentation, using “saslauthd” in the “pwcheck_method” (see above) means you cannot use cram-md5 or digest-md5!

To quote:

Important

Do not specify any other mechanisms in mech_list than PLAIN or LOGIN when using saslauthd! It can only handle these two mechanisms, and authentication will fail if clients are allowed to choose other mechanisms.

Then a bit further down it says:

Important

These three plugins [sasldb,sql,ldapdb] support shared-secret mechanisms i.e. CRAM-MD5, DIGEST-MD5 and NTLM. These mechanisms send credentials encrypted but their verification process requires the password to be available in plaintext. Consequently passwords cannot (!) be stored in encrypted form.

It seems fairly conclusive to me that I was never going to get this to work and have failed to do so tinkering with a multitude of settings and configurations (cleartext passwords, MD5 encrypted passwords, encrypted passwords etc..) to date. Now I’ve decided to omit the step below, move on to the section “Configuring encryption” and have altered /etc/postfix/sasl/smtpd.conf so that…

mech_list: plain login cram-md5 digest-md5

now looks like…

mech_list: plain login

The section from Flurdy’s guide is included for consistency and completeness.

IMAP SASL / Courier

I tend not to have SASL for my courier authentication, as I enforce TLS for all my clients. However, if you have a more lenient access policy (which is wise if you have many users), then you may want SASL in Courier as well.

sudo vi /etc/courier/imapd

This is available as a commented out line. Uncomment it or replace the current line by adding AUTH=CRAM-MD5 AUTH=CRAM-SHA1 so it resembles something like this on one line.

IMAP_CAPABILITY="IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA AUTH=CRAM-MD5 AUTH=CRAM-SHA1 IDLE"sudo /etc/init.d/courier-authdaemon restart;

sudo /etc/init.d/courier-imap restart

Configuring encryption

Notes on TLS

Encrypting the traffic stops anyone else from listening in on your email communications, which is very desirable. There are different types of communication that need to be encrypted:

  1. The data traffic between your email applications and the server when you read (or send) emails.
  2. Communication between other email servers and your server.

For the encryption of reading emails, it is Courier you need to configure. For sending and the “between server” communication encryption, it’s Postfix we change.

TLS in Postfix

To encrypt you need a certificate. As I’ve mentioned in the section above, Ubuntu creates some for you for which you can use while setting up the server. However before you go live, it is recommended to create your own with your proper domain name. In the examples below we will refer to the fictitious certificates created above.

sudo vi /etc/postfix/main.cf

There are already some TLS settings in the default Debian/Ubuntu version of this file. I amalgamated them and moved them to the beginning for clarity, but that’s up to you.

# TLS parameters
#smtp_use_tls = no
smtp_tls_security_level = may
#smtpd_use_tls=yes
smtpd_tls_security_level = may
#smtpd_tls_auth_only = no
smtp_tls_note_starttls_offer = yes
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
# The original references to the system cert and key.
# smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
# smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_tls_cert_file=/etc/ssl/certs/smtp.example.com.pem
smtpd_tls_key_file=/etc/ssl/private/smtp.example.com.key
#smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
#smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

Next we edit the master.cf file.

sudo vi /etc/postfix/master.cf

By default, only the normal smtp service is enabled, which is fine. It’s preferable to enable submission (port 587), so that clients can use it, and I can restrict them to TLS only. Also enabled is the smtps service (port 465), for some compatibility with some older clients (Outlook Express etc…)

submission inet n       -       n       -       -       smtpd
  -o smtpd_sasl_auth_enable=yes
# if you do not want to restrict it encryption only, comment out next line
  -o smtpd_tls_auth_only=yes
# -o smtpd_tls_security_level=encrypt
# -o header_checks=
# -o body_checks=
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject_unauth_destination,reject
  -o smtpd_sasl_security_options=noanonymous,noplaintext
  -o smtpd_sasl_tls_security_options=noanonymous
# -o milter_macro_daemon_name=ORIGINATING
smtps     inet  n       -       -       -       -       smtpd
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes 
  -o smtpd_tls_auth_only=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o smtpd_sasl_security_options=noanonymous,noplaintext
  -o smtpd_sasl_tls_security_options=noanonymous
# -o milter_macro_daemon_name=ORIGINATING

TLS in Courier

As mentioned previously, Ubuntu has created a certificate for you, but if you want to create your own, especially for a properly named server, then follow the steps in the section SSL Certificates above.

sudo vi /etc/courier/imapd-ssl

By default Ubuntu already points to your certificate file…

TLS_CERTFILE=/etc/courier/imapd.pem

…modify this if needed.

Also you if want to restrict IMAP users to only using SSL/TLS, toggle the following setting to “1”.

IMAP_TLS_REQUIRED=1

To maintain maximum compatibility it is not wise or recommended to restrict the traffic between servers to TLS only, as this means some of the valid emails sent by others may not reach your server.

Be aware that the emails are not encrypted on your machine, nor on the server.

There are some situations where SASL and TLS do not play well together. Those situations are normally when you combine the storing of encrypted passwords and using MD5 authentication over encrypted traffic. I (read:flurdy) recommend insisting on TLS traffic with your authenticating clients, which then negates the need for SASL.

Stage Four: Roundcube webmail client

Installing Roundcube

As an alternative to SquirrelMail, Roundcube has a more modern feel to it. It doesn’t have the proven track record of SquirrelMail and is still not quite a version “1.0” release (0.4 0.5 0.6 0.7 0.9.5 was the latest release at the time this was written).

However if you prefer a more appealing interface (and design is everything for me!) then follow these easy steps to get it up and running.

If you’ve already installed it and want to remove SquirrelMail, then

sudo aptitude remove squirrelmail squirrelmail-locales

Then install Roundcube (if you didn’t do so already in the “Packages” section).

sudo aptitude install roundcube roundcube-mysql

During the install it will ask you if you want to configure database access automatically. Answer Yes, then select MySQL as the database. You will then be prompted for the root MySQL password. This script will create the relevant database. tables and a Roundcube MySQL user. You will be prompted for a password for this new user.

The install will create a symblink in /etc/apache2/conf.d/ to /etc/roundcube/apache.conf. Edit this file…

sudo vi /etc/roundcube/apache.conf

Depending on your setup you may want to move those Alias commands to the top to your virtual hosts configuration, or for this example enable them here for all hosts.

Alias /roundcube/program/js/tiny_mce/ /usr/share/tinymce/www/
Alias /roundcube /var/lib/roundcube

Roundcube uses PEAR, which is a framework and distribution system for reusable PHP components. Assuming your system is up-to-date, it might pay to make sure PEAR is also.

sudo pear channel-update pear.php.net

…and make sure the MDB2 module is up to date.

sudo pear install mdb2

Now edit the Roundcube configuration file.

sudo vi /etc/roundcube/main.inc.php

Modify the following lines for added security/support of SASL/TLS (see informational note below)…

$rcmail_config['default_host'] = 'ssl://localhost:993';
$rcmail_config['smtp_server'] = 'ssl://localhost';
$rcmail_config['smtp_port'] = 465;

NOTE:

I deviated from “Flurdy’s” guide at this point as I had problems logging in until tweaked the following (based on the debug of messages in /var/log/syslog)

$rcmail_config['smtp_user'] = '%u';
$rcmail_config['smtp_pass'] = '%p';

Update the following to enable some default features. Note: for “smtp_helo_host” you can keep as the default value or change to your mail server name.

$rcmail_config['smtp_helo_host'] = 'mail.example.com';
$rcmail_config['language'] = 'en_GB';
$rcmail_config['product_name'] = '[your organisation name] RoundCube Webmail';
$rcmail_config['default_imap_folders'] = array('INBOX', 'Drafts', 'Read', 'Sent', 'Junk', 'Trash');
$rcmail_config['create_default_folders'] = TRUE;
$rcmail_config['preview_pane'] = TRUE;
$rcmail_config['logout_purge'] = TRUE;
$rcmail_config['logout_expunge'] = TRUE;

There are other tweaks and security features you can enable such as…

$rcmail_config['sendmail_delay'] = 1;

NOTE:

If you’re configuring Roundcube prior to having setup SASL/TLS (detailed under the “Secure” section above) then you’ll need to use the following settings until you do:

$rcmail_config['default_host'] = 'localhost';
$rcmail_config['smtp_server'] = 'localhost';
$rcmail_config['smtp_port'] = 25;
$rcmail_config['smtp_user'] = '';
$rcmail_config['smtp_pass'] = '';

Lastly we’ll add support for the default time of the server…

sudo vi /usr/share/roundcube/.htaccess

…by adding this parameter. Note: Of course you substitude “Pacific/Auckland” with your own time zone.

php_value date.timezone "Pacific/Auckland"

You can find a list of valid timezones here at the PHP website.

A lot of these tweaks are personal preference, but as an advisory it might pay to get the basics working first before playing with the settings too much! When you’re finished save and restart the apache process to enable changes.

sudo /etc/init.d/apache2 reload

Go to any web browser and point it to your Roundcube installation. Normally it’s http://mail.example.com/roundcube by default.

You can obviously modify and tweak further. One thing that may be useful is to have the Roundcube Apache Alias on different virtual hosts and configure username_domain in main.inc.php to append different email addresses, or configure the default_host to a different mail server depending on the virtual host…

More details on the Roundcube wiki.

Configuring HTTPS (If you want it?)

The mod_ssl module adds an important feature to the Apache2 server – the ability to encrypt communications. So, when your browser is communicating using SSL, the https:// prefix is used at the beginning of the Uniform Resource Locator (URL) in the browser navigation bar.

The mod_ssl module is available in the apache2-common package. Execute the following command from a terminal prompt to enable the mod_ssl module.

sudo a2enmod ssl

The default HTTPS configuration file is /etc/apache2/sites-available/default-ssl. In order for Apache2 to provide HTTPS, a certificate and key file are also needed. The default HTTPS configuration will use a certificate and key generated by the ssl-cert package.

They are good for testing, but the auto-generated certificate and key should be replaced by a certificate specific to the site or server. For information on generating a key and obtaining a certificate see the section Secure » SSL Certificates of this HOWTO or refer to Ubuntu’s documentation in the section called “Certificates”

To configure Apache2 for HTTPS, enter the following command.

sudo a2ensite default-ssl
sudo vi /etc/apache2/sites-available/default-ssl

SSLCertificateFile /etc/ssl/certs/<your certificate file name>.pem
SSLCertificateKeyFile /etc/ssl/private/<your key file name>.key

NOTE:

The directories /etc/ssl/certs and /etc/ssl/private are the default locations. If you install the certificate and key in another directory make sure to change SSLCertificateFile and SSLCertificateKeyFile appropriately.

Then restart Apache…

sudo /etc/init.d/apache2 restart

You can access the secure server pages by typing https://your_hostname/url/ in your browser’s address bar.

Appendix One: Adding data to the server

Add users and domains

Alright, so you finally got your mail server installed and you want to test it… BUT there’s no users or domains! Nada!! OK, to kick things off you need to add some default data, some of which is required and most which should make sense.

After that, we need to start adding your users and associated mail domains. Then you should be ready to start testing, or if you’ve done the testing… roll the server into production!

Base domain and user

First the required domain for local mail.

mysql -u mail -p maildb

Insert the domain information:

INSERT INTO domains (domain) VALUES ('localhost'), ('localhost.localdomain');

Then the default aliases. Some people say these are not needed, but I recommend including them.

INSERT INTO aliases (mail,destination) VALUES ('postmaster@localhost','root@localhost'), ('sysadmin@localhost','root@localhost'), ('webmaster@localhost','root@localhost'), ('abuse@localhost','root@localhost'), ('root@localhost','root@localhost'), ('@localhost','root@localhost'), ('@localhost.localdomain','@localhost');

It’s worth noting at this point, that this “set” of aliases is common across each domain i.e. postmaster,abuse and if you like sysadmin or admin.

Now we create a “root” user.

INSERT INTO users (id,name,maildir,crypt) VALUES ('root@localhost','root','root/', encrypt('apassword') );

Domains and users

It’s at this point that we add some proper data. In this example we’ll say you want this machine to handle data for the fictional domains of “blobber.org” and “whopper.nu“.

We will refer to this machine’s name as “mail.blobber.org“.

INSERT INTO domains (domain) VALUES ('blobber.org'), ('mail.blobber.org'), ('whopper.nu');
INSERT INTO aliases (mail,destination) VALUES ('@mail.blobber.org','@blobber.org'), ('postmaster@whopper.nu','postmaster@localhost'), ('abuse@whopper.nu','abuse@localhost'), ('postmaster@blobber.org','postmaster@localhost'), ('abuse@blobber.org','abuse@localhost');

Having created the domains and aliases we now add names for two users called “Fred” and “Wilma“.

INSERT INTO users (id,name,maildir,crypt) VALUES ('fred@blobber.org','Fred Flintsone','fredflinstone/', encrypt('apassword') ), ('wilma@blobber.org','Wilma Fintstone','wilmaflinstone/', encrypt('anotherpassword') );
INSERT INTO aliases (mail,destination) VALUES ('fred@blobber.org','fred@blobber.org'), ('wilma@blobber.org','wilma@blobber.org');
Adding tweaks to the basic users

If you want all the mail for whooper.nu to go to Fred (i.e. *@whooper.nu).

INSERT INTO aliases (mail,destination) VALUES ('@whopper.nu','fred@blobber.org');

Say there is a user called “Pebbles“, but she wants all her mail forwarded to an external account.

INSERT INTO aliases (mail,destination) VALUES ('pebbles@blobber.org','pebbles.flintstone@gmail.com');

So what do each of these lines above actually do? Well the domain entries are pretty straight forward. So too are the lines relating to the users, however it’s worth noting here that each entry requires the following four fields to be included.

  • id is the email address of the user, and also its username when logging in. This is described later on.
  • name is optional description or “full name” of the user.
  • maildir is the name of the folder inside /var/spool/mail/virtual. It MUST end in a /, otherwise it won’t be used as the unix maildir format.
  • crypt is the encrypted text password to use.

The aliases are the interesting part. Lets track an incoming message to see how it’s delivered.

Say an email arrives addressed to “john@whopper.nu“:

  1. Postfix looks up the domains table and locates whopper.nu as a domain it listens to.
  2. Postfix then looks up aliases and searches for a row where the mail field matches “john@whopper.nu“.
  3. There is no match, so it searches for “@whopper.nu“, which is the “catchall” for that domain.
  4. Postfix then finds one row and its destination is “fred@blobber.org“.
  5. It then searches for “fred@blobber.org” and finds an entry, which has a destination that is the same as the mail and therefore it is the final destination.
  6. It then tries to deliver this mail. The look up says blobber.org is a local mail domain so it looks up users for a matching id and delivers it to its maildir.

Any mail arriving for “pebbles@blobber.org” gets forwarded to the external address of “pebbles.flintstone@gmail.com“. So forwarding is as simple as adding a line into the aliases table.

I also added the required aliases of postmaster and abuse to blobber.org and whopper.nu. Another useful alias to add is root, as often you get admin mail from cron jobs etc within those domains. Other often used aliases are info, sysadmin, support, sales, webmaster, mail, contact and all. The problem with creating all of them is that they are also honeypots for spam, so just include the ones you think you will need.

Adding domains, users and aliases using a template

To add a new domain to the system you use this command in MySQL, replacing the values in italics with data relevant to you:

INSERT INTO domains (domain) VALUES ('domain.tld');
INSERT INTO aliases (mail,destination) VALUES ('@domain.tld','email@address'), ('postmaster@domain.tld','email@address'), ('abuse@domain.tld','email@address');

To add a new user to the system:

INSERT INTO users (id,name,maildir,crypt) VALUES ('email@address','short description','foldername/',encrypt('password'));
INSERT INTO aliases (mail,destination) VALUES ('email@address','email@address');

Common SQL statements

A selection of useful SQL statements to use if you are not using an admin/manager program to maintain your email domains and users.

Find domains without a “catchall”

Remember some might be disabled!!

SELECT dom.domain FROM domains dom LEFT JOIN aliases al ON CONCAT( '@', dom.domain ) = al.mail WHERE al.mail is null OR al.enabled = 0 ORDER BY dom.domain ASC;
Find aliases for an invalid domain
SELECT al.* FROM aliases al LEFT JOIN domains dom ON dom.domain = SUBSTRING(al.mail,LOCATE('@',al.mail)+1) WHERE dom.domain is null OR dom.enabled = 0 ORDER BY al.mail ASC;
Find all non local destination aliases
SELECT al.* FROM aliases al LEFT JOIN domains dom ON dom.domain = SUBSTRING(al.destination,LOCATE('@',al.destination)+1) WHERE dom.domain is null ORDER BY al.enabled, al.destination ASC, al.mail ASC;
Find all aliases for a certain domain
SELECT al.* FROM aliases al WHERE SUBSTRING(al.mail,LOCATE('@',al.mail)+1) = 'domain.tld' ORDER BY al.enabled, al.mail ASC;
Find all aliases for a certain domain, checking if enabled for both domain and alias
SELECT * FROM domains d JOIN aliases a ON a.mail like concat( '%','@',d.domain) AND a.enabled = 1 WHERE d.enabled = 1 AND d.domain like '%foobar%' ORDER BY d.domain,a.mail;

Appendix Two: Testing the server

Common problems

  • Missed a step?

    If you mistakenly or intentionally skipped past sections, you may have missed an important step in your configuration, which this guide presumes you have followed.

  • Typo!

    99% of all problems is spelling errors or typos you entered while following this guide. Sorry, but it just happens. Often it can be trivial, such as a space at the end of the configuration line which was not expected etc. Or not understanding my example where it is a multi line entry.

  • Typo in he documentation.

    Yes, I (read: flurdy) can make mistakes. Nothing wrong in that, but I hope I have corrected most over time after each proof read. However, any new sections to this guide are potentially at risk… ☺

  • Different application or configuration.

    It is obviously entirely up to you how you set up your system. But the more you deviate from this guide, the more likely incompatibilities or confusion will arise.

  • Distribution/version differences.

    If you run a different version or even distribution to this guide, then some things will be different. Small issues, such as default values and significant things such as path differences etc. Some sections in this guide are not always thoroughly tested with every new release of Ubuntu, but these differences get pointed out by people for me.

  • Walking before crawling.

    Don’t try the full blown mail server before the basics are working.

  • Gamma rays and little goblins!

    Because we’ve got to blame it on something… right? 😉

Test strategy

Test early and frequently

It is very helpful to test early in this set-up to establish if the first sections are working as expected. So when you only have your very basic Postfix and MySQL up and running – TEST IT! That way you know that certain step worked and you can rule it out of any future problems. Don’t wait until you complicated the install by adding additional features like AMAVIS, SASL, Postgrey etc…

By constantly testing if you can send and receive you can tick off and black box each section as working, and immediately spot issues.

Isolate the problem!

Testing how things work is often about isolating the problem first. So by using the steps of testing early above, you can see which step caused the problem. Also if you can’t log into your webmail it is often nothing to do with the webmail section that is causing the problem. Often postfix itself is broken…

Test in order

As part of the isolating the problem step it’s a good idea to test steps in order, thus isolating the problem. This would then quickly indicate where the problem might lie. e.g. In the example relating to webmail access, the order of testing might go:

  1. Access: Can I get (SSH) access to the box, and is there a firewall issue?
  2. Database: Is the database up, can my applications talk to it?
  3. Postfix: Can I send an email by command line (telnet)? Can I receive emails via telnet?
  4. Content checks: Is the content of my message causing a problem?
  5. Courier: Can I read emails?
  6. Webmail: Does the web integration work?

Simplify the system

Assisting in isolating the problem, you often have to disable options and applications. An example might be to turn off Postgrey or the content checks to make sure emails to get delivered.

Get out the toolbox… MXToolbox that is

One website I found invaluable for testing my server from the “outside” was MXToolbox. You can use it to lookup/check your MX records, run SMTP tests to the server, check for open ports… and (hopefully never) check if your server has been blacklisted!

It’s probably really widely known by those in the “mail” support field, but I stumbled across it trying to troubleshot why a colleague’s emails were getting bounced when he tried to send them to us. The support guy on the phone told me to use MXToolbox to check if the server had been blacklisted and follow it up from there as there was nothing they could do. Lo-and-behold, his server was blacklisted on one of the many “blacklist reference servers” used by our ISP (and others). I can’t stop using it now, it’s a great site…

Tail, tail and tail again

It’s essential to monitor what actually happens, and tailing (specifically the mail and mysql log) is key in tracking down problems. The following are most commonly referred to when troubleshooting:

  • /var/log/syslog
  • /var/log/mail.log
  • /var/log/mysql.log
  • /var/log/apache2/access.log

You could have multiple windows open “tailing” the log files and changing the configuration in a third.

tail -f /var/log/mail.log
tail -f /var/log/mysql.log

Switch debugging on

Shorewall

You can also switch on more messages for when the firewall is rejecting connections. Add info to all REJECT, BOUNCE and DROP policies.

sudo vi /etc/shorewall/policy

Net        all        DROP       info

MySQL

There is no point in tailing the mysql log if query debugging is not turned one. By default it is not. However in this guide I do switch it on. In case that was missed switch it on now:

sudo vi /etc/mysql/my.cnf

and make sure this line is not commented out.

log = /var/log/mysql/mysql.log

Courier

As mentioned in the setup , switching on debugging for Courier is easy:

sudo vi /etc/courier/authdaemonrc
DEBUG_LOGIN=2

WARNING:

With this setting passwords logged in syslog will be in clear text!

AMAVIS

You can also debug AMAVIS:

sudo vi /etc/amavis/conf.d/50-user

And perhaps bump up the log level if you are already debugging:

$log_level = 2;

Telnet is your friend!

When testing a mail server, telnet is alpha & omega. You use it to simulate another mail server in order to test responses by your mail server.

  1. First you test it on the server to rule out firewall and network issues.
  2. Then you test it from another machine to simulate another mail server trying to communicate.
  3. Once these are working you can use a proper email client, however in 99% of cases I just use MUTT locally when I need to test if a server is working.

Can Postfix receive?

Lets assume:

  • You have followed the guide up to “Basic” configuration at the very least.
  • You have entered data into the database (see previous section).
  • The services MySQL and Postfix are running.
  • If testing a fuller stack, then amavis, postgrey, clamav-daemon, spamassassin etc must also be running.

Try this locally on the server first, then try from another machine once you confirm it works locally.

Lets try and send a message to fred@example.org (replace with your own user in this setup, or use postmaster@localhost) from you@example.com (again, replace with a real email address that is not associated with this server).

telnet localhost 25
 # Open and hand shake with EHLO and the server name you are connecting from. Change mail.example.com to something valid. e.g. your servername
 EHLO mail.example.com
 # The mail server will then dump out some details about its capabilities, e.g.
 > 250-mail.flurdy.net
 > 250-PIPELINING
 > ...
 > ...
 # then say who is the sender of this email
 MAIL FROM: your@example.com
 > 250 Ok
 # then say who the mail is for...
 RCPT TO: fred@example.org
 > 250 Ok
 # then enter the keyword data
 data
 > 354 End data with <CR><LF>.<CR><LF>
 # enter message body and end with a line with only a full stop then enter.
 blah blah blah
 more blah
 .
 > 250 Ok; queued as QWKJDKASAS
 # end the connection with quit
 quit
 > 221 BYE

If (while you were doing this) you were tailing /var/log/mail.log you would (read: should) have seen some activity and whether any errors occurred. (You might get some complaints about missing headers as we skipped most of them…)

If (while you were doing this) you were tailing /var/log/mysql.log as well, you would definitely have seen some activity – if not you have a problem!

If you see any errors (or worse no activity) in these log files, then this highlights what you need to fix! However, if no errors popped up, and the folder /var/mail/virtual/fred (or similar) now exists then your server can receive emails!

Can Postfix send?

You need to first make sure that receive emails (as detailed above) and the services MySQL and Postfix are running.

Try this locally on the server first, then try from another machine once you confirm it works locally.

telnet localhost 25
 # Open and hand shake with EHLO and the server name you are connecting from. Change mail.example.com to something valid. e.g. your servername
 EHLO mail.example.com
 # The mail server will then dump out some details about its capabilities, e.g.
 > 250-mail.flurdy.net
 > 250-PIPELINING
 > ...
 > ...
 # then say who is the sender of this email
 MAIL FROM: fred@example.org
 > 250 Ok
 # then say who the mail is for...
 RCPT TO: your@example.com
 > 250 Ok
 # then enter the keyword data
 data
 > 354 End data with <CR><LF>.<CR><LF>
 # enter message body and end with a line with only a full stop then enter.
 blah blah blah
 more blah
 .
 > 250 Ok; queued as QWKJDKASAS
 # end the connection with quit
 quit
 > 221 BYE

We have to assume (I know what they say about assuming) that receiving works so there is no ned to tail the MySQL log. However, if any rejection messages occurred in the mail.log then you need to troubleshoot it to resolution.

If no errors occurred and you see something in the mail.log that looks like this:

Dec 17 10:25:45 servername postfix/smtp[12345]: 12345678: to=<you@example.com>, relay=127.0.0.1[127.0.0.1]:10024, delay=15, delays=15/0.01/0.02/0.11, dsn=2.0.0, status=sent (250 2.0.0 Ok, id=12345-09, from MTA([127.0.0.1]:10025): 250 2.0.0 Ok: queued as 1234567)

then the sending of emails is working perfectly!

Can Courier read emails?

  • You need to first make sure that you can receive emails (as detailed above).
  • You need to make sure that you can send emails (as detailed above).
  • You need to make sure you have received an email by checking that the folder /var/mail/virtual/username exists.
  • The MySQL, courier-authdaemon and courier-imap services are all running.

There is not too much you can test via telnet for courier. But you can check if it is up and that you can connect to it.

telnet 127.0.0.1 143
 Trying 127.0.0.1...
 Connected to 127.0.0.1. Escape character is '^]'.
 * OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION STARTTLS LOGINDISABLED] Courier-IMAP ready. Copyright 1998-2008 Double Precision, Inc. See COPYING for distribution information

For more in depth testing, you would have to use a proper IMAP email client.

Can AMAVIS check and pass on emails?

  • You need to first make sure that you can receive emails (as detailed above).
  • You need to make sure that you can send emails (as detailed above).
  • You need to make sure you have received an email by checking that the folder /var/mail/virtual/username exists.

All you can do here is check to make sure that the service is responding.

telnet 127.0.0.1 10024
 Trying 127.0.0.1...
 Connected to 127.0.0.1.
 Escape character is '^]'.
 220 [127.0.0.1] ESMTP amavisd-new service ready

then tail /var/log/mail.log and see if there are any problems.