Deploying a mail server with Ansible

Pangs of conscience

I refused to provide automatic installation scripts since I started the first ISPmail guide for Debian. Why? Because my intention is to help you understand the complexity of setting up your own mail server. The protocols. The software components. The caveats. The features. There are countless copy’n’paste tutorials on the internet that you can follow if you don’t care learning all that. Unfortunately even if those tutorials help you set up a mail server (which quite often does not work) you will not understand how it works nor be able to troubleshoot a faulty installation. I purposely make you do all the tiny steps manually and explain what they mean. Not to torture you but to help and educate you.

But I have to be honest with you. As I’m dealing with mail servers nearly every day I have automated everything that I describe in this guide. So let me share it with you. Just promise me that you won’t take the easy route. Don’t just use the playbook without reading the entire tutorial. I will help you a bit with Ansible and Vagrant but bear with me if I refuse to expain topics that are actually part of the ISPmail guide.

Ansible

There are various tools to help you install and configure a server – they are called configuration management systems. In their special syntax you describe how you want your system to look like and then the tools checks what has to be done and does it. You can use them to automatically install a whole system or just deploy your own application. Famous pieces of software like that are Puppet and Chef. However both are painful on different levels. More modern alternatives are Ansible and Salt. I have been using Ansible very successfully in the last two years so obviously I chose it for setting up ISPmail servers.

You haven’t tried Ansible? You should. Head over to their introduction page and walk through the first chapter. From now on I expect you to know the basics of playbooks, roles and variables.

Make sure you have Ansible (at least version 1.9) installed. That version is contained in Debian testing or Ubuntu 15.10. Otherwise you can install it as a Debian package from an Ubuntu PPA or – if you don’t mind spoiling your workstation – running “pip install ansible”. Now you can clone my Git repository that contains everything you need. Use Git to clone it to somewhere on the disk of your workstation. It does not belong onto the mail server.

git clone https://git.workaround.org/chaas/ansible-ispmail-jessie.git

Vagrant

This is another very valuable tool. It is mainly used by developers but is also useful for system administrators. Have you ever thought… “Heck, I would just need a quick Debian Jessie installation to try something.”? Vagrant is the answer. It has a repository of prepared installation images that you can download and run as a virtual machine in VirtualBox.

The reason I am mentioning it is that it allows you to run all the tasks necessary to create an ISPmail server in a virtual machine. All the work that is explained in this tutorial is contained in the Ansible playbook and roles in machine-readable form. So with Ansible and Vagrant you can easily simulate a mail server installation in a virtual machine without the need of an actual server. You could use it to play around with it and don’t have to fear breaking it. Should it break you just throw it away and rebuild it.

Install Vagrant and VirtualBox:

apt-get install vagrant virtualbox

My Git repository contains a Vagrantfile that defines what Vagrant is supposed to do. It is very simple indeed:

Vagrant.configure(2) do |config|
  config.vm.box = "debian/jessie64"

  config.vm.provision "ansible" do |ansible|
    ansible.playbook = "ispmail.yml"
    ansible.sudo = true
  end

config.vm.provider "virtualbox" do |vb|
  config.vm.network "private_network", ip: "10.0.0.100"
  end
end

When you start Vagrant it downloads the image “debian/jessie64” from the image repository and boots it. You could already log into a working Jessie installation and administer it with sudo. Then it takes the ispmail.yml file and runs it as an Ansible playbook. That installs the mail server. And it sets up a virtual network on your workstation and makes the virtual server available as 10.0.0.100.

Make sure you are in the directory that contains the Vagrantfile and go create a mail server:

vagrant up

Be patient while the server is provisioned. Downloading the Jessie image takes a bit depending on your internet connection. And the first task “ispmail-packages” downloads and installs quite a lot of software – so give a few minutes. At the end you should just see green and yellow result message. Let me know if any red messages appear because that probably means a bug in the playbook.

After that you can log into the server using

vagrant ssh

And become root…

sudo -i

Of course everything else is set up already. Point your browser to https://10.0.0.100/ and you will get the RoundCube webmail interface. Go to https://10.0.0.100/phpmyadmin to manage the database via PHPmyAdmin. IMAP, POP3 and SMTP work as expected. And the example data described in this guide is already there, too.

To destroy the virtual server again run…

vagrant destroy

Customizing the installation

I am using various variable to allow you to customize the installation. See group_vars/all. Their meaning:

  • ispmail_populate_test_data: true
    Populates the database with sample data (e.g. john@example.org)
  • ispmail_mysql_mailuser_password: ChangeMe
    MySQL password for the ‘mailuser’ user
  • ispmail_mysql_root_password: ChangeMeRoot
    MySQL password for the ‘root’ user
  • ispmail_mysql_roundcube_password: ChangeMeRound
    MySQL password for the ’roundcube’ user
  • ispmail_dovecot_auth_mechanisms: plain login
    Authentication mechanisms for Dovecot. ‘plain’ is the common mechanism. ‘login’ is needed for old Microsoft clients.
  • ispmail_postmaster_address: postmaster@example.org
    Email address of the person who is responsible for the server
  • ispmail_webmail_hostname: 10.0.0.100
    Host name to be used for the web server. Should be set to the FQN.
  • ispmail_certificate_country: DE
    Entries starting with ‘ispmail_certificate…’ are strings passed to OpenSSL when generating the self-signed certificate.
  • ispmail_certificate_state: Hamburg
  • ispmail_certificate_location: Hamburg
  • ispmail_certificate_organisation: workaround.org
  • ispmail_certificate_orgunit: IT-Crowd
  • ispmail_certificate_domains:
    – example.org
    You can use multiple hostnames/domains here if your mail server should be used for them. Additional entries get added as “alternate names” in the certificate. The names must exactly match the name that users enter to access the mail server.
  • ispmail_certificate_email: postmaster@example.org
  • ispmail_certificate_days_valid: 3650
    Number of days that the certificate will be valid

Using Ansible to deploy a real-life server

Have you played with your virtual Vagrant mail server enough? Would you like to deploy a real server with Ansible? That’s easy. After a basic installation of Debian Jessie make sure that you can access the server without authentication – use SSH public key authentication.

Edit the group_vars/all. Choose proper secure passwords (‘pwgen’ is your friend) and set the email addresses, domains and certificate information to values that fit your bill.

Tell Ansible where your mail server is by editing the ‘hosts’ file and changing the ‘default’ host to the FQDN of your server. Then run

ansible-playbook ispmail.yml

You are invited to add a comment if this page helped you. Even though it’s a pretty advanced topic.

9 thoughts on “Deploying a mail server with Ansible

  • 2015-11-10 at 16:25
    Permalink

    There are 2 errors in the Ansible scripts as of 2015-11-10;

    In ansible-ispmail-jessie/roles/ispmail-database/tasks/main.yml:
    Uncomment lines 4 & 5 and change “src=root-my-cnf.j2” to “src=my-cnf.j2” on line 5

    In ansible-ispmail-jessie/roles/ispmail-database/templates/my-cnf.j2
    Add “user=root” on a separate line between “[client]” and “password={{ispmail_mysql_root_password}}”

    Reply
    • 2015-11-10 at 17:23
      Permalink

      I forgot there was a third error, it’s also in the main.yml file. Since the installation of mysql-server was done without an interactive shell the first operation must be to set a root password or else the script will fail with an unknown password set (didn’t get that far in debugging).

      The file now looks like this:

      – name: Installing required Python mysqldb module for Ansible to manage databases
      apt: pkg=python-mysqldb state=installed
      – name: set the MySQL root user’s password
      mysql_user: name=root password={{ispmail_mysql_root_password}} state=present
      – name: Creating my-cnf for root user
      template: src=my-cnf.j2 dest=/root/.my.cnf mode=0600
      – name: creating mailserver MySQL database
      mysql_db: name=mailserver state=present
      – name: creating Roundcube MySQL database
      mysql_db: name=roundcube state=present
      – name: copying MySQL database schema to server
      copy: src=schema.sql dest=/tmp
      – name: setting up SQL schema of mailserver database
      mysql_db: name=mailserver state=import target=/tmp/schema.sql
      – name: creating MySQL user to read the mailserver database
      mysql_user: name=mailuser password={{ispmail_mysql_mailuser_password}} priv=mailserver.*:SELECT,INSERT,UPDATE,DELETE state=present
      – name: creating MySQL user for roundcube
      mysql_user: name=roundcube password={{ispmail_mysql_roundcube_password}} priv=roundcube.*:ALL state=present
      – name: copying MySQL test data to server
      copy: src=test.sql dest=/tmp
      when: ispmail_populate_test_data == true
      – name: populating the database with test data
      mysql_db: name=mailserver state=import target=/tmp/test.sql
      when: ispmail_populate_test_data == true
      # – name: set the MySQL root user’s password
      # mysql_user: name=root password={{ispmail_mysql_root_password}} state=present
      # – name: Create root’s .my.cnf
      # template: src=my-cnf.j2 dest=/root/.my.cnf mode=0600

      Reply
      • 2015-11-10 at 17:58
        Permalink

        While the script does seem to complete now, Roundcube throws a database error on the login screen…

        Reply
        • 2015-11-11 at 11:35
          Permalink

          Can you login to MySQL as user “roundcube” with the ispmail_mysql_roundcube_password you set?

          Reply
    • 2015-11-11 at 11:28
      Permalink

      I commented out these lines on purpose. I did not want to set a MySQL root password (knowing that it’s insecure during the deployment) because I failed to make the Ansible playbook idempotent when setting one.

      Reply
  • 2016-02-18 at 16:25
    Permalink

    Thank you for sharing your great work!!!

    After executing the playbook, https://10.0.0.100/phpmyadmin was not working out of the box.
    I had to add

    Include /etc/phpmyadmin/apache.conf

    to the /etc/apache2/sites-enabled/default-ssl.conf and add a password for the mysql-user root

    mysql

    SET PASSWORD FOR ‘root’@’localhost’ = PASSWORD(‘MyNewPass’);

    to be able to login on phpmyadmin.

    Reply
  • 2016-10-18 at 17:27
    Permalink

    I would love to know what sort of backup solution(s) you recommend.

    Reply
    • 2016-10-18 at 18:22
      Permalink

      Depends. 🙂 I backup my personal mail server using “rsnapshot” to another system. Since emails are just files it’s simple to copy them away. Plus rsnapshot properly copies only those files that have changed. To find an email that was deleted you can simply go to the directory like “daily.1” for yesterday or “daily.2” for two days ago and use “mutt -f Maildir” to access the emails. It doesn’t get simpler than that.

      For larger mail servers where you want to put backup media offline I would recommend Bareos (formerly: Bacula). It can deal with media changers (e.g. a tape library) and compress files (which is useful as emails are mainly text).

      Do not forget to “mysqldump” your database.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close