nixos/mailserver.nix
2025-01-22 20:46:34 +00:00

248 lines
6.8 KiB
Nix

{ config, pkgs, lib, ... }: let
aliases = lib.fix (self: {
postmaster = [ "q3k" ];
MAILER-DAEMON = self.postmaster;
security = self.postmaster;
nobody = self.postmaster;
usenet = self.postmaster;
uucp = self.postmaster;
webmaster = self.postmaster;
www = self.postmaster;
orga = [ "rheya" "mewp" "martyna" ];
cfp = [ "martyna" ];
conduct = [ "mewp" "q3k" ];
});
mkAlias = name: members: "${name}: ${lib.concatStringsSep ", " members}";
aliasFile = pkgs.writeTextFile {
name = "aliases";
text = lib.concatStringsSep "\n" (lib.mapAttrsToList mkAlias aliases);
};
acmeDir = config.security.acme.certs."mail.orga.cebula.camp".directory;
# Little tool to encrypt the SMTP source.
encryptor = pkgs.rustPlatform.buildRustPackage {
name = "encryptor";
src = ./encryptor;
#cargoSha256 = lib.fakeSha256;
cargoHash = "sha256:1ky42jssq3sk2a3lf29qc019bwj73rczcaqdjaick3nmz7yf1679";
};
in {
security.acme.certs."mail.orga.cebula.camp" = {
group = "acme-mail";
};
networking.firewall.allowedTCPPorts = [ 25 587 143 993 465 ];
users.groups.acme-mail = {
members = [
config.services.nginx.user
config.services.dovecot2.user
config.services.exim.user
];
};
services.nginx.virtualHosts."mail.orga.cebula.camp" = {
forceSSL = true;
enableACME = true;
};
services.exim = {
enable = true;
package = (pkgs.exim.override { enableLDAP = true; }).overrideAttrs (oa: {
preBuild = ''
sed '
s:^# \(EXPAND_DLFUNC\)=.*:\1=yes:
s:^# \(EXTRALIBS_EXIM\)=.*:\1=-ldl -rdynamic -export-dynamic:
' -i Local/Makefile
'';
});
config = ''
primary_hostname = szalotka.cebula.camp
domainlist local_domains = cebula.camp
hostlist relay_from_hosts = 127.0.0.1
qualify_domain = cebula.camp
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data
tls_advertise_hosts = *
tls_certificate = ${acmeDir}/fullchain.pem
tls_privatekey = ${acmeDir}/key.pem
tls_on_connect_ports = 465
daemon_smtp_ports = 25 : 587 : 465
never_users = root
host_lookup = *
rfc1413_hosts = *
rfc1413_query_timeout = 5s
ignore_bounce_errors_after = 2d
timeout_frozen_after = 7d
begin acl
acl_check_rcpt:
accept hosts = :
control = dkim_disable_verify
deny message = Restricted characters in address
domains = +local_domains
local_parts = ^[.] : ^.*[@%!/|]
accept local_parts = postmaster
domains = +local_domains
require verify = sender
accept hosts = +relay_from_hosts
control = submission
control = dkim_disable_verify
accept authenticated = *
control = submission
control = dkim_disable_verify
require message = Relay not permitted
domains = +local_domains
require verify = recipient
accept
acl_check_data:
accept
begin routers
dnslookup:
driver = dnslookup
domains = ! +local_domains
transport = remote_smtp
ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
headers_add = X-Cebulacamp-Smtp-Source: ''${dlfunc{${encryptor}/lib/libencryptor.so}{encryptor}{$sender_rcvhost}}
headers_remove = Received
no_more
aliases:
driver = redirect
allow_fail
allow_defer
data = ''${lookup{$local_part}lsearch{${aliasFile}} {$value@cebula.camp}}
ldap_user:
driver = redirect
data = ''${lookup ldap { user="cn=ldap-access,ou=users,dc=cebula,dc=camp" pass="''${readfile(/var/secrets/ldap-access)}" \
ldap://10.88.0.1:389/ou=users,dc=cebula,dc=camp?\
cn?sub?(cn=''${quote_ldap:$local_part})} {$value@$domain}}
cannot_route_message = Unknown address
more = false
localuser:
driver = accept
transport = local_delivery
begin transports
remote_smtp:
driver = smtp
local_delivery:
driver = lmtp
socket = /var/run/dovecot2/lmtp
batch_max = 200
rcpt_include_affixes
begin authenticators
PLAIN:
driver = plaintext
server_set_id = $auth2
server_prompts = :
server_advertise_condition = ''${if def:tls_cipher }
server_condition = ''${if and{{ \
!eq{}{$auth2} }{ \
ldapauth{\
user="cn=''${quote_ldap_dn:$auth2},ou=users,dc=cebula,dc=camp" \
pass=''${quote:$auth3} \
ldap://10.88.0.1/} }} }
LOGIN:
driver = plaintext
server_set_id = $auth1
server_prompts = <| Username: | Password:
server_advertise_condition = ''${if def:tls_cipher }
server_condition = ''${if and{{ \
!eq{}{$auth1} }{ \
ldapauth{\
user="cn=''${quote_ldap_dn:$auth1},ou=users,dc=cebula,dc=camp" \
pass=''${quote:$auth2} \
ldap://10.88.0.1/} }} }
'';
};
services.dovecot2 = {
enable = true;
mailUser = "vmail";
mailGroup = "vmail";
createMailUser = true;
enableLmtp = true;
sslServerCert = "${acmeDir}/fullchain.pem";
sslServerKey = "${acmeDir}/key.pem";
enablePAM = false;
extraConfig = ''
userdb {
driver = ldap
args = /etc/dovecot/dovecot-users-ldap.conf.ext
}
passdb {
driver = ldap
args = /etc/dovecot/dovecot-pass-ldap.conf.ext
}
protocol lmtp {
auth_username_format = %n
}
'';
};
systemd.services.dovecot2.preStart = ''
cat > /etc/dovecot/dovecot-users-ldap.conf.ext <<END
user_attrs = \\
=home=/var/mail/%{ldap:cn}, \\
=mail=maildir:/var/mail/%{ldap:cn}/Maildir
user_filter = (&(objectClass=inetOrgPerson)(cn=%u))
iterate_attrs = =user=%{ldap:cn}
iterate_filter = (objectClass=inetOrgPerson)
base = ou=users,dc=cebula,dc=camp
hosts = 10.88.0.1
dn = cn=ldap-access,ou=users,dc=cebula,dc=camp
dnpass = $(cat /var/secrets/ldap-access)
END
cat > /etc/dovecot/dovecot-pass-ldap.conf.ext <<END
auth_bind = yes
auth_bind_userdn = cn=%u,ou=users,dc=cebula,dc=camp
base = ou=users,dc=cebula,dc=camp
hosts = 10.88.0.1
dn = cn=ldap-access,ou=users,dc=cebula,dc=camp
dnpass = $(cat /var/secrets/ldap-access)
END
'';
services.roundcube = {
enable = true;
hostName = "mail.orga.cebula.camp";
extraConfig = ''
$config['default_host'] = 'tls://mail.orga.cebula.camp';
$config['smtp_server'] = 'tls://mail.orga.cebula.camp';
$config['mail_domain'] = 'cebula.camp';
'';
};
users.users.dovecot2.extraGroups = [ "ldap-access" ];
users.users.exim.extraGroups = [ "ldap-access" ];
}