From ed80609997440d289ed27ae481652520fd23b2b7 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 16 Oct 2021 20:22:48 +0200 Subject: [PATCH] VEGAS: full mail server --- hosts/VEGAS/services/mail/default.nix | 19 ++++ hosts/VEGAS/services/mail/generic-aliases | 107 ++++++++++++++++++ hosts/VEGAS/services/mail/imap.nix | 65 +++++++++++ hosts/VEGAS/services/mail/known-spam-domains | 7 ++ hosts/VEGAS/services/mail/opendkim.nix | 15 +++ hosts/VEGAS/services/mail/postfix.nix | 93 +++++++++++++++ hosts/VEGAS/services/mail/saslauthd.nix | 17 +++ hosts/VEGAS/services/mail/sieve/plus.sieve | 9 ++ .../services/mail/virtual-mail-domain-aliases | 1 + hosts/VEGAS/system.nix | 1 + secrets/postfix-ldap-mailboxes.age | 11 ++ secrets/secrets.nix | 1 + 12 files changed, 346 insertions(+) create mode 100644 hosts/VEGAS/services/mail/default.nix create mode 100644 hosts/VEGAS/services/mail/generic-aliases create mode 100644 hosts/VEGAS/services/mail/imap.nix create mode 100644 hosts/VEGAS/services/mail/known-spam-domains create mode 100644 hosts/VEGAS/services/mail/opendkim.nix create mode 100644 hosts/VEGAS/services/mail/postfix.nix create mode 100644 hosts/VEGAS/services/mail/saslauthd.nix create mode 100644 hosts/VEGAS/services/mail/sieve/plus.sieve create mode 100644 hosts/VEGAS/services/mail/virtual-mail-domain-aliases create mode 100644 secrets/postfix-ldap-mailboxes.age diff --git a/hosts/VEGAS/services/mail/default.nix b/hosts/VEGAS/services/mail/default.nix new file mode 100644 index 0000000..9c4e0de --- /dev/null +++ b/hosts/VEGAS/services/mail/default.nix @@ -0,0 +1,19 @@ +{ tools, ... }: +{ + imports = [ + ./imap.nix + ./opendkim.nix + ./postfix.nix + ./saslauthd.nix + ]; + services.nginx.virtualHosts."mail.${tools.meta.domain}" = { + enableACME = true; + locations."/".return = "204"; + }; + security.acme.certs."mail.${tools.meta.domain}".extraDomainNames = map + (x: "${x}.${tools.meta.domain}") [ + "mx" + "imap" + "smtp" + ]; +} diff --git a/hosts/VEGAS/services/mail/generic-aliases b/hosts/VEGAS/services/mail/generic-aliases new file mode 100644 index 0000000..db4d3b4 --- /dev/null +++ b/hosts/VEGAS/services/mail/generic-aliases @@ -0,0 +1,107 @@ +# +# Aliases in this file will NOT be expanded in the header from +# Mail, but WILL be visible over networks or from /bin/mail. +# +# >>>>>>>>>> The program "newaliases" must be run after +# >> NOTE >> this file is updated for any changes to +# >>>>>>>>>> show through to sendmail. +# + +# Basic system aliases -- these MUST be present. +mailer-daemon: postmaster +postmaster: root + +# General redirections for pseudo accounts. +bin: root +daemon: root +adm: root +lp: root +sync: root +shutdown: root +halt: root +mail: root +news: root +uucp: root +operator: root +games: root +gopher: root +ftp: root +nobody: root +radiusd: root +nut: root +dbus: root +vcsa: root +canna: root +wnn: root +rpm: root +nscd: root +pcap: root +apache: root +webalizer: root +dovecot: root +fax: root +quagga: root +radvd: root +pvm: root +amandabackup: root +privoxy: root +ident: root +named: root +xfs: root +gdm: root +mailnull: root +postgres: root +sshd: root +smmsp: root +postfix: root +netdump: root +ldap: root +squid: root +ntp: root +mysql: root +desktop: root +rpcuser: root +rpc: root +nfsnobody: root +pcp: root + +ingres: root +system: root +toor: root +manager: root +dumper: root +abuse: root + +newsadm: news +newsadmin: news +usenet: news +ftpadm: ftp +ftpadmin: ftp +ftp-adm: ftp +ftp-admin: ftp +www: webmaster +webmaster: root +noc: root +security: root +hostmaster: root +info: postmaster +marketing: postmaster +sales: postmaster +support: postmaster + + +# trap decode to catch security attacks +decode: root + +# Person who should get root's mail +#root: marc +# + +contact: admins +office: admins +root: admins +admin: admins + +# TODO: unhardcode +admins: max, bone, num + diff --git a/hosts/VEGAS/services/mail/imap.nix b/hosts/VEGAS/services/mail/imap.nix new file mode 100644 index 0000000..bbd828d --- /dev/null +++ b/hosts/VEGAS/services/mail/imap.nix @@ -0,0 +1,65 @@ +{ config, lib, pkgs, tools, ... }: +let + inherit (tools.identity) ldap; + inherit (tools.meta) domain; + + postfixCfg = config.services.postfix; + + # TODO: switch to proper certdir + certDir = config.security.acme.certs."mail.${domain}".directory; + + # TODO: check how this thing does lookups, apply bind dn + ldapConfig = with ldap.accounts; pkgs.writeText "dovecot-ldap.conf.ext" '' + uris = ${ldap.server.url} + + auth_bind = yes + auth_bind_userdn = ${uidAttribute}=%n,${userSearchBase} + base = ${userSearchBase} + pass_filter = (uid=%n) + pass_attrs = uid=user + ''; +in { + networking.firewall.allowedTCPPorts = [ 143 993 ]; + + services.dovecot2 = { + enable = true; + enableLmtp = true; + enableImap = true; + enablePAM = false; + mailUser = "vmail"; + mailGroup = "vmail"; + sslServerCert = "${certDir}/fullchain.pem"; + sslServerKey = "${certDir}/key.pem"; + + modules = [ pkgs.dovecot_pigeonhole ]; + + sieveScripts.after = ./sieve; + + extraConfig = with config.services.dovecot2; '' + auth_username_format = %n + + namespace { + inbox = yes + separator = / + } + userdb { + driver = static + args = allow_all_users=yes uid=${mailUser} gid=${mailUser} home=/var/mail/virtual/%d/%n + } + passdb { + driver = ldap + args = ${ldapConfig} + } + + service auth { + unix_listener auth { + mode = 0660 + user = ${postfixCfg.user} + group = ${postfixCfg.group} + } + } + + auth_mechanisms = plain login + ''; + }; +} diff --git a/hosts/VEGAS/services/mail/known-spam-domains b/hosts/VEGAS/services/mail/known-spam-domains new file mode 100644 index 0000000..45c4e7a --- /dev/null +++ b/hosts/VEGAS/services/mail/known-spam-domains @@ -0,0 +1,7 @@ +/\.ru$/ DISCARD +/\.cf$/ DISCARD +/\.gq$/ DISCARD +/\.tk$/ DISCARD +/\.ga$/ DISCARD +/pixelsurplus.com$/ DISCARD +/mega.nz$/ DISCARD diff --git a/hosts/VEGAS/services/mail/opendkim.nix b/hosts/VEGAS/services/mail/opendkim.nix new file mode 100644 index 0000000..82075ac --- /dev/null +++ b/hosts/VEGAS/services/mail/opendkim.nix @@ -0,0 +1,15 @@ +{ lib, tools, ... }: +let + inherit (tools.meta) domain; +in +{ + services.opendkim = { + enable = true; + selector = domain; + domains = domain; + }; + # ensure socket becomes group-writable + systemd.services.opendkim.serviceConfig.UMask = lib.mkForce "0007"; + # TODO: figure out which one works + users.users.postfix.extraGroups = [ "opendkim" ]; +} diff --git a/hosts/VEGAS/services/mail/postfix.nix b/hosts/VEGAS/services/mail/postfix.nix new file mode 100644 index 0000000..9c4b755 --- /dev/null +++ b/hosts/VEGAS/services/mail/postfix.nix @@ -0,0 +1,93 @@ +{ config, tools, ... }: +let + inherit (tools.meta) domain; + certDir = config.security.acme.certs."mail.${domain}".directory; + + receivePolicy = [ "permit_sasl_authenticated" "permit_mynetworks" "reject_unauth_destination" ]; + spamPolicy = [ "reject_sender_login_mismatch" "permit_sasl_authenticated" "pcre:${./known-spam-domains}" ]; + + dkimSocket = builtins.replaceStrings ["local:"] ["unix:"] config.services.opendkim.socket; + lmtpSocket = "lmtp:unix:/run/dovecot2/lmtp"; + postfixLdapMailboxes = "ldap:${config.age.secrets."postfix-ldap-mailboxes.cf".path}"; +in +{ + age.secrets."postfix-ldap-mailboxes.cf" = { + file = ../../../../secrets/postfix-ldap-mailboxes.age; + owner = "postfix"; + group = "postfix"; + mode = "0400"; + }; + + networking.firewall.allowedTCPPorts = [ 25 465 587 ]; + + services.postfix = { + enable = true; + enableSubmission = true; + enableSubmissions = true; + inherit domain; + origin = domain; + recipientDelimiter = "+"; + + # TODO: replace with proper certs + sslCert = "/var/lib/acme/mail.${domain}/fullchain.pem"; + sslKey = "/var/lib/acme/mail.${domain}/key.pem"; + #sslCert = "${certDir}/fullchain.pem"; + #sslKey = "${certDir}/privkey.pem"; + + setSendmail = true; + + # TODO: un-hardcode + networks = [ + "localhost" + "10.1.0.1/32" + "10.10.0.0/16" + "10.100.0.0/16" + ]; + + aliasFiles.genericAliases = ./generic-aliases; + + config = { + myhostname = "mx.${domain}"; + # TODO: un-hardcode + add ip address instead of $myhostname + inet_interfaces = [ "localhost" "$myhostname" "10.1.0.1" ]; + + disable_vrfy_command = true; + + # authorization policies + smtpd_recipient_restrictions = receivePolicy; + + smtpd_relay_restrictions = receivePolicy; + smtpd_sender_restrictions = spamPolicy; + + smtpd_sender_login_maps = postfixLdapMailboxes; + + # authentication + # TODO: review these options + smtpd_sasl_auth_enable = true; + smtpd_sasl_type = "dovecot"; + smtpd_sasl_path = "/run/dovecot2/auth"; + smtpd_sasl_local_domain = domain; + smtpd_sasl_security_options = "noanonymous"; + smtpd_sasl_tls_security_options = "noanonymous"; + smtpd_sasl_authenticated_header = true; + broken_sasl_auth_clients = false; + + smtpd_milters = dkimSocket; + non_smtpd_milters = dkimSocket; + + # delivery + virtual_mailbox_domains = [ domain "max.admin.${domain}" ]; + virtual_transport = lmtpSocket; + mailbox_transport = lmtpSocket; + virtual_mailbox_maps = postfixLdapMailboxes; + virtual_alias_maps = [ + postfixLdapMailboxes + "regexp:${./virtual-mail-domain-aliases}" + "hash:/var/lib/postfix/conf/genericAliases" + ]; + }; + }; + services.fail2ban.jails.postfix = '' + enabled = true + ''; +} diff --git a/hosts/VEGAS/services/mail/saslauthd.nix b/hosts/VEGAS/services/mail/saslauthd.nix new file mode 100644 index 0000000..0418816 --- /dev/null +++ b/hosts/VEGAS/services/mail/saslauthd.nix @@ -0,0 +1,17 @@ +{ pkgs, tools, ... }: +let + inherit (tools.identity) ldap; +in +{ + services.saslauthd = { + enable = true; + mechanism = "ldap"; + package = pkgs.cyrus_sasl.override { enableLdap = true; }; + config = '' + ldap_servers: ${ldap.server.url} + ldap_filter: ${ldap.accounts.uidFilter} + ldap_search_base: ${ldap.accounts.userSearchBase} + ldapdb_canon_attr: ${ldap.accounts.uidAttribute} + ''; + }; +} diff --git a/hosts/VEGAS/services/mail/sieve/plus.sieve b/hosts/VEGAS/services/mail/sieve/plus.sieve new file mode 100644 index 0000000..2e02c16 --- /dev/null +++ b/hosts/VEGAS/services/mail/sieve/plus.sieve @@ -0,0 +1,9 @@ +require ["variables", "envelope", "fileinto", "subaddress", "mailbox"]; + +if envelope :matches :detail "to" "*" { + set :lower :upperfirst "name" "''${1}"; +} + +if not string :is "''${name}" "" { + fileinto :create "Plus/''${name}"; +} diff --git a/hosts/VEGAS/services/mail/virtual-mail-domain-aliases b/hosts/VEGAS/services/mail/virtual-mail-domain-aliases new file mode 100644 index 0000000..9a3dcde --- /dev/null +++ b/hosts/VEGAS/services/mail/virtual-mail-domain-aliases @@ -0,0 +1 @@ +/.*@max.admin.privatevoid.net$/ max@privatevoid.net diff --git a/hosts/VEGAS/system.nix b/hosts/VEGAS/system.nix index 9a6f4eb..388557e 100644 --- a/hosts/VEGAS/system.nix +++ b/hosts/VEGAS/system.nix @@ -25,6 +25,7 @@ ./services/ipfs ./services/jokes ./services/nfs + ./services/mail ] # TODO: fix users # ++ (import ../../users "server").groups.admin diff --git a/secrets/postfix-ldap-mailboxes.age b/secrets/postfix-ldap-mailboxes.age new file mode 100644 index 0000000..6ea7b72 --- /dev/null +++ b/secrets/postfix-ldap-mailboxes.age @@ -0,0 +1,11 @@ +age-encryption.org/v1 +-> ssh-ed25519 NO562A cnRopy2Tr4CFJGdwaaLZqFTSa7dLsNw4myqg7++hChQ +rVCyDQyP2bt6dKDYg7Xe2PhVh07nJTgUFdM0zWEUmFQ +-> ssh-ed25519 5/zT0w wxyMxiGccuneCthOkIMt8/v3Qcei6xUr3AluNLUrFQk +YvEbaZ8cFX11kL5G/Kq1UWCAa3V8FonnPk7p0QspfQI +-> ssh-ed25519 d3WGuA I0SpBd+PzikDgD4dw2H0H/kKUClinFQhIuihy3R10E8 +mhzTew29HEftx80w/WjALnk8PdxFaMmyiTxD9v1TBJU +-> ':2-grease ^`%- +e9YRGhE0uUNFLHvPoLM6PskQh4ANBsBFeN8 +--- mINLO9AxrnxY8TdFfrDNxwwth+lYBtNnEWP1IqUV+78 +tV"oSEhY 7*ZV@|+/8Z)rXcRŪ]^* =Zl/\[ b>- 'BeH=憤"$74i` '3J,p8WgL,܊1yVuz s?-|*o>>2{ Cm XϑCbBS8PsݩÊقvlF(q,eoXimMP'4|B_q K)FT(}jbδa2IF6U(k\ʙRڇĕ,$Ӑ \ No newline at end of file diff --git a/secrets/secrets.nix b/secrets/secrets.nix index 46bfe70..28c9084 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -10,5 +10,6 @@ in with hosts; "hydra-db-credentials.age".publicKeys = max ++ map systemKeys [ styx ]; "hydra-s3.age".publicKeys = max ++ map systemKeys [ styx ]; "oauth2_proxy-secrets.age".publicKeys = max ++ map systemKeys [ VEGAS ]; + "postfix-ldap-mailboxes.age".publicKeys = max ++ map systemKeys [ VEGAS ]; "wireguard-key-wgautobahn.age".publicKeys = max ++ map systemKeys [ VEGAS ]; }