Self-host your own digital island
1# nixos-mailserver: a simple mail server
2# Copyright (C) 2016-2018 Robin Raymond
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>
16
17{ config, pkgs, lib, ... }:
18
19with config.mailserver;
20
21let
22 vmail_user = {
23 name = vmailUserName;
24 isSystemUser = true;
25 uid = vmailUID;
26 home = mailDirectory;
27 createHome = true;
28 group = vmailGroupName;
29 };
30
31
32 virtualMailUsersActivationScript = pkgs.writeScript "activate-virtual-mail-users" ''
33 #!${pkgs.stdenv.shell}
34
35 set -euo pipefail
36
37 # Create directory to store user sieve scripts if it doesn't exist
38 if (! test -d "${sieveDirectory}"); then
39 mkdir "${sieveDirectory}"
40 chown "${vmailUserName}:${vmailGroupName}" "${sieveDirectory}"
41 chmod 770 "${sieveDirectory}"
42 fi
43
44 # Copy user's sieve script to the correct location (if it exists). If it
45 # is null, remove the file.
46 ${lib.concatMapStringsSep "\n" ({ name, sieveScript }:
47 if lib.isString sieveScript then ''
48 if (! test -d "${sieveDirectory}/${name}"); then
49 mkdir -p "${sieveDirectory}/${name}"
50 chown "${vmailUserName}:${vmailGroupName}" "${sieveDirectory}/${name}"
51 chmod 770 "${sieveDirectory}/${name}"
52 fi
53 cat << 'EOF' > "${sieveDirectory}/${name}/default.sieve"
54 ${sieveScript}
55 EOF
56 chown "${vmailUserName}:${vmailGroupName}" "${sieveDirectory}/${name}/default.sieve"
57 '' else ''
58 if (test -f "${sieveDirectory}/${name}/default.sieve"); then
59 rm "${sieveDirectory}/${name}/default.sieve"
60 fi
61 if (test -f "${sieveDirectory}/${name}.svbin"); then
62 rm "${sieveDirectory}/${name}/default.svbin"
63 fi
64 '') (map (user: { inherit (user) name sieveScript; })
65 (lib.attrValues loginAccounts))}
66 '';
67in {
68 config = lib.mkIf enable {
69 # assert that all accounts provide a password
70 assertions = (map (acct: {
71 assertion = (acct.hashedPassword != null || acct.hashedPasswordFile != null);
72 message = "${acct.name} must provide either a hashed password or a password hash file";
73 }) (lib.attrValues loginAccounts));
74
75 # warn for accounts that specify both password and file
76 warnings = (map
77 (acct: "${acct.name} specifies both a password hash and hash file; hash file will be used")
78 (lib.filter
79 (acct: (acct.hashedPassword != null && acct.hashedPasswordFile != null))
80 (lib.attrValues loginAccounts)));
81
82 # set the vmail gid to a specific value
83 users.groups = {
84 "${vmailGroupName}" = { gid = vmailUID; };
85 };
86
87 # define all users
88 users.users = {
89 "${vmail_user.name}" = lib.mkForce vmail_user;
90 };
91
92 systemd.services.activate-virtual-mail-users = {
93 wantedBy = [ "multi-user.target" ];
94 before = [ "dovecot2.service" ];
95 serviceConfig = {
96 ExecStart = virtualMailUsersActivationScript;
97 };
98 enable = true;
99 };
100 };
101}