This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / 2configs / mailserver.nix
11 kB 339 lines
1{ 2 self, 3 config, 4 lib, 5 pkgs, 6 ... 7}: 8let 9 relayHosts = map (host: host.nets.retiolum.ip6.addr) [ 10 config.krebs.hosts.aergia 11 config.krebs.hosts.ignavia 12 config.krebs.hosts.coaxmetal 13 config.krebs.hosts.green 14 config.krebs.hosts.mors 15 ]; 16 17 mynetworks = map (ip: "[${ip}]") relayHosts; 18in 19{ 20 imports = [ 21 self.inputs.nixos-mailserver.nixosModules.default 22 ]; 23 24 mailserver = { 25 enable = true; 26 fqdn = "mail.lassul.us"; 27 domains = [ "lassul.us" ]; 28 29 loginAccounts = { 30 "lass@lassul.us" = { 31 hashedPasswordFile = 32 config.clan.core.vars.generators.mailserver-lass.files."lass-mail-password-hash".path; 33 aliases = [ 34 "postmaster@lassul.us" 35 "root@lassul.us" 36 "abuse@lassul.us" 37 "hostmaster@lassul.us" 38 "webmaster@lassul.us" 39 "security@lassul.us" 40 "noc@lassul.us" 41 "mailer-daemon@lassul.us" 42 "nobody@lassul.us" 43 "lassulus@lassul.us" 44 ]; 45 catchAll = [ "lassul.us" ]; 46 }; 47 "bot@lassul.us" = { 48 hashedPasswordFile = 49 config.clan.core.vars.generators.mailserver-bot.files."bot-mail-password-hash".path; 50 }; 51 }; 52 53 # TLS via existing ACME cert 54 x509.useACMEHost = "mail.lassul.us"; 55 56 # DKIM — auto-managed by module 57 dkimSigning = true; 58 dkimSelector = "mail"; 59 60 # ManageSieve: lets mail clients manage sieve rules dynamically (port 4190) 61 enableManageSieve = true; 62 63 # Full-text search for IMAP clients 64 fullTextSearch = { 65 enable = true; 66 memoryLimit = 500; 67 }; 68 69 stateVersion = 3; 70 71 # virtual.All mailbox needs more memory to sync all folders 72 imapMemoryLimit = 1024; 73 }; 74 75 # nixos-mailserver mkForces virtualMail without homeMode; match its priority 76 users.users.virtualMail = lib.mkOverride 50 { homeMode = "2770"; }; 77 78 # POSIX ACL default ensures new files inherit lass access (needed for muchsync hardlinks) 79 systemd.services.dovecot.serviceConfig.ExecStartPost = [ 80 "+${pkgs.acl}/bin/setfacl -R -m u:lass:rwX /var/vmail/lassul.us/lass/mail" 81 "+${pkgs.acl}/bin/setfacl -R -d -m u:lass:rwX /var/vmail/lassul.us/lass/mail" 82 ]; 83 84 # Password generation for mail accounts 85 clan.core.vars.generators.mailserver-lass = { 86 files."lass-mail-password" = { }; 87 files."lass-mail-password-hash" = { }; 88 runtimeInputs = with pkgs; [ 89 coreutils 90 mkpasswd 91 pwgen 92 ]; 93 script = '' 94 pwgen -s 32 1 > "$out/lass-mail-password" 95 mkpasswd -sm bcrypt < "$out/lass-mail-password" > "$out/lass-mail-password-hash" 96 ''; 97 }; 98 99 clan.core.vars.generators.mailserver-bot = { 100 files."bot-mail-password" = { }; 101 files."bot-mail-password-hash" = { }; 102 files."mail.env" = { }; 103 runtimeInputs = with pkgs; [ 104 coreutils 105 mkpasswd 106 pwgen 107 ]; 108 script = '' 109 pwgen -s 32 1 > "$out/bot-mail-password" 110 mkpasswd -sm bcrypt < "$out/bot-mail-password" > "$out/bot-mail-password-hash" 111 cat > "$out/mail.env" << EOF 112 IMAP_USER=bot@lassul.us 113 IMAP_PASSWORD=$(cat "$out/bot-mail-password") 114 IMAP_HOST=localhost 115 EOF 116 ''; 117 }; 118 119 # Retiolum relay: allow krebs hosts to relay through postfix 120 services.postfix.settings.main.mynetworks = lib.mkForce ( 121 [ 122 "127.0.0.0/8" 123 "[::1]/128" 124 ] 125 ++ mynetworks 126 ); 127 128 # ACME cert — reuse existing setup 129 security.acme.certs."mail.lassul.us" = { 130 group = "lasscert"; 131 webroot = "/var/lib/acme/acme-challenge"; 132 }; 133 users.groups.lasscert.members = [ 134 "dovecot2" 135 "postfix" 136 "nginx" 137 ]; 138 139 # Firewall: SMTP(25), submission(587), submissions(465), IMAPS(993), ManageSieve(4190) 140 networking.firewall.allowedTCPPorts = [ 141 25 142 587 143 465 144 993 145 4190 146 ]; 147 148 # muchsync: lass user needs access to vmail for notmuch/muchsync 149 users.groups.virtualMail.members = [ "lass" ]; 150 systemd.tmpfiles.rules = [ 151 "L+ /home/lass/Maildir - - - - /var/vmail/lassul.us/lass/mail" 152 "z /var/vmail/lassul.us 2770 virtualMail virtualMail -" 153 "z /var/vmail/lassul.us/lass 2770 virtualMail virtualMail -" 154 "d /var/vmail/lassul.us/lass/mail 2770 virtualMail virtualMail -" 155 ]; 156 157 # Thunderbird autoconfig 158 services.nginx.virtualHosts."autoconfig.lassul.us" = { 159 forceSSL = true; 160 enableACME = true; 161 locations."= /mail/config-v1.1.xml".extraConfig = '' 162 default_type application/xml; 163 return 200 '<?xml version="1.0" encoding="UTF-8"?> 164 <clientConfig version="1.1"> 165 <emailProvider id="lassul.us"> 166 <domain>lassul.us</domain> 167 <displayName>lassul.us Mail</displayName> 168 <displayShortName>lassul.us</displayShortName> 169 <incomingServer type="imap"> 170 <hostname>mail.lassul.us</hostname> 171 <port>993</port> 172 <socketType>SSL</socketType> 173 <authentication>password-cleartext</authentication> 174 <username>%EMAILADDRESS%</username> 175 </incomingServer> 176 <outgoingServer type="smtp"> 177 <hostname>mail.lassul.us</hostname> 178 <port>465</port> 179 <socketType>SSL</socketType> 180 <authentication>password-cleartext</authentication> 181 <username>%EMAILADDRESS%</username> 182 </outgoingServer> 183 </emailProvider> 184 </clientConfig>'; 185 ''; 186 }; 187 188 # Dovecot virtual mailbox + ACL-restricted shared namespace for bot 189 services.dovecot2.mailPlugins.globally.enable = [ 190 "virtual" 191 "acl" 192 ]; 193 # Provision virtual mailbox config to a writable directory (nix store is read-only, 194 # and the ACL plugin needs to write dovecot-acl-list next to the virtual config) 195 systemd.tmpfiles.settings."dovecot-virtual" = { 196 "/var/vmail/virtual-config".d = { 197 user = "virtualMail"; 198 group = "virtualMail"; 199 mode = "0750"; 200 }; 201 "/var/vmail/virtual-config/Unread".d = { 202 user = "virtualMail"; 203 group = "virtualMail"; 204 mode = "0750"; 205 }; 206 "/var/vmail/virtual-config/Unread/dovecot-virtual".f = { 207 user = "virtualMail"; 208 group = "virtualMail"; 209 mode = "0640"; 210 argument = "*\n-notmuch\n-fts-flatcurve\n unseen\n"; 211 }; 212 "/var/vmail/virtual-config/All".d = { 213 user = "virtualMail"; 214 group = "virtualMail"; 215 mode = "0750"; 216 }; 217 "/var/vmail/virtual-config/All/dovecot-virtual".f = { 218 user = "virtualMail"; 219 group = "virtualMail"; 220 mode = "0640"; 221 argument = "*\n-notmuch\n-fts-flatcurve\n all\n"; 222 }; 223 }; 224 225 services.dovecot2.extraConfig = '' 226 plugin { 227 acl = vfile 228 acl_shared_dict = file:/var/vmail/shared-mailboxes.db 229 } 230 231 namespace shared { 232 type = shared 233 separator = . 234 prefix = shared.%%n. 235 location = maildir:/var/vmail/lassul.us/%%n/mail:LAYOUT=Maildir++ 236 subscriptions = no 237 list = children 238 } 239 240 namespace virtual { 241 prefix = virtual. 242 separator = . 243 location = virtual:/var/vmail/virtual-config:INDEX=/var/vmail/virtual-indexes/%u 244 subscriptions = no 245 mailbox Unread { 246 auto = subscribe 247 } 248 mailbox All { 249 auto = subscribe 250 } 251 } 252 ''; 253 254 # Write dovecot-acl files to grant bot read-only + flag access to lass's mail 255 systemd.services.dovecot-acl-sync = { 256 description = "Sync dovecot ACL files for bot shared access"; 257 after = [ "dovecot.service" ]; 258 serviceConfig = { 259 Type = "oneshot"; 260 User = "virtualMail"; 261 Group = "virtualMail"; 262 }; 263 # l=lookup, r=read, w=write-flags, s=write-seen (no insert/delete/expunge) 264 script = '' 265 acl_line="user=bot@lassul.us lrws" 266 # Only write ACL in mailbox root dirs, skip cur/new/tmp/notmuch subdirs 267 find /var/vmail/lassul.us/lass/mail -type d \ 268 -not -name cur -not -name new -not -name tmp \ 269 -not -path '*/.notmuch/*' -not -name .notmuch \ 270 -not -path '*/fts-flatcurve/*' -not -name fts-flatcurve \ 271 | while IFS= read -r dir; do 272 acl_file="$dir/dovecot-acl" 273 if [ ! -f "$acl_file" ] || [ "$(cat "$acl_file")" != "$acl_line" ]; then 274 echo "$acl_line" > "$acl_file" 275 fi 276 done 277 ''; 278 }; 279 systemd.timers.dovecot-acl-sync = { 280 wantedBy = [ "timers.target" ]; 281 timerConfig = { 282 OnBootSec = "1min"; 283 OnUnitActiveSec = "1h"; 284 }; 285 }; 286 287 # notmuch + muchsync + msmtp for CLI mail access 288 environment.systemPackages = [ 289 self.packages.${pkgs.system}.notmuch 290 pkgs.muchsync 291 pkgs.msmtp 292 ]; 293 294 # After muchsync writes tags to notmuch DB, the Maildir filenames don't get updated 295 # (muchsync bypasses notmuch's flag sync). This watches the xapian DB for changes and 296 # renames files to add/remove the S (Seen) flag based on the notmuch unread tag. 297 systemd.paths.notmuch-flag-sync = { 298 wantedBy = [ "multi-user.target" ]; 299 pathConfig.PathChanged = "/var/vmail/lassul.us/lass/mail/.notmuch/xapian"; 300 }; 301 systemd.services.notmuch-flag-sync = { 302 description = "Sync notmuch tags to Maildir flags after muchsync"; 303 serviceConfig = { 304 Type = "oneshot"; 305 User = "lass"; 306 Group = "virtualMail"; 307 ExecStart = pkgs.writeShellScript "notmuch-flag-sync" '' 308 export NOTMUCH_CONFIG="${ 309 self.packages.${pkgs.system}.notmuch.passthru.configuration.configFile.path 310 }" 311 # First, pick up any IMAP flag changes into notmuch 312 ${pkgs.notmuch}/bin/notmuch new --quiet 2>/dev/null || true 313 # Add S flag to files notmuch considers read but that lack it 314 ${pkgs.notmuch}/bin/notmuch search --output=files 'NOT tag:unread' \ 315 | ${pkgs.gnugrep}/bin/grep '/cur/' \ 316 | while IFS= read -r f; do 317 [ -f "$f" ] || continue 318 flags="''${f##*:2,}" 319 case "$flags" in *S*) continue ;; esac 320 base="''${f%:2,*}" 321 mv "$f" "''${base}:2,''${flags}S" 322 done 323 # Remove S flag from files notmuch considers unread but that have it 324 ${pkgs.notmuch}/bin/notmuch search --output=files 'tag:unread' \ 325 | ${pkgs.gnugrep}/bin/grep '/cur/' \ 326 | while IFS= read -r f; do 327 [ -f "$f" ] || continue 328 flags="''${f##*:2,}" 329 case "$flags" in *S*) ;; *) continue ;; esac 330 base="''${f%:2,*}" 331 newflags="''${flags//S/}" 332 mv "$f" "''${base}:2,''${newflags}" 333 done 334 # Update notmuch DB with the renames 335 ${pkgs.notmuch}/bin/notmuch new --quiet 2>/dev/null || true 336 ''; 337 }; 338 }; 339}