Slicehost – Ubuntu 10.10 Mail Server from Scratch

Its been a while since I posted, work keeps on interfering, but since I last posted I was forced to move to a new virtual hosting company. VPSLink just did not cut it any more so I moved to Slicehost and am very happy with them thus far. So since I was moving I decided that it was time to update my Mail Server from Scratch recipe.

You know the song by now, I am not an expert, but I found that the following steps worked for me… etc … etc

This post is a simple recipe for getting the job done, it is most likely not the best way of doing it but it works. The problem with this type of recipe approach is that the “why for’s” and the “where for’s” are minimal or non existent. Any comments and/or corrections are more than welcome.

What this exercise leaves us with in the end of the day is a mail server that can handle multiple domains and email addresses. The domain and user data is stored in an postgresql database while the actual mails end up in special directory.

We will not only be able to administer the mail server from a web interface but the email account holders will be able to access their mail via a web interface. To do this we use Ubuntu 10.10, postfix, postfix-admin, dovecot, roundcube, postgresql and nginx.

This is a copy/paste job that I put together some of it from tutorials and some of it I hacked together as I set up a test server to actually try it. In this process I some times went back to fix or change stuff in this post and ended up fluffing some of it. So I will keep on cleaning it up as I find errors or errors are pointed out to me.

Just a note, when I supply a command for the command line I will prefix it with ~# for the server or ~local# for the local machine

First things first, lets set-up a secure server:

1. Install your slice from A Ubuntu 10.10 (maverick meerkat) 32bit 256 slice should be more than enough.

2. ssh into your new slice with the supplied password.

  ~local#ssh root@ip_address_of_server

3. Set up your locale.

  ~#locale-gen en_ZA.UTF-8
  ~#update-locale LANG=en_ZA.UTF-8
  ~#dpkg-reconfigure locales

3. Update the server.

  ~#apt-get update
  ~#apt-get upgrade

4. Change the root password. Do I have to remind you to choose something sensible but secure enough?


5. Add a new user called admin and add it to the sudoers list. This is the user you will be using in the future to ssh into the server. We are going to disallow logging in as root later so you cant skip this step unless you want to experience locking your self out of the server later.

  ~#adduser admin
  ~#usermod -a -G sudo admin

6. Create a ssh key and upload your public key to the server. Dont leave the ssh passphrase blank!

  ~local#ssh-keygen -t rsa
  ~local#scp ~/.ssh/ admin@ip_address_of_server:

7. Create the authorized_keys file and set its permissions correctly. ssh is very pedantic when it comes to permissions!

  ~#mkdir ~admin/.ssh
  ~#mv ~admin/ ~admin/.ssh/authorized_keys
  ~#chown -R admin:admin ~admin/.ssh
  ~#chmod 700 ~admin/.ssh
  ~#chmod 600 ~admin/.ssh/authorized_keys

8. Next, let’s edit the /etc/ssh/sshd_config file and disable some defaults. Here are the adjustments you should make:

  ~#nano /etc/ssh/sshd_config

  Port 22222                           <--- change to a port of your choosing
  Protocol 2
  PermitRootLogin no
  PasswordAuthentication no
  X11Forwarding no
  UsePAM no
  AllowUsers admin

9. Set up IPTABLES to make server more secure.

/sbin/iptables -F
nano /etc/iptables.up.rules

Paste the following into the file:


#  Allows all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -d -j REJECT

#  Accepts all established inbound connections

#  Allows all outbound traffic
#  You can modify this to only allow certain traffic

# Allows HTTP and HTTPS connections from anywhere (the normal ports for websites)
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT

# Allows SMTP access and SSMTP
-A INPUT -p tcp --dport 25 -j ACCEPT
-A INPUT -p tcp --dport 465 -j ACCEPT

# Allows pop and pops connections
-A INPUT -p tcp --dport 110 -j ACCEPT
-A INPUT -p tcp --dport 995 -j ACCEPT

# Allows imap and imaps connections
-A INPUT -p tcp --dport 143 -j ACCEPT
-A INPUT -p tcp --dport 993 -j ACCEPT

#  Allows SSH connections
-A INPUT -p tcp -m state --state NEW --dport 22222 -j ACCEPT

# Allow ping
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT

# log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7

# Reject all other inbound - default deny unless explicitly allowed policy


10. Tell the server to use the new rules.

  ~#/sbin/iptables-restore < /etc/iptables.up.rules

11. To make sure the iptable rules are automatically loaded in the future create the following script.

  ~#nano /etc/network/if-pre-up.d/iptables

/sbin/iptables-restore < /etc/iptables.up.rules

  ~#chmod +x /etc/network/if-pre-up.d/iptables

12. Now that we have our security in place lets reset ssh

  ~#/etc/init.d/ssh reload

13. To test the ssh security settings run the following command from your local machine without closing the current ssh connection. If this command does not log you in automatically you have fluffed something along the way and need to recheck your settings!

  ~local#ssh -p 22222 admin@ip_address_of_server

Installing the mail stuff:

(From here on we will be using the session where we logged in as admin so that is why there are sudo’s in the commands now.)

1. Install and set-up the database that will host the multiple domains.

  ~#sudo apt-get install postgresql -y
  ~#sudo -u postgres createuser -D -A -P that_password_u_used
  ~#sudo -u postgres createdb -O postfix postfix

2. Install dovecot and postfix.

  ~#sudo apt-get install dovecot-postfix

Select “Internet Site” when prompted by the install to pick a configuration. Next you will be prompted for your domain name. This is a bit confusing if you are planning to run multiple domains of this instance. You can leave it as your machine name as per default or fill in a domain name as asked. Don’t ask me what the difference is because I could not see any difference and I tried it both ways.

3. Install the stuff to make postfix and postgresql play nice.

  ~#sudo apt-get install postfix-pgsql

4. We are going to be running postfix admin and roundcube interfaces on nginx and php so we need to install those.

~#sudo apt-get install nginx
~#sudo apt-get install php5-cgi
~#sudo apt-get install php5-imap
~#sudo apt-get install php5-pgsql

5. To get fascgi to start on boot the following script from must be created and its permissions set.

~#sudo nano /etc/init.d/php-fastcgi


PHP_CGI_NAME=`basename $PHP_CGI`

start() {
      echo -n "Starting PHP FastCGI: "
      start-stop-daemon --quiet --start --background --chuid "$USER" --exec /usr/bin/env -- $PHP_CGI_ARGS
      echo "$PHP_CGI_NAME."
stop() {
      echo -n "Stopping PHP FastCGI: "
      killall -q -w -u $USER $PHP_CGI
      echo "$PHP_CGI_NAME."

case "$1" in
      echo "Usage: php-fastcgi {start|stop|restart}"
      exit 1
exit $RETVAL

~#sudo chmod +x /etc/init.d/php-fastcgi
~#sudo /etc/init.d/php-fastcgi start
~#sudo update-rc.d php-fastcgi defaults

6. Install postfix-admin

  ~#sudo wget
  ~#sudo tar -zxvf postfixadmin-2.3.2.tar.gz
  ~#sudo mv postfixadmin-2.3.2 /var/www/nginx-default/postfixadmin

7. Update the postfixadmin configuration file with your settings.

  ~#sudo nano /var/www/nginx-default/postfixadmin/

Change the following settings to:

$CONF['configured'] = true;
$CONF['postfix_admin_url'] = $_SERVER['HTTP_HOST'].'/postfixadmin';
$CONF['database_type'] = 'pgsql';
$CONF['database_password'] = 'that_password_u_used';

Update the following variables to what makes sense for your installation


8. Update nginx to know about postfix-admin by adding the following entry and restarting nginx.

~#sudo nano /etc/nginx/sites-available/default

server {
	listen  80;

        location / {
            root   /var/www/nginx-default/postfixadmin;
            index  index.php index.html index.htm;

      # pass the PHP scripts to FastCGI server listening on
        location ~ \.php$ {
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /var/www/nginx-default/postfixadmin/$fastcgi_script_name;
            include        fastcgi_params;

        location ~ /\.ht {
            deny  all;

~#sudo /etc/init.d/nginx restart

9. Switch off magic quotes by editing the php.ini file and then restart php.

~#sudo nano /etc/php5/cgi/php.ini
magic_quotes_gpc = Off
~#sudo /etc/init.d/php-fastcgi restart

10. Create a password for the using the setup.php page and save it in the config file.

Browse to http://yourserverip/setup.php and at the bottom of the page enter a “Setup password”. Update with the supplied hash.

~#sudo nano /var/www/nginx-default/postfixadmin/
$CONF['setup_password'] = 'hash_goes_here';

11. On the http://yourserverip/setup.php page you can now create a admin user using the setup password from then previous step.

12. We need to create 4 files for postfix containing SQL queries that will give postfix the information it needs.

  ~#sudo nano /etc/postfix/

user = postfix
password = yourdbpasswd
hosts = localhost
dbname = postfix
query = SELECT goto FROM alias WHERE address = '%s' AND active = true

~#sudo nano /etc/postfix/

user = postfix
password = yourdbpasswd
hosts = localhost
dbname = postfix
query = SELECT domain FROM domain WHERE domain = '%s' AND backupmx = false AND active = true

~#sudo nano /etc/postfix/

user = postfix
password = yourdbpasswd
hosts = localhost
dbname = postfix
query = SELECT quota FROM mailbox WHERE username = '%s' AND active = true

~#sudo nano /etc/postfix/

user = postfix
password = yourdbpasswd
hosts = localhost
dbname = postfix
query = SELECT domain || '/' || maildir FROM mailbox WHERE username = '%s' AND active = true

13. Now we have to update postfix’s to add the paths to the new files as well as some other updates.

  ~#sudo nano /etc/postfix/

Add the following to the end of the file.

virtual_minimum_uid = 150
virtual_uid_maps = static:150
virtual_gid_maps = static:8
virtual_mailbox_base = /var/vmail
virtual_transport = dovecot
dovecot_destination_recipient_limit = 1

virtual_alias_maps = proxy:pgsql:/etc/postfix/
virtual_mailbox_limit = proxy:pgsql:/etc/postfix/
virtual_mailbox_domains = proxy:pgsql:/etc/postfix/
virtual_mailbox_maps = proxy:pgsql:/etc/postfix/

Comment out:

#home_mailbox = Maildir/
#mailbox_command = /usr/lib/dovecot/deliver -c /etc/dovecot/dovecot-postfix.conf -n -m "${EXTENSION}"

Remove from mydestination. (It can’t be in mydestination and virtual_mailbox_domains)

14. Add the following to the end of the file.

Note: This tells dovecot to use dovecot-postfix.conf and not the dovecot.conf which is the default!

~#sudo nano /etc/postfix/

dovecot unix - n n - - pipe flags=DRhu user=vmail:mail argv=/usr/lib/dovecot/deliver -c /etc/dovecot/dovecot-postfix.conf -f ${sender} -d $(recipient)

15. Create a group called mail, user called vmail and the vmail directory that will hold the mail.

  ~#sudo useradd -r -u 150 -g mail -d /var/vmail -s /sbin/nologin vmail
  ~#sudo mkdir /var/vmail
  ~#sudo chmod 770 /var/vmail
  ~#sudo chown vmail:mail /var/vmail/

16. Some changes to the dovecot configuration to accept the mail and deliver it using the data in postgresql database.

Set the following as indicated. Where the option is commented out with a # remeber to uncomment it.

~#sudo nano /etc/dovecot/dovecot-sql.conf

driver = pgsql
connect = host=localhost dbname=postfix user=postfix password=postfix
default_pass_scheme = MD5-CRYPT
user_query = SELECT '/var/vmail/%d/%n' as home, 'maildir:/var/vmail/%d/%n' as mail, 150 AS uid, 8 AS gid, 'dirsize:storage=' ||  quota AS quota FROM mailbox WHERE username = '%u' AND active = true
password_query = SELECT username as user, password, '/var/vmail/%d/%n' as userdb_home, 'maildir:/var/vmail/%d/%n' as userdb_mail, 150 as userdb_uid, 8 as userdb_gid FROM mailbox WHERE username = '%u' AND active = true

#I missed the lda section in my first posting of this post and it caused me majour grief, hope it did not catch anybody else ...sorry ppls.

~#sudo nano /etc/dovecot/dovecot-postfix.conf

mail_location = maildir:/var/vmail/%d/%n
first_valid_uid = 150
last_valid_uid = 150

protocol lda {
                    postmaster_address =

auth default {
  mechanisms = plain

  passdb sql {
    args = /etc/dovecot/dovecot-sql.conf

  userdb sql {
    args = /etc/dovecot/dovecot-sql.conf

  master {
    path = /var/run/dovecot/auth-master
    mode = 0660
    user = vmail
    group = mail

17. Restart both services so changes take effect.

~#sudo /etc/init.d/postfix restart
~#sudo /etc/init.d/dovecot restart

18. Don’t forget to supply the ssl certificate. If you don’t have any you can create one with openssl.

~#openssl genrsa -out server.key 1024
~#openssl req -new -x509 -key server.key -out server.pem -days 1826
~#sudo mv server.key /etc/ssl/private/ssl-mail.key
~#sudo mv server.pem /etc/ssl/certs/ssl-mail.pem

Roundcube web client install

1. Get rouncube.

  ~#sudo apt-get install roundcube
  ~#sudo apt-get install roundcube-pgsql

  Say yes to db config.
  Select pgsql
  Enter password for the roundcude database.
  Confirm password.

  ~#sudo ln -s /usr/share/roundcube /var/www/roundcube

2. Let nginx know about roundcube for each of the domains you want to.

  ~#sudo nano /etc/nginx/sites-enabled/default

          server {
      	    listen          80;

      	    location / {
      		    index index.php index.html;
      		    root  /var/www/nginx-default/roundcube;
      	    location ~ \.php$ {
      		    fastcgi_index   index.php;
      		    fastcgi_param     SCRIPT_FILENAME     /var/www/roundcube$fastcgi_script_name;
      		    include      fastcgi_params;


  ~#sudo /etc/init.d/nginx restart

That should do it. Have fun.


Tags: , , , , , , , ,

2 Responses to “Slicehost – Ubuntu 10.10 Mail Server from Scratch”

  1. myname Says:

    Sweet Jesus, how many bugs. Looks for me like “copy+paste” from many tutorials. Misconfigured nginx, misconfigured dovecot.conf. Once you chose “dovecot-postfix.conf” as configuration file, then you try edit dovecot.conf?
    “~#sudo nano /etc/dovecot/dovecot.conf”?
    Where “auth default” directive?
    Once password to database is “postfix”, once “‘that_password_u_used'”?

  2. zaries Says:

    Thanx for the input. Yes it is a copy paste job that I put together some of it from tutorials and some of it I hacked together as I set up a test server to actually try it. In this process I some times went back to fix or change stuff and like with the ~#sudo nano /etc/dovecot/dovecot.conf I fluffed it.

    So if there are errors that slipped in I will fix them. Please feel free to suggest alternatives or elaborations of any thing in this step by step.

    I added the auth-default, the little bit I have in dovecot-postfix.conf at the moment works, even though it might not be the best way. Once again feel free to comment.

    I would appreciate it if you would elaborate on your point that nginx is misconfigured.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: