~yoctocell/yoctocell.xyz

yoctocell.xyz/src/posts/mailserver-with-nixos.org -rw-r--r-- 5.2 KiB
46c74072Xinglu Chen templates: footer: Fix typo 2 months ago
                                                                                
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#+TITLE: Mailserver with NixOS
#+DATE: 2021-02-06
#+AUTHOR: yoctocell
#+EMAIL: public@yoctocell.xyz

In this blog post I will go through how I setup a mailserver using the
excellent [[https://gitlab.com/simple-nixos-mailserver/nixos-mailserver][nixos-mailserver]] module.  If you want to follow along, you
obviously need to setup a domain with a domain registrar.

This post will only cover the details for the server, I will make a
follow-up post on how I configure my laptop to deal with mail.

** What you get
Before we actually use the module, it is probably a good idea to go
over what it is that you get when using the module.  The module will
configure the following things for you

- Dovecot for delivering mail using IMAP or POP3
- Postfix for sending mail
- Rspamd for filtering spam and greylisting
- Let' Encrypt for SSL certificate
- Opendkim for DKIM signing
- Sieve for filtering mail
  
** Download the modules
The nixos-mailserver module is not part of the official Nixpkgs
repository, this means that we have to download it separately to use
it.  You can do this in many different ways, I am currently using the
experimental [[https://nixos.wiki/wiki/Flakes][nix-flakes]] feature so I will put the following in my
=flake.nix=:

#+begin_src nix
nixos-mailserver = {
  url = "gitlab:simple-nixos-mailserver/nixos-mailserver/nixos-20.09";
};
#+end_src

If you do not use flakes, you can put the following in your
=configuration.nix=:

#+begin_src nix
{ config, pkgs, ... }:
let release = "nixos-20.09";
in {
  imports = [
    (builtins.fetchTarball {
      url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/${release}/nixos-mailserver-${release}.tar.gz";
      # This hash needs to be updated
      sha256 = "0000000000000000000000000000000000000000000000000000";
    })
  ];

  #########################
  # The rest of the config
  ########################
}
#+end_src

** Configuration
All you have to configure is the domain for your server, the mailboxes
you want to use and the user accounts, and that is it!  Settings
=certificateScheme= to =3= means that my SSL certificate will be created
automatically with Let's Encrypt.  For this to work, you also have to
create an "A" record for the FQDN to point to your IP address.

The account password should be a hash of the real password, this can
be generated by running =mkpasswd -m sha-512= in the shell.  I will go
into more detail on the sieve script I am using in the next section.

#+begin_src nix
{
  mailserver = {
    enable = true;
    fqdn = "mail.yoctocell.xyz";
    domains = [ "yoctocell.xyz" ];

    loginAccounts = {
      "public@yoctocell.xyz" = {
        # mkpasswd -m sha-512
        hashedPassword = "<hashed-password>";
        sieveFilter = builtins.readFile ./filters.sieve;
      };
    };

    mailboxes = {
      Trash = {
        auto = "no";
        specialUse = "Trash";
      };
      Junk = {
        auto = "subscribe";
        specialUse = "Junk";
      };
      Drafts = {
        auto = "subscribe";
        specialUse = "Drafts";
      };
      Sent = {
        auto = "subscribe";
        specialUse = "Sent";
      };
      Archive = {
        auto = "subscribe";
        specialUse = "Archive";
      };
    };

    certificateScheme = 3;
  };

  networking.firewall.allowedTCPPorts = [ 465 993 ];
}
#+end_src

** Sieve
I am subscribed to a few mailing lists, to better keep track of them I
have a sieve filter on the server that refiles the emails to different
folders.  Every mailing list will get its own folder and the folder
structure looks something like this (it follows the Maildir++ spec):

#+begin_src sh
maildir
├── .Archive
├── .Sent
├── .lists.emacs.git-email
├── .lists.emacs.piem
├── .lists.nix.nixpkgs-dev
├── .lists.mail.public-inbox
├── cur
├── new
└── tmp
#+end_src

I could write all of the sieve filters manually, but that would be
very tedious.  John Wiegley has a really nice [[https://github.com/jwiegley/scripts/blob/master/sievegen][Haskell script]] which
generates the sieve script from a Haskell lookup table.  I have
modified it to filter the relevant mailing lists, and the result looks
like this:

#+begin_src sieve
elsif anyof (header :contains ["List-Id"]
            "<piem.inbox.kyleam.com>",
          header :contains ["Sender","From","To","Reply-To","Cc"]
            "piem@inbox.kyleam.com") {
  fileinto "lists.emacs.piem";
}

elsif anyof (header :contains ["List-Id"]
            "<~yoctocell/git-email-devel.lists.sr.ht>",
          header :contains ["Sender","From","To","Reply-To","Cc"]
            "~yoctocell/git-email-devel@lists.sr.ht") {
  fileinto "lists.emacs.git-email";
}

elsif anyof (header :contains ["List-Id"]
            "<meta.public-inbox.org>",
          header :contains ["Sender","From","To","Reply-To","Cc"]
            "meta@public-inbox.org") {
  fileinto "lists.mail.public-inbox";
}
#+end_src

Then I just import this file in my mailserver config with
=sieveFilter = builtins.readFile ./filters.sieve;=.

This is pretty much it for the server-side. There will be a follow up
blog post about the client-side configuration (it is quite
complicated). You can find the relevant configurations [[https://git.sr.ht/~yoctocell/nixrc/tree/master/item/profiles/mail/mailserver][here]].