Running email service on my own server!

If u want to follow the procedures below, be sure to replace informations like URL with ur own choice of configuration,

Update Hosts

Verify that the /etc/hosts file contains a line for the server's public IP address and is associated with the Fully Qualified Domain Name (FQDN). In the examples below, the public IP address of my VPS is 12.34.56.78 and the FQDN is mail.snorl.ax. It's made for resolving the FQDN in the server locally and u needn't configure another A record for this.

127.0.0.1 localhost
12.34.56.78 mail.snorl.ax mail

Software Installation

I'm using Debian Stretch on my Linode VPS, so I installed from stretch-backports. For backports to work, a line like the following one should be added to the /etc/apt/sources.list:

deb http://ftp.debian.org/debian stretch-backports main

The address could be my choice of source containing the backports, what I added before installation is:

deb http://mirrors.linode.com/debian stretch-backports main

Then I installed the packages using the command:

$ sudo apt -t stretch-backports install postfix-pgsql sasl2-bin libsasl2-modules postgresql libpam-pgsql dovecot-pgsql dovecot-imapd dovecot-pop3d

Configuring PostgreSQL

  1. Edit /etc/postgresql/pg_hba.conf to accept password authentication for localhost:

    host    all         all         127.0.0.1         255.255.255.255   password
    
  2. Create the database:

    $ sudo su postgres
    $ createdb mails
    $ psql mails
    
  3. Create tables:

    CREATE TABLE transport (
      domain VARCHAR(128) NOT NULL,
      transport VARCHAR(128) NOT NULL,
      PRIMARY KEY (domain)
    );
    CREATE TABLE users (
      userid VARCHAR(128) NOT NULL,
      password VARCHAR(128),
      realname VARCHAR(128),
      uid INTEGER NOT NULL,
      gid INTEGER NOT NULL,
      home VARCHAR(128),
      mail VARCHAR(255),
      PRIMARY KEY (userid)
    );
    CREATE TABLE virtual (
      address VARCHAR(255) NOT NULL,
      userid VARCHAR(255) NOT NULL,
      PRIMARY KEY (address)
    );
    create view postfix_mailboxes as
      select userid, home||'/' as mailbox from users
      union all
      select domain as userid, 'dummy' as mailbox from transport;
    create view postfix_virtual as
      select userid, userid as address from users
      union all
      select userid, address from virtual;
    
  4. Create separate users for read and write accesses. Postfix and Dovecot needs only read access. I may want to use the writer user for your own purposes.

    CREATE USER mailreader PASSWORD 'secret';
    grant select on transport, users, virtual, postfix_mailboxes, postfix_virtual to mailreader;
    create user mailwriter password 'secret';
    grant select, insert, update, delete on transport, users, virtual, postfix_mailboxes, postfix_virtual to mailwriter;
    
  5. Add domain, user to the database. The examples below add the domain snorl.ax and a user with the mail address kim@snorl.ax:

    insert into transport (domain, transport) values ('snorl.ax', 'virtual:');
    insert into users (userid, uid, gid, home) values ('user@snorl.ax', 5000, 5000, 'snorl.ax/mails/user');
    insert into users (userid, uid, gid, home) values ('user2@snorl.ax', 5000, 5000, 'snorl.ax/mails/user2');
    insert into virtual (address, userid) values ('kim@snorl.ax', 'user@snorl.ax');
    
  6. The password for a user can be generated using doveadm utility (do it as a user with permission to use sudo):

    $ sudo doveadm pw -s CRYPT
    

    It will prompt me to enter the password twice:

    Enter new password: 
    Retype new password:
    

    Then it will print a encrypted string like the one below for me to write into the database:

    {CRYPT}1cElWVzS3.EVg
    

    To do something with the password, access the psql utility again:

    $ sudo su postgres
    $ psql mails
    

    To update an existing user with the password:

    UPDATE users SET password='{CRYPT}1cElWVzS3.EVg' WHERE userid='user@snorl.ax';
    

    To set the password when creating the user:

    insert into users (userid, password, uid, gid, home) values ('user3@snorl.ax', '{CRYPT}1cElWVzS3.EVg', 5000, 5000, 'snorl.ax/mails/user3');
    

Create the folder and user for mail

  1. Create /var/mail/vhosts/ and the folder named my domain:

    $ sudo mkdir /var/mail/vhosts
    $ sudo mkdir /var/mail/vhosts/snorl.ax
    
  2. Create the group and the user for it:

    $ sudo groupadd -g 5000 vmail
    $ sudo useradd -g vmail -u 5000 vmail -d /var/mail
    
  3. Change the owner of the /var/mail/ folder and its contents to belong to vmail:

    $ sudo chown -R vmail:vmail /var/mail
    

Configuring Postfix

  1. Edit /etc/postfix/main.cf to include the following lines:

    transport_maps = pgsql:/etc/postfix/transport.cf
    virtual_uid_maps = pgsql:/etc/postfix/uids.cf
    virtual_gid_maps = pgsql:/etc/postfix/gids.cf
    virtual_mailbox_base = /var/mail/vhosts
    virtual_mailbox_maps = pgsql:/etc/postfix/mailboxes.cf
    virtual_maps = pgsql:/etc/postfix/virtual.cf
    mydestination = $mydomain, $myhostname
    smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination
    smtpd_sasl_auth_enable = yes
    smtpd_sasl_security_options = noanonymous
    smtpd_sasl_local_domain = mail.snorl.ax
    smtp_sasl_auth_enable = no
    
  2. Edit /etc/postfix/sasl/smtpd.conf to include the following lines:

    pwcheck_method: saslauthd
    saslauthd_path: /etc/mux
    
  3. /etc/postfix/transport.cf

    user=mailreader
    password=secret
    dbname=mails
    table=transport
    select_field=transport
    where_field=domain
    hosts=localhost
    
  4. /etc/postfix/uids.cf

    user=mailreader
    password=secret
    dbname=mails
    table=users
    select_field=uid
    where_field=userid
    hosts=localhost
    
  5. /etc/postfix/gids.cf

    user=mailreader
    password=secret
    dbname=mails
    table=users
    select_field=gid
    where_field=userid
    hosts=localhost
    
  6. /etc/postfix/mailboxes.cf

    user=mailreader
    password=secret
    dbname=mails
    table=postfix_mailboxes
    select_field=mailbox
    where_field=userid
    hosts=localhost
    
  7. /etc/postfix/virtual.cf

    user=mailreader
    password=secret
    dbname=mails
    table=postfix_virtual
    select_field=userid
    where_field=address
    hosts=localhost
    

Configuring SASL2

  1. Edit /etc/default/saslauthd:

    START=yes
    MECHANISMS=pam
    PARAMS="-r -m /var/spool/postfix/etc"
    
  2. /etc/pam_pgsql.conf

    database = mails
    host = localhost
    user = mailreader
    password = secret
    table = users
    user_column = userid
    pwd_column = password
    #expired_column = acc_expired
    #newtok_column = acc_new_pwreq
    pw_type = crypt
    #debug
    
  3. Create /etc/pam.d/smtp:

    auth        required    pam_pgsql.so
    account     required    pam_pgsql.so
    password    required    pam_pgsql.so
    
  4. Put the following line to /etc/postfix/sasl/smtpd.conf:

    mech_list: login plain
    

Configuring Dovecot

  1. Put the following lines in the /etc/dovecot/dovecot.conf:

    mail_location = maildir:~/
    auth default {
        passdb sql {
            args = /usr/local/etc/dovecot-sql.conf
        }
        userdb sql {
            args = /usr/local/etc/dovecot-sql.conf
        }
    }
    
  2. /usr/local/etc/dovecot-sql.conf

    driver = pgsql
    connect = host=localhost dbname=mails user=mailreader password=secret
    default_pass_scheme = CRYPT
    password_query = SELECT userid as user, password FROM users WHERE userid = '%u'
    user_query = SELECT '/home/'||home AS home, uid, gid FROM users WHERE userid = '%u'
    
  3. Ensure the following line is included in /etc/dovecot/conf.d/10-auth.conf:

    disable_plaintext_auth = no
    

    `You probably want to switch this back to "yes" or other options afterward.

Configure DNS

When I'm ready to send and receive mails from my server, I should edit the domain's MX record so it points to the IP address of the server, similar to the examples below:

hmm.snorl.ax A 10 12.34.56.78
snorl.ax MX 10 hmm.snorl.ax

Then I can send and receive from username@snorl.ax. Configuration varies. If I also want to send mails from username@hey.snorl.ax, my record might look like the examples below:

hmm.snorl.ax A 10 12.34.56.78
snorl.ax MX 10 hmm.snorl.ax
hey.snorl.ax MX 10 hmm.snorl.ax

Testing Imap

  1. Restart the relevant services before testing:

    sudo systemctl restart saslauthd postgresql postfix dovecot
    
  2. Install the Mailutils package:

    sudo apt-get install mailutils
    
  3. Send a test email to an email address outside of my mail server, like a Gmail account.

    echo "Email body text" | sudo mail -s "Email subject line" recipient@gmail.com -aFrom:kim@snorl.ax
    
  4. Log into the test email account to check whether the mail is received, then send an email back to kim@snorl.ax to check if there's any message:

    sudo mail -f /var/mail/vhosts/snorl.ax/mails/user
    
  5. Now test imap using an Email Client like ThunderBird:

    • Username: kim@snorl.ax, the full email address.
    • Password: The one I just set, not the encrypted string. The one before being encrypted.
    • Server name: hmm.snorl.ax, or the IP address of the server.
    • SSL: None
    • Port: 143

SMTP Relay

An email sent from a server IP can easily go to Spam Folder, so what I do for SMTP is use an email delivery service.
I'm now using Amazon SES for sending mails. Previously I've used Sendgrid to do it. Both of them perform perfectly. Amazon SES charges $0.10 for every 1,000 emails you send. Sendgrid charges at least $14.95 monthly if u exceed 100/day.
However u'll need request for a sending quota increase to get started with Amazon SES, while u can start with it as soon as u register on Sendgrid.
The documentations below r quite useful if u r using either of the email delivery services. They clarify how to configure the service and integrate it with Postfix and email client.

Some documentations useful for using Sendgrid:

Some documentations useful for using Amazon SES:


References:

  1. HowTo/DovecotPostgresql - Dovecot Wiki
  2. Email with Postfix, Dovecot, and MySQL - Linode
  3. Debian Backports - Instructions
  4. centos - How do I change Dovecot virtual user passwords? - Server Fault
  5. BasicConfiguration - Dovecot Wiki