diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2019-07-01 22:07:52 +0200 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2019-07-01 22:07:52 +0200 |
commit | afcc5de071dfffdc507995d1845372ba40dc1dc2 (patch) | |
tree | c96fe6b4d915e7382316a57d0d626760a7fd2876 /modules/private/websites/tools/mail | |
parent | 2f16a987d306cdb7bf9b4e80fa4af173373719bd (diff) | |
download | Nix-afcc5de071dfffdc507995d1845372ba40dc1dc2.tar.gz Nix-afcc5de071dfffdc507995d1845372ba40dc1dc2.tar.zst Nix-afcc5de071dfffdc507995d1845372ba40dc1dc2.zip |
Implement mta-sts and move mail services to specific domain
Diffstat (limited to 'modules/private/websites/tools/mail')
-rw-r--r-- | modules/private/websites/tools/mail/default.nix | 75 | ||||
-rw-r--r-- | modules/private/websites/tools/mail/mta-sts.nix | 55 | ||||
-rw-r--r-- | modules/private/websites/tools/mail/rainloop.nix | 59 | ||||
-rw-r--r-- | modules/private/websites/tools/mail/roundcubemail.nix | 125 | ||||
-rw-r--r-- | modules/private/websites/tools/mail/www/index.html | 73 |
5 files changed, 387 insertions, 0 deletions
diff --git a/modules/private/websites/tools/mail/default.nix b/modules/private/websites/tools/mail/default.nix new file mode 100644 index 0000000..ea0a27f --- /dev/null +++ b/modules/private/websites/tools/mail/default.nix | |||
@@ -0,0 +1,75 @@ | |||
1 | { lib, pkgs, config, myconfig, ... }: | ||
2 | let | ||
3 | roundcubemail = pkgs.callPackage ./roundcubemail.nix { | ||
4 | inherit (pkgs.webapps) roundcubemail roundcubemail-plugins roundcubemail-skins; | ||
5 | env = myconfig.env.tools.roundcubemail; | ||
6 | }; | ||
7 | rainloop = pkgs.callPackage ./rainloop.nix {}; | ||
8 | cfg = config.myServices.websites.tools.email; | ||
9 | in | ||
10 | { | ||
11 | options.myServices.websites.tools.email = { | ||
12 | enable = lib.mkEnableOption "enable email website"; | ||
13 | }; | ||
14 | |||
15 | imports = [ | ||
16 | ./mta-sts.nix | ||
17 | ]; | ||
18 | |||
19 | config = lib.mkIf cfg.enable { | ||
20 | secrets.keys = roundcubemail.keys; | ||
21 | |||
22 | services.websites.env.tools.modules = | ||
23 | [ "proxy_fcgi" ] | ||
24 | ++ rainloop.apache.modules | ||
25 | ++ roundcubemail.apache.modules; | ||
26 | |||
27 | services.websites.env.tools.vhostConfs.mail = { | ||
28 | certName = "mail"; | ||
29 | addToCerts = true; | ||
30 | hosts = ["mail.immae.eu"]; | ||
31 | root = "/run/current-system/webapps/_mail"; | ||
32 | extraConfig = [ | ||
33 | rainloop.apache.vhostConf | ||
34 | roundcubemail.apache.vhostConf | ||
35 | '' | ||
36 | <Directory /run/current-system/webapps/_mail> | ||
37 | Require all granted | ||
38 | Options -Indexes | ||
39 | </Directory> | ||
40 | '' | ||
41 | ]; | ||
42 | }; | ||
43 | systemd.services = { | ||
44 | phpfpm-rainloop = { | ||
45 | after = lib.mkAfter rainloop.phpFpm.serviceDeps; | ||
46 | wants = rainloop.phpFpm.serviceDeps; | ||
47 | }; | ||
48 | phpfpm-roundcubemail = { | ||
49 | after = lib.mkAfter roundcubemail.phpFpm.serviceDeps; | ||
50 | wants = roundcubemail.phpFpm.serviceDeps; | ||
51 | }; | ||
52 | }; | ||
53 | |||
54 | services.phpfpm.pools.roundcubemail = { | ||
55 | listen = roundcubemail.phpFpm.socket; | ||
56 | extraConfig = roundcubemail.phpFpm.pool; | ||
57 | phpOptions = config.services.phpfpm.phpOptions + roundcubemail.phpFpm.phpConfig; | ||
58 | }; | ||
59 | services.phpfpm.poolConfigs = { | ||
60 | rainloop = rainloop.phpFpm.pool; | ||
61 | }; | ||
62 | system.activationScripts = { | ||
63 | roundcubemail = roundcubemail.activationScript; | ||
64 | rainloop = rainloop.activationScript; | ||
65 | }; | ||
66 | |||
67 | myServices.websites.webappDirs = { | ||
68 | _mail = ./www; | ||
69 | "${roundcubemail.apache.webappName}" = roundcubemail.webRoot; | ||
70 | "${rainloop.apache.webappName}" = rainloop.webRoot; | ||
71 | }; | ||
72 | |||
73 | }; | ||
74 | |||
75 | } | ||
diff --git a/modules/private/websites/tools/mail/mta-sts.nix b/modules/private/websites/tools/mail/mta-sts.nix new file mode 100644 index 0000000..bedefda --- /dev/null +++ b/modules/private/websites/tools/mail/mta-sts.nix | |||
@@ -0,0 +1,55 @@ | |||
1 | { lib, pkgs, config, myconfig, ... }: | ||
2 | let | ||
3 | domains = (lib.remove null (lib.flatten (map | ||
4 | (zone: map | ||
5 | (e: if e.receive | ||
6 | then { | ||
7 | domain = "${e.domain}${lib.optionalString (e.domain != "") "."}${zone.name}"; | ||
8 | mail = zone.name; | ||
9 | } | ||
10 | else null | ||
11 | ) | ||
12 | (zone.withEmail or []) | ||
13 | ) | ||
14 | myconfig.env.dns.masterZones | ||
15 | ))); | ||
16 | # FIXME: increase the id number in modules/private/dns.nix when this | ||
17 | # file change (date -u +'%Y%m%d%H%M%S'Z) | ||
18 | file = domain: pkgs.writeText "mta-sts-${domain.domain}.txt" '' | ||
19 | version: STSv1 | ||
20 | mode: testing | ||
21 | mx: mx-1.${domain.mail} | ||
22 | mx: mx-2.${domain.mail} | ||
23 | max_age: 604800 | ||
24 | ''; | ||
25 | root = pkgs.runCommand "mta-sts_root" {} '' | ||
26 | mkdir -p $out | ||
27 | ${builtins.concatStringsSep "\n" (map (d: | ||
28 | "cp ${file d} $out/${d.domain}.txt" | ||
29 | ) domains)} | ||
30 | ''; | ||
31 | in | ||
32 | { | ||
33 | config.myServices.websites.webappDirs = { | ||
34 | _mta-sts = root; | ||
35 | }; | ||
36 | |||
37 | config.services.websites.env.tools.vhostConfs.mta_sts = { | ||
38 | certName = "mail"; | ||
39 | addToCerts = true; | ||
40 | hosts = ["mta-sts.mail.immae.eu"] ++ map (v: "mta-sts.${v.domain}") domains; | ||
41 | root = "/run/current-system/webapps/_mta-sts"; | ||
42 | extraConfig = [ | ||
43 | '' | ||
44 | RewriteEngine on | ||
45 | RewriteCond %{HTTP_HOST} ^mta-sts.(.*)$ | ||
46 | RewriteRule ^/.well-known/mta-sts.txt$ %{DOCUMENT_ROOT}/%1.txt [L] | ||
47 | <Directory /run/current-system/webapps/_mta-sts> | ||
48 | Require all granted | ||
49 | Options -Indexes | ||
50 | </Directory> | ||
51 | '' | ||
52 | ]; | ||
53 | }; | ||
54 | |||
55 | } | ||
diff --git a/modules/private/websites/tools/mail/rainloop.nix b/modules/private/websites/tools/mail/rainloop.nix new file mode 100644 index 0000000..dbf0f24 --- /dev/null +++ b/modules/private/websites/tools/mail/rainloop.nix | |||
@@ -0,0 +1,59 @@ | |||
1 | { lib, pkgs, writeText, stdenv, fetchurl }: | ||
2 | rec { | ||
3 | varDir = "/var/lib/rainloop"; | ||
4 | activationScript = { | ||
5 | deps = [ "wrappers" ]; | ||
6 | text = '' | ||
7 | install -m 0755 -o ${apache.user} -g ${apache.group} -d ${varDir} | ||
8 | install -m 0750 -o ${apache.user} -g ${apache.group} -d ${varDir}/phpSessions | ||
9 | install -m 0750 -o ${apache.user} -g ${apache.group} -d ${varDir}/data | ||
10 | ''; | ||
11 | }; | ||
12 | webRoot = pkgs.rainloop-community.override { dataPath = "${varDir}/data"; }; | ||
13 | apache = rec { | ||
14 | user = "wwwrun"; | ||
15 | group = "wwwrun"; | ||
16 | modules = [ "proxy_fcgi" ]; | ||
17 | webappName = "tools_rainloop"; | ||
18 | root = "/run/current-system/webapps/${webappName}"; | ||
19 | vhostConf = '' | ||
20 | Alias /rainloop "${root}" | ||
21 | <Directory "${root}"> | ||
22 | DirectoryIndex index.php | ||
23 | AllowOverride All | ||
24 | Options -FollowSymlinks | ||
25 | Require all granted | ||
26 | |||
27 | <FilesMatch "\.php$"> | ||
28 | SetHandler "proxy:unix:${phpFpm.socket}|fcgi://localhost" | ||
29 | </FilesMatch> | ||
30 | </Directory> | ||
31 | |||
32 | <DirectoryMatch "${root}/data"> | ||
33 | Require all denied | ||
34 | </DirectoryMatch> | ||
35 | ''; | ||
36 | }; | ||
37 | phpFpm = rec { | ||
38 | serviceDeps = [ "postgresql.service" ]; | ||
39 | basedir = builtins.concatStringsSep ":" [ webRoot varDir ]; | ||
40 | socket = "/var/run/phpfpm/rainloop.sock"; | ||
41 | pool = '' | ||
42 | listen = ${socket} | ||
43 | user = ${apache.user} | ||
44 | group = ${apache.group} | ||
45 | listen.owner = ${apache.user} | ||
46 | listen.group = ${apache.group} | ||
47 | pm = ondemand | ||
48 | pm.max_children = 60 | ||
49 | pm.process_idle_timeout = 60 | ||
50 | |||
51 | ; Needed to avoid clashes in browser cookies (same domain) | ||
52 | php_value[session.name] = RainloopPHPSESSID | ||
53 | php_admin_value[upload_max_filesize] = 200M | ||
54 | php_admin_value[post_max_size] = 200M | ||
55 | php_admin_value[open_basedir] = "${basedir}:/tmp" | ||
56 | php_admin_value[session.save_path] = "${varDir}/phpSessions" | ||
57 | ''; | ||
58 | }; | ||
59 | } | ||
diff --git a/modules/private/websites/tools/mail/roundcubemail.nix b/modules/private/websites/tools/mail/roundcubemail.nix new file mode 100644 index 0000000..8bb60d6 --- /dev/null +++ b/modules/private/websites/tools/mail/roundcubemail.nix | |||
@@ -0,0 +1,125 @@ | |||
1 | { env, roundcubemail, roundcubemail-plugins, roundcubemail-skins, phpPackages, apacheHttpd }: | ||
2 | rec { | ||
3 | varDir = "/var/lib/roundcubemail"; | ||
4 | activationScript = { | ||
5 | deps = [ "wrappers" ]; | ||
6 | text = '' | ||
7 | install -m 0755 -o ${apache.user} -g ${apache.group} -d ${varDir} \ | ||
8 | ${varDir}/cache ${varDir}/logs | ||
9 | install -m 0750 -o ${apache.user} -g ${apache.group} -d ${varDir}/phpSessions | ||
10 | ''; | ||
11 | }; | ||
12 | keys = [{ | ||
13 | dest = "webapps/tools-roundcube"; | ||
14 | user = apache.user; | ||
15 | group = apache.group; | ||
16 | permissions = "0400"; | ||
17 | text = '' | ||
18 | <?php | ||
19 | $config['db_dsnw'] = '${env.psql_url}'; | ||
20 | $config['default_host'] = 'ssl://imap.immae.eu'; | ||
21 | $config['username_domain'] = array( | ||
22 | "imap.immae.eu" => "mail.immae.eu" | ||
23 | ); | ||
24 | $config['imap_conn_options'] = array("ssl" => array("verify_peer" => false)); | ||
25 | $config['smtp_server'] = 'tls://smtp.immae.eu'; | ||
26 | $config['smtp_port'] = '587'; | ||
27 | $config['managesieve_host'] = 'imap.immae.eu'; | ||
28 | $config['managesieve_port'] = '4190'; | ||
29 | $config['managesieve_usetls'] = true; | ||
30 | $config['managesieve_conn_options'] = array("ssl" => array("verify_peer" => false)); | ||
31 | |||
32 | $config['imap_cache'] = 'db'; | ||
33 | $config['messages_cache'] = 'db'; | ||
34 | |||
35 | $config['support_url'] = '''; | ||
36 | |||
37 | $config['des_key'] = '${env.secret}'; | ||
38 | |||
39 | $config['skin'] = 'elastic'; | ||
40 | $config['plugins'] = array( | ||
41 | 'attachment_reminder', | ||
42 | 'emoticons', | ||
43 | 'filesystem_attachments', | ||
44 | 'hide_blockquote', | ||
45 | 'identicon', | ||
46 | 'identity_select', | ||
47 | 'jqueryui', | ||
48 | 'markasjunk', | ||
49 | 'managesieve', | ||
50 | 'newmail_notifier', | ||
51 | 'vcard_attachments', | ||
52 | 'zipdownload', | ||
53 | |||
54 | 'automatic_addressbook', | ||
55 | 'message_highlight', | ||
56 | 'carddav', | ||
57 | // Ne marche pas ?: 'ident_switch', | ||
58 | // Ne marche pas ?: 'thunderbird_labels', | ||
59 | ); | ||
60 | |||
61 | $config['language'] = 'fr_FR'; | ||
62 | |||
63 | $config['drafts_mbox'] = 'Drafts'; | ||
64 | $config['junk_mbox'] = 'Junk'; | ||
65 | $config['sent_mbox'] = 'Sent'; | ||
66 | $config['trash_mbox'] = 'Trash'; | ||
67 | $config['default_folders'] = array('INBOX', 'Drafts', 'Sent', 'Junk', 'Trash'); | ||
68 | $config['draft_autosave'] = 60; | ||
69 | $config['enable_installer'] = false; | ||
70 | $config['log_driver'] = 'file'; | ||
71 | $config['temp_dir'] = '${varDir}/cache'; | ||
72 | $config['mime_types'] = '${apacheHttpd}/conf/mime.types'; | ||
73 | ''; | ||
74 | }]; | ||
75 | webRoot = (roundcubemail.override { roundcube_config = "/var/secrets/webapps/tools-roundcube"; }).withPlugins | ||
76 | (builtins.attrValues roundcubemail-plugins) (builtins.attrValues roundcubemail-skins); | ||
77 | apache = rec { | ||
78 | user = "wwwrun"; | ||
79 | group = "wwwrun"; | ||
80 | modules = [ "proxy_fcgi" ]; | ||
81 | webappName = "tools_roundcubemail"; | ||
82 | root = "/run/current-system/webapps/${webappName}"; | ||
83 | vhostConf = '' | ||
84 | Alias /roundcube "${root}" | ||
85 | <Directory "${root}"> | ||
86 | DirectoryIndex index.php | ||
87 | AllowOverride All | ||
88 | Options FollowSymlinks | ||
89 | Require all granted | ||
90 | |||
91 | <FilesMatch "\.php$"> | ||
92 | SetHandler "proxy:unix:${phpFpm.socket}|fcgi://localhost" | ||
93 | </FilesMatch> | ||
94 | </Directory> | ||
95 | ''; | ||
96 | }; | ||
97 | phpFpm = rec { | ||
98 | serviceDeps = [ "postgresql.service" ]; | ||
99 | basedir = builtins.concatStringsSep ":" ( | ||
100 | [ webRoot "/var/secrets/webapps/tools-roundcube" varDir ] | ||
101 | ++ webRoot.plugins | ||
102 | ++ webRoot.skins); | ||
103 | phpConfig = '' | ||
104 | date.timezone = 'CET' | ||
105 | extension=${phpPackages.imagick}/lib/php/extensions/imagick.so | ||
106 | ''; | ||
107 | socket = "/var/run/phpfpm/roundcubemail.sock"; | ||
108 | pool = '' | ||
109 | user = ${apache.user} | ||
110 | group = ${apache.group} | ||
111 | listen.owner = ${apache.user} | ||
112 | listen.group = ${apache.group} | ||
113 | pm = ondemand | ||
114 | pm.max_children = 60 | ||
115 | pm.process_idle_timeout = 60 | ||
116 | |||
117 | ; Needed to avoid clashes in browser cookies (same domain) | ||
118 | php_value[session.name] = RoundcubemailPHPSESSID | ||
119 | php_admin_value[upload_max_filesize] = 200M | ||
120 | php_admin_value[post_max_size] = 200M | ||
121 | php_admin_value[open_basedir] = "${basedir}:${apacheHttpd}/conf/mime.types:/tmp" | ||
122 | php_admin_value[session.save_path] = "${varDir}/phpSessions" | ||
123 | ''; | ||
124 | }; | ||
125 | } | ||
diff --git a/modules/private/websites/tools/mail/www/index.html b/modules/private/websites/tools/mail/www/index.html new file mode 100644 index 0000000..3727c42 --- /dev/null +++ b/modules/private/websites/tools/mail/www/index.html | |||
@@ -0,0 +1,73 @@ | |||
1 | <!doctype html> | ||
2 | <html lang="fr"> | ||
3 | <head> | ||
4 | <meta charset="UTF-8"> | ||
5 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
6 | <title>E-mail configuration</title> | ||
7 | <style type="text/css"> | ||
8 | body { | ||
9 | padding-top: 1em; | ||
10 | padding-left: 5px; | ||
11 | padding-right: 5px; | ||
12 | text-align: left; | ||
13 | margin: auto; | ||
14 | font: 20px Helvetica, sans-serif; | ||
15 | color: #333; | ||
16 | height: 100%; | ||
17 | min-height: 100%; | ||
18 | } | ||
19 | article { | ||
20 | text-align: justify; | ||
21 | display: block; | ||
22 | max-width: 850px; | ||
23 | margin: 0 auto; | ||
24 | padding-top: 30px; | ||
25 | } | ||
26 | span.code { | ||
27 | font-family: monospace; | ||
28 | } | ||
29 | </style> | ||
30 | </head> | ||
31 | <body> | ||
32 | <p> | ||
33 | Email configuration. For automatic configuration in your smart e-mail | ||
34 | client, use <span class="code">login@mail.immae.eu</span>. If it | ||
35 | doesn’t work, the details are there: | ||
36 | <ul> | ||
37 | <li>IMAP: <span class="code">imap.immae.eu</span> | ||
38 | <ul> | ||
39 | <li>No unencrypted access</li> | ||
40 | <li>STARTTLS: 143 (recommended)</li> | ||
41 | <li>SSL: 993</li> | ||
42 | </ul> | ||
43 | </li> | ||
44 | <li>POP3: <span class="code">pop3.immae.eu</span> | ||
45 | <ul> | ||
46 | <li>No unencrypted access</li> | ||
47 | <li>STARTTLS: 110 (recommended)</li> | ||
48 | <li>SSL: 995</li> | ||
49 | </ul> | ||
50 | </li> | ||
51 | <li>SMTP: <span class="code">smtp.immae.eu</span> | ||
52 | <ul> | ||
53 | <li>No unencrypted access</li> | ||
54 | <li>STARTTLS: 587</li> | ||
55 | </ul> | ||
56 | </li> | ||
57 | <li>Sieve: <span class="code">imap.immae.eu</span> | ||
58 | <ul> | ||
59 | <li>No unencrypted access</li> | ||
60 | <li>STARTTLS: 4190</li> | ||
61 | </ul> | ||
62 | </li> | ||
63 | </ul> | ||
64 | </p> | ||
65 | <p>Webmails: | ||
66 | <ul> | ||
67 | <li><a href="/roundcube">Roundcube</a></li> | ||
68 | <li><a href="/rainloop">Rainloop</a> (experimental)</li> | ||
69 | </ul> | ||
70 | </p> | ||
71 | </body> | ||
72 | </html> | ||
73 | |||