summaryrefslogtreecommitdiffstats
path: root/src/journal/14-mail-setup.rst
blob: 9c46c175865806cdd81d094e3a452f83628372b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
.. frontmatter
   title: Mail setup
   date: 2015-05-10
   author: Johannes Löthberg
   author_link: /~kyrias/about.html

.. role:: sc

:sc:`My old mail setup has` been a bit suboptimal.
It has worked for a long time, but how it was set up wasn’t as good as it could have been.
It was set up as follows:

There are two mail servers, my server, theos, and my old classmate zephcom’s server, lucifer.
Both servers received email for the :sc:`kyriasis.com` domain, but only theos had the full user list, so lucifer forwarded all email to theos which then relayed all emails to zephcom back to lucifer.
This means that if theos goes down all email will be received by lucifer, which is good, but it also means that zephcom won’t receive his emails until theos gets back up again, even though they are on lucifer already.

To solve this I wanted to firstly use a common users table for :sc:`OpenSMTPD` so that it would be easier to maintain and deploy to both servers. The format for the plain-text tables is as follows:

.. code::

    johannes: kyrias
    gmail: demizide@gmail.com
    @kyriasis.com: bar

That user table will cause ``smtpd`` to deliver emails to ``johannes@kyriasis.com`` to the system user ``kyrias``, emails to ``gmail@kyriasis.com`` will be relayed to ``demizide@gmail.com``, and anything else will be delivered to the system user ``bar`` due to the last line which is a catch-all rule.

That’s all rather simple, but what if I want to be able to use the same file on two servers, and have each server handle just a subset of the users?
What I ended up doing was something like the following:

.. code::

    johannes: kyrias@theos.kyriasis.com
    erik: sysbunny@lucifer.kyriasis.com

Instead of delivering directly to a local users this config will instead relay the email to the proper server using that server’s :sc:`fqdn`.
Both servers are then configured to just deliver everything received at their :sc:`fqdn` to any system users, and to act as a backup server for the other, just relaying it to the proper server when it comes up.

Since ``theos`` will handle email sent to ``lucifer.kyriasis.com`` differently from how ``lucifer`` will, they can not share the exact same ``smtpd.conf``, but they can have a very similar one.

Additionally, to make deploying to both servers easier I wrote the following ``fabfile``, which lets me just run eg, ``fab upload_conf`` which will upload the config and restart ``smtpd`` on both servers, if necessary:

.. code:: python

    from fabric.api import env, local, put, run, sudo, task

    env.hosts = ['theos.kyriasis.com', 'lucifer.kyriasis.com']

    def checksum_match(local_file, remote_file):
        md5 = '/usr/bin/md5sum {}'
        local_sum = local(md5.format(local_file), capture=True).split()[0]
        remote_sum = run(md5.format(remote_file)).split()[0]

        return local_sum == remote_sum

    @task
    def upload_conf():
        local_file = '{}/smtpd.conf'.format(env.host)

        if not checksum_match(local_file, '/etc/smtpd/smtpd.conf'):
            print("Config changed, uploading new file...")
            put(local_path=local_file, remote_path='/etc/smtpd/smtpd.conf')
            sudo('systemctl restart smtpd.service')

    @task
    def upload_users():
        local_file = 'common/users'

        if not checksum_match(local_file, '/etc/smtpd/users'):
            print("Users changed, uploading new file...")
            put(local_path=local_file, remote_path='/etc/smtpd/users')
            sudo('smtpctl update table users')

And of course all of it now resides in a nice `Git repository`__ with a history of all future changes.

.. __: https://git.kyriasis.com/smtpd-conf