2023-09-17 19:51:08 +00:00
|
|
|
{
|
|
|
|
config,
|
|
|
|
pkgs,
|
|
|
|
lib,
|
|
|
|
inputs,
|
|
|
|
...
|
|
|
|
}:
|
|
|
|
with lib; let
|
|
|
|
cfg = config.services.skynet_email;
|
|
|
|
|
|
|
|
# create teh new strings
|
|
|
|
create_filter_array = map (x: "(memberOf=cn=${x},ou=groups,${cfg.ldap.base})");
|
|
|
|
|
|
|
|
create_filter_join = x: concatStringsSep "" x;
|
|
|
|
|
|
|
|
# thought you could escape racket?
|
|
|
|
create_filter = groups: create_filter_join (create_filter_array groups);
|
2023-10-09 16:53:22 +00:00
|
|
|
|
2023-10-11 09:49:25 +00:00
|
|
|
create_skynet_email = accounts: mailbox: (map (account: "${account}+${mailbox}@skynet.ie") accounts);
|
2023-10-09 16:53:22 +00:00
|
|
|
|
2023-10-11 09:49:25 +00:00
|
|
|
create_skynet_email_admin = mailbox: (create_skynet_email config.skynet.users.admin mailbox) ++ ["${mailbox}_int@skynet.ie"];
|
|
|
|
create_skynet_email_committee = mailbox: (create_skynet_email config.skynet.users.committee mailbox) ++ ["${mailbox}_int@skynet.ie"];
|
2023-09-17 19:51:08 +00:00
|
|
|
in {
|
2023-06-16 22:18:53 +00:00
|
|
|
imports = [
|
|
|
|
./dns.nix
|
2023-07-16 02:23:47 +00:00
|
|
|
./acme.nix
|
|
|
|
./nginx.nix
|
2023-07-21 20:56:19 +00:00
|
|
|
inputs.simple-nixos-mailserver.nixosModule
|
2023-10-09 16:53:22 +00:00
|
|
|
|
|
|
|
# for teh config
|
|
|
|
../config/users.nix
|
2023-06-16 22:18:53 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
options.services.skynet_email = {
|
|
|
|
# options that need to be passed in to make this work
|
|
|
|
|
|
|
|
enable = mkEnableOption "Skynet Email";
|
|
|
|
|
|
|
|
host = {
|
|
|
|
ip = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
};
|
|
|
|
|
|
|
|
name = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
domain = mkOption {
|
|
|
|
type = types.str;
|
2023-07-04 20:53:24 +00:00
|
|
|
default = "skynet.ie";
|
2023-06-16 22:18:53 +00:00
|
|
|
description = lib.mdDoc "domaino";
|
|
|
|
};
|
|
|
|
|
|
|
|
sub = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "mail";
|
|
|
|
description = lib.mdDoc "mailserver subdomain";
|
|
|
|
};
|
|
|
|
|
2023-07-04 20:53:24 +00:00
|
|
|
groups = mkOption {
|
|
|
|
type = types.listOf types.str;
|
|
|
|
default = [
|
|
|
|
# general skynet users
|
|
|
|
"skynet-users"
|
|
|
|
# C&S folsk get access
|
|
|
|
"skynet-cns"
|
|
|
|
# skynet service accounts
|
|
|
|
"skynet-service"
|
|
|
|
];
|
|
|
|
description = lib.mdDoc "Groups we want to allow access to the email";
|
|
|
|
};
|
|
|
|
|
2023-06-16 22:18:53 +00:00
|
|
|
ldap = {
|
|
|
|
hosts = mkOption {
|
|
|
|
type = types.listOf types.str;
|
|
|
|
default = [
|
2023-08-06 18:00:02 +00:00
|
|
|
"ldaps://account.skynet.ie"
|
2023-06-16 22:18:53 +00:00
|
|
|
];
|
|
|
|
description = lib.mdDoc "ldap domains";
|
|
|
|
};
|
|
|
|
|
|
|
|
base = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "dc=skynet,dc=ie";
|
|
|
|
description = lib.mdDoc "where to find users";
|
|
|
|
};
|
|
|
|
|
|
|
|
searchBase = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "ou=users,${cfg.ldap.base}";
|
|
|
|
description = lib.mdDoc "where to find users";
|
|
|
|
};
|
|
|
|
|
|
|
|
bind_dn = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "cn=admin,${cfg.ldap.base}";
|
|
|
|
description = lib.mdDoc "where to find users";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = mkIf cfg.enable {
|
2023-07-15 14:05:57 +00:00
|
|
|
services.skynet_backup.normal.backups = [
|
|
|
|
"/var/vmail"
|
|
|
|
"/var/dkim"
|
|
|
|
];
|
2023-06-16 22:18:53 +00:00
|
|
|
|
|
|
|
age.secrets.ldap_pw.file = ../secrets/ldap/pw.age;
|
|
|
|
|
2023-08-06 19:09:15 +00:00
|
|
|
skynet_acme.domains = [
|
2023-08-06 19:29:24 +00:00
|
|
|
"${cfg.sub}.${cfg.domain}"
|
2023-08-06 19:09:15 +00:00
|
|
|
];
|
|
|
|
|
2023-06-16 22:18:53 +00:00
|
|
|
# set up dns record for it
|
2023-07-16 00:53:21 +00:00
|
|
|
skynet_dns.records = [
|
2023-06-16 22:18:53 +00:00
|
|
|
# basic one
|
2023-09-17 19:51:08 +00:00
|
|
|
{
|
|
|
|
record = "mail";
|
|
|
|
r_type = "A";
|
|
|
|
value = cfg.host.ip;
|
|
|
|
}
|
2023-06-16 22:18:53 +00:00
|
|
|
|
2023-07-16 00:53:21 +00:00
|
|
|
# TXT records, all tehse are inside escaped strings to allow using ""
|
|
|
|
# SPF record
|
2023-09-17 19:51:08 +00:00
|
|
|
{
|
|
|
|
record = "${cfg.domain}.";
|
|
|
|
r_type = "TXT";
|
2023-10-01 20:24:02 +00:00
|
|
|
value = ''"v=spf1 a:${cfg.sub}.${cfg.domain} a:gitlab.skynet.ie -all"'';
|
2023-09-17 19:51:08 +00:00
|
|
|
}
|
|
|
|
|
2023-07-16 11:32:18 +00:00
|
|
|
# DKIM keys
|
2023-09-17 19:51:08 +00:00
|
|
|
{
|
|
|
|
record = "mail._domainkey.skynet.ie.";
|
|
|
|
r_type = "TXT";
|
|
|
|
value = ''"v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxju1Ie60BdHwyFVPNQKovL/cX9IFPzBKgjnHZf+WBzDCFKSBpf7NvnfXajtFDQN0poaN/Qfifid+V55ZCNDBn8Y3qZa4Y69iNiLw2DdvYf0HdnxX6+pLpbmj7tikGGLJ62xnhkJhoELnz5gCOhpyoiv0tSQVaJpaGZmoll861/QIDAQAB"'';
|
|
|
|
}
|
|
|
|
{
|
|
|
|
record = "mail._domainkey.ulcompsoc.ie.";
|
|
|
|
r_type = "TXT";
|
|
|
|
value = ''"v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDl8ptSASx37t5sfmU2d2Y6yi9AVrsNFBZDmJ2uaLa4NuvAjxGQCw4wx+1Jui/HOuKYLpntLsjN851wgPR+3i51g4OblqBDvcHn9NYgWRZfHj9AASANQjdsaAbkXuyKuO46hZqeWlpESAcD6a4Evam4fkm+kiZC0+rccb4cWgsuLwIDAQAB"'';
|
|
|
|
}
|
2023-07-24 17:49:43 +00:00
|
|
|
|
2023-06-16 22:18:53 +00:00
|
|
|
# DMARC
|
2023-09-17 19:51:08 +00:00
|
|
|
{
|
|
|
|
record = "_dmarc.${cfg.domain}.";
|
|
|
|
r_type = "TXT";
|
|
|
|
value = ''"v=DMARC1; p=none"'';
|
|
|
|
}
|
2023-06-16 22:18:53 +00:00
|
|
|
|
2023-07-24 14:45:38 +00:00
|
|
|
# reverse pointer
|
2023-09-17 19:51:08 +00:00
|
|
|
{
|
|
|
|
record = cfg.host.ip;
|
|
|
|
r_type = "PTR";
|
|
|
|
value = "${cfg.sub}.${cfg.domain}.";
|
|
|
|
}
|
2023-06-16 22:18:53 +00:00
|
|
|
|
2023-09-17 19:51:08 +00:00
|
|
|
# SRV records to help gmail on android etc find the correct mail.skynet.ie domain for config rather than just defaulting to skynet.ie
|
2023-07-24 14:19:00 +00:00
|
|
|
# https://serverfault.com/questions/935192/how-to-setup-auto-configure-email-for-android-mail-app-on-your-server/1018406#1018406
|
|
|
|
# response should be:
|
|
|
|
# _imap._tcp SRV 0 1 143 imap.example.com.
|
2023-09-17 19:51:08 +00:00
|
|
|
{
|
|
|
|
record = "_imaps._tcp";
|
|
|
|
r_type = "SRV";
|
|
|
|
value = "0 1 993 ${cfg.sub}.${cfg.domain}.";
|
|
|
|
}
|
|
|
|
{
|
|
|
|
record = "_imap._tcp";
|
|
|
|
r_type = "SRV";
|
|
|
|
value = "0 1 143 ${cfg.sub}.${cfg.domain}.";
|
|
|
|
}
|
|
|
|
{
|
|
|
|
record = "_submissions._tcp";
|
|
|
|
r_type = "SRV";
|
|
|
|
value = "0 1 465 ${cfg.sub}.${cfg.domain}.";
|
|
|
|
}
|
|
|
|
{
|
|
|
|
record = "_submission._tcp";
|
|
|
|
r_type = "SRV";
|
|
|
|
value = "0 1 587 ${cfg.sub}.${cfg.domain}.";
|
|
|
|
}
|
2023-06-16 23:59:22 +00:00
|
|
|
];
|
|
|
|
|
2023-07-16 01:44:22 +00:00
|
|
|
# to provide the certs
|
|
|
|
services.nginx.virtualHosts = {
|
|
|
|
"${cfg.sub}.${cfg.domain}" = {
|
2023-09-17 19:51:08 +00:00
|
|
|
forceSSL = true;
|
2023-07-16 01:44:22 +00:00
|
|
|
useACMEHost = "skynet";
|
2023-07-25 08:53:01 +00:00
|
|
|
# override the inbuilt nginx config
|
2023-07-25 08:05:08 +00:00
|
|
|
enableACME = false;
|
2023-09-17 19:51:08 +00:00
|
|
|
serverName = "${cfg.sub}.${cfg.domain}";
|
2023-07-16 01:44:22 +00:00
|
|
|
};
|
|
|
|
};
|
2023-07-24 13:51:37 +00:00
|
|
|
|
2023-07-25 08:05:08 +00:00
|
|
|
#https://nixos-mailserver.readthedocs.io/en/latest/add-roundcube.html
|
|
|
|
users.groups.nginx = {};
|
2023-07-25 08:53:01 +00:00
|
|
|
users.groups.roundcube = {};
|
2023-07-25 08:05:08 +00:00
|
|
|
services.roundcube = {
|
2023-09-17 19:51:08 +00:00
|
|
|
enable = true;
|
|
|
|
# this is the url of the vhost, not necessarily the same as the fqdn of
|
|
|
|
# the mailserver
|
|
|
|
hostName = "${cfg.sub}.${cfg.domain}";
|
|
|
|
extraConfig = ''
|
2023-07-25 08:05:08 +00:00
|
|
|
# starttls needed for authentication, so the fqdn required to match
|
|
|
|
# the certificate
|
2023-07-25 08:53:01 +00:00
|
|
|
$config['smtp_server'] = "ssl://${cfg.sub}.${cfg.domain}";
|
2023-07-25 08:05:08 +00:00
|
|
|
$config['smtp_user'] = "%u";
|
|
|
|
$config['smtp_pass'] = "%p";
|
2023-07-25 18:29:51 +00:00
|
|
|
$config['imap_host'] = "ssl://${cfg.sub}.${cfg.domain}";
|
|
|
|
$config['product_name'] = "Skynet Webmail";
|
|
|
|
$config['identities_level'] = 4;
|
|
|
|
$config['login_username_filter'] = "email";
|
|
|
|
$config['ldap_public']['public'] = array(
|
|
|
|
'name' => 'Public LDAP Addressbook',
|
2023-08-06 18:00:02 +00:00
|
|
|
'hosts' => 'tls://account.skynet.ie',
|
2023-07-25 18:29:51 +00:00
|
|
|
'port' => 636 ,
|
|
|
|
'user_specific' => false,
|
|
|
|
'base_dn' => 'ou=users,dc=skynet,dc=ie',
|
|
|
|
'filter' => '(skMemberOf=cn=skynet-users-linux,ou=groups,dc=skynet,dc=ie)',
|
|
|
|
'fieldmap' => [
|
|
|
|
// Roundcube => LDAP:limit
|
|
|
|
'name' => 'cn',
|
|
|
|
'surname' => 'sn',
|
|
|
|
'email' => 'skMail:*',
|
2023-09-17 19:51:08 +00:00
|
|
|
]
|
2023-07-25 18:29:51 +00:00
|
|
|
);
|
|
|
|
'';
|
2023-07-25 08:05:08 +00:00
|
|
|
};
|
|
|
|
|
2023-06-16 22:18:53 +00:00
|
|
|
mailserver = {
|
|
|
|
enable = true;
|
|
|
|
fqdn = "${cfg.sub}.${cfg.domain}";
|
|
|
|
domains = [
|
|
|
|
cfg.domain
|
|
|
|
];
|
|
|
|
|
2023-10-11 09:49:25 +00:00
|
|
|
lmtpSaveToDetailMailbox = "yes";
|
|
|
|
|
2023-10-09 16:53:22 +00:00
|
|
|
extraVirtualAliases = {
|
2023-10-11 09:49:25 +00:00
|
|
|
"abuse@skynet.ie" = create_skynet_email_admin "abuse";
|
|
|
|
"accounts@skynet.ie" = create_skynet_email_committee "accounts";
|
|
|
|
"compsoc@skynet.ie" = create_skynet_email_committee "compsoc";
|
|
|
|
"contact@skynet.ie" = create_skynet_email_committee "contact";
|
|
|
|
"dbadmin@skynet.ie" = create_skynet_email_admin "dbadmin";
|
|
|
|
"dnsadm@skynet.ie" = create_skynet_email_admin "dnsadm";
|
|
|
|
"hostmaster@skynet.ie" = create_skynet_email_admin "hostmaster";
|
|
|
|
"intersocsrep@skynet.ie" = create_skynet_email_committee "intersocsrep";
|
|
|
|
"mailman@skynet.ie" = create_skynet_email_admin "mailman";
|
|
|
|
"security@skynet.ie" = create_skynet_email_admin "security";
|
|
|
|
"sysadm@skynet.ie" = create_skynet_email_admin "sysadm";
|
|
|
|
"webadmin@skynet.ie" = create_skynet_email_admin "webadmin";
|
2023-10-09 16:53:22 +00:00
|
|
|
};
|
|
|
|
|
2023-07-16 01:44:22 +00:00
|
|
|
# use the letsencrypt certs
|
|
|
|
certificateScheme = "acme";
|
|
|
|
|
2023-07-04 20:53:24 +00:00
|
|
|
# 20MB max size
|
|
|
|
messageSizeLimit = 20000000;
|
2023-06-16 22:18:53 +00:00
|
|
|
|
|
|
|
ldap = {
|
|
|
|
enable = true;
|
|
|
|
uris = cfg.ldap.hosts;
|
|
|
|
bind = {
|
|
|
|
dn = cfg.ldap.bind_dn;
|
|
|
|
passwordFile = config.age.secrets.ldap_pw.path;
|
|
|
|
};
|
2023-07-04 20:53:24 +00:00
|
|
|
|
2023-06-16 22:18:53 +00:00
|
|
|
searchBase = cfg.ldap.searchBase;
|
|
|
|
searchScope = "sub";
|
|
|
|
|
|
|
|
dovecot = {
|
2023-06-18 11:50:23 +00:00
|
|
|
userFilter = "(skMail=%u)";
|
2023-06-16 22:18:53 +00:00
|
|
|
|
2023-07-16 14:06:06 +00:00
|
|
|
# can lock down how much space each user has access to from ldap
|
2023-07-16 14:18:23 +00:00
|
|
|
userAttrs = "quotaEmail=quota_rule=*:bytes=%$,=quota_rule2=Trash:storage=+100M";
|
2023-07-16 14:06:06 +00:00
|
|
|
|
2023-07-04 20:53:24 +00:00
|
|
|
# accept emails in, but only allow access to paid up members
|
2023-09-17 19:51:08 +00:00
|
|
|
passFilter = "(&(|${create_filter cfg.groups})(skMail=%u))";
|
2023-06-16 22:18:53 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
postfix = {
|
2023-07-04 22:14:58 +00:00
|
|
|
filter = "(|(skMail=%s)(uid=%s))";
|
2023-06-16 22:18:53 +00:00
|
|
|
uidAttribute = "skMail";
|
2023-06-18 11:50:23 +00:00
|
|
|
mailAttribute = "skMail";
|
2023-06-16 22:18:53 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
# feckin spammers
|
|
|
|
rejectRecipients = [
|
|
|
|
];
|
|
|
|
};
|
|
|
|
|
|
|
|
# tune the spam filter
|
|
|
|
/*
|
|
|
|
services.rspamd.extraConfig = ''
|
|
|
|
actions {
|
|
|
|
reject = null; # Disable rejects, default is 15
|
|
|
|
add_header = 7; # Add header when reaching this score
|
|
|
|
greylist = 4; # Apply greylisting when reaching this score
|
|
|
|
}
|
|
|
|
'';
|
|
|
|
*/
|
|
|
|
};
|
|
|
|
}
|