Self-host your own digital island
1# nixos-mailserver: a simple mail server
2# Copyright (C) 2017 Brian Olsen
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{ config, lib, pkgs, ... }:
17
18with lib;
19
20let
21 cfg = config.mailserver;
22
23 dkimUser = config.services.opendkim.user;
24 dkimGroup = config.services.opendkim.group;
25
26 createDomainDkimCert = dom:
27 let
28 dkim_key = "${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.key";
29 dkim_txt = "${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.txt";
30 in
31 ''
32 if [ ! -f "${dkim_key}" ]
33 then
34 ${pkgs.opendkim}/bin/opendkim-genkey -s "${cfg.dkimSelector}" \
35 -d "${dom}" \
36 --bits="${toString cfg.dkimKeyBits}" \
37 --directory="${cfg.dkimKeyDirectory}"
38 mv "${cfg.dkimKeyDirectory}/${cfg.dkimSelector}.private" "${dkim_key}"
39 mv "${cfg.dkimKeyDirectory}/${cfg.dkimSelector}.txt" "${dkim_txt}"
40 echo "Generated key for domain ${dom} selector ${cfg.dkimSelector}"
41 fi
42 '';
43 createAllCerts = lib.concatStringsSep "\n" (map createDomainDkimCert cfg.domains);
44
45 keyTable = pkgs.writeText "opendkim-KeyTable"
46 (lib.concatStringsSep "\n" (lib.flip map cfg.domains
47 (dom: "${dom} ${dom}:${cfg.dkimSelector}:${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.key")));
48 signingTable = pkgs.writeText "opendkim-SigningTable"
49 (lib.concatStringsSep "\n" (lib.flip map cfg.domains (dom: "${dom} ${dom}")));
50
51 dkim = config.services.opendkim;
52 args = [ "-f" "-l" ] ++ lib.optionals (dkim.configFile != null) [ "-x" dkim.configFile ];
53in
54{
55 config = mkIf (cfg.dkimSigning && cfg.enable) {
56 services.opendkim = {
57 enable = true;
58 selector = cfg.dkimSelector;
59 keyPath = cfg.dkimKeyDirectory;
60 domains = "csl:${builtins.concatStringsSep "," cfg.domains}";
61 configFile = pkgs.writeText "opendkim.conf" (''
62 Canonicalization ${cfg.dkimHeaderCanonicalization}/${cfg.dkimBodyCanonicalization}
63 UMask 0002
64 Socket ${dkim.socket}
65 KeyTable file:${keyTable}
66 SigningTable file:${signingTable}
67 '' + (lib.optionalString cfg.debug ''
68 Syslog yes
69 SyslogSuccess yes
70 LogWhy yes
71 ''));
72 };
73
74 users.users = optionalAttrs (config.services.postfix.user == "postfix") {
75 postfix.extraGroups = [ "${dkimGroup}" ];
76 };
77 systemd.services.opendkim = {
78 preStart = lib.mkForce createAllCerts;
79 serviceConfig = {
80 ExecStart = lib.mkForce "${pkgs.opendkim}/bin/opendkim ${escapeShellArgs args}";
81 PermissionsStartOnly = lib.mkForce false;
82 };
83 };
84 systemd.tmpfiles.rules = [
85 "d '${cfg.dkimKeyDirectory}' - ${dkimUser} ${dkimGroup} - -"
86 ];
87 };
88}