VEGAS: full mail server

This commit is contained in:
Max Headroom 2021-10-16 20:22:48 +02:00
parent 6526279a5e
commit ed80609997
12 changed files with 346 additions and 0 deletions

View file

@ -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"
];
}

View file

@ -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

View file

@ -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
'';
};
}

View file

@ -0,0 +1,7 @@
/\.ru$/ DISCARD
/\.cf$/ DISCARD
/\.gq$/ DISCARD
/\.tk$/ DISCARD
/\.ga$/ DISCARD
/pixelsurplus.com$/ DISCARD
/mega.nz$/ DISCARD

View file

@ -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" ];
}

View file

@ -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
'';
}

View file

@ -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}
'';
};
}

View file

@ -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}";
}

View file

@ -0,0 +1 @@
/.*@max.admin.privatevoid.net$/ max@privatevoid.net

View file

@ -25,6 +25,7 @@
./services/ipfs
./services/jokes
./services/nfs
./services/mail
]
# TODO: fix users
# ++ (import ../../users "server").groups.admin

View file

@ -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"o<>¦SEÈhY¹Õ è7*žZëÿâöŽV@ü|+/áÃ8Z<>óÓ)£èrÃXðcRŪû¿]í^*ä½ =ÎZ®l/ö†³\É[ »b>-§ 'BÜeÈH=憤–è‚"$†Þ7ë4i`¥ '3¾ÄìJÃ,«µ—ïápÀ8Wg<57>ÅLž,ÜŠ1yV«ðušz sþ?ØßØÉòÏ-|*Èoìô§Ô>>2{ ©CÐÁÐm<17> ãXÏëCºÚbBSÊöÕ8ªæPs¹†ƒÿݩÊÿÙ‚vl<16>ÙöFôÚߨ(Àq¶ÖÕçÙ,eoæXimô<6D>ÎMæî<C3A6>ÈøP¢'4š|Bø_—q ëKû)«æF¤T(}<7D>jbÃδ´®ûaÝ2ÇIFé<46>òà6õUŸ(k\êÊ™RÚ‡üÄ•,¼$ÜÓ<C39C>ú

View file

@ -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 ];
}