refactor folder structure

This commit is contained in:
2024-12-01 12:35:59 +01:00
parent da669efee2
commit df50e70f3e
175 changed files with 7 additions and 3760 deletions

View File

@@ -0,0 +1,266 @@
{ pkgs
, config
, ...
}:
let
domain = config.networking.domain;
# domain = "cloonar.com";
ldapConfig = pkgs.writeText "dovecot-ldap.conf" ''
hosts = ldap.cloonar.com
tls = yes
dn = "cn=vmail,ou=system,ou=users,dc=cloonar,dc=com"
dnpass = "@ldap-password@"
auth_bind = no
ldap_version = 3
base = ou=users,dc=%Dd
user_filter = (&(objectClass=mailAccount)(mail=%u))
user_attrs = \
quota=quota_rule=*:bytes=%$, \
=home=/var/vmail/%d/%n/, \
=mail=maildir:/var/vmail/%d/%n/Maildir
pass_attrs = mail=user,userPassword=password
pass_filter = (&(objectClass=mailAccount)(mail=%u))
iterate_attrs = =user=%{ldap:mail}
iterate_filter = (objectClass=mailAccount)
scope = subtree
default_pass_scheme = CRYPT
'';
doveSync = pkgs.writeShellScriptBin "dove-sync.sh" ''
#!/usr/bin/env bash
SERVER=''${1}
if [ -z "$SERVER" ]; then
echo "use as dove-sync.sh host.example.com"
exit 1
fi
doveadm user *@cloonar.com | while read user; do
doveadm -v sync -u $user $SERVER
done
doveadm user *@optiprot.eu | while read user; do
doveadm -v sync -u $user $SERVER
done
doveadm user *@superbros.tv | while read user; do
doveadm -v sync -u $user $SERVER
done
doveadm user *@ghetto.at | while read user; do
doveadm -v sync -u $user $SERVER
done
doveadm user *@szaku-consulting.at | while read user; do
doveadm -v sync -u $user $SERVER
done
doveadm user *@korean-skin.care | while read user; do
doveadm -v sync -u $user $SERVER
done
'';
quotaWarning = pkgs.writeShellScriptBin "quota-warning.sh" ''
#!/usr/bin/env bash
PERCENT=''${1}
USER=''${2}
cat << EOF | /usr/lib/dovecot/deliver -d ''${USER} -o "plugin/quota=dict:User quota::noenforcing:proxy::quotadict"
From: no-reply@$(hostname -f)
Subject: Warning: Your mailbox is now ''${PERCENT}% full.
Your mailbox is now ''${PERCENT}% full, please clean up some mails for further incoming mails.
EOF
if [ ''${PERCENT} -ge 95 ]; then
DOMAIN="$(echo ''${USER} | awk -F'@' '{print $2}')"
cat << EOF | /usr/lib/dovecot/deliver -d postmaster@''${DOMAIN} -o "plugin/quota=dict:User quota::noenforcing:proxy::quotadict"
From: no-reply@$(hostname -f)
Subject: Mailbox Quota Warning: ''${PERCENT}% full, ''${USER}
Mailbox (''${USER}) is now ''${PERCENT}% full, please clean up some mails for
further incoming mails.
EOF
fi
'';
in
{
environment.systemPackages = with pkgs; [
doveSync
];
services.dovecot2 = {
enable = true;
enableImap = true;
enableLmtp = true;
enablePAM = false;
mailLocation = "maildir:/var/vmail/%d/%n/Maildir";
mailUser = "vmail";
mailGroup = "vmail";
extraConfig = ''
ssl = yes
ssl_cert = </var/lib/acme/imap.${domain}/fullchain.pem
ssl_key = </var/lib/acme/imap.${domain}/key.pem
ssl_min_protocol = TLSv1.2
ssl_cipher_list = EECDH+AESGCM:EDH+AESGCM
ssl_prefer_server_ciphers = yes
ssl_dh=<${config.security.dhparams.params.dovecot2.path}
mail_plugins = virtual fts fts_lucene quota acl
service lmtp {
user = vmail
unix_listener /var/lib/postfix/queue/private/dovecot-lmtp {
group = postfix
mode = 0600
user = postfix
}
}
service doveadm {
inet_listener {
port = 4170
ssl = yes
}
}
protocol imap {
mail_plugins = $mail_plugins imap_quota imap_acl
}
protocol lmtp {
postmaster_address=postmaster@${domain}
hostname=mail.cloonar.com
mail_plugins = $mail_plugins sieve
}
service auth {
unix_listener auth-userdb {
mode = 0640
user = vmail
group = vmail
}
# Postfix smtp-auth
unix_listener /var/lib/postfix/queue/private/auth {
mode = 0666
user = postfix
group = postfix
}
}
userdb {
args = /run/dovecot2/ldap.conf
driver = ldap
}
passdb {
args = /run/dovecot2/ldap.conf
driver = ldap
}
service imap-login {
client_limit = 1000
service_count = 0
inet_listener imaps {
port = 993
}
}
service managesieve-login {
inet_listener sieve {
port = 4190
}
}
service quota-warning {
executable = script ${quotaWarning}/bin/quota-warning.sh
unix_listener quota-warning {
user = vmail
group = vmail
mode = 0660
}
}
service quota-status {
# '-p <protocol>'. Currently only 'postfix' protocol is supported.
executable = quota-status -p postfix
client_limit = 1
inet_listener {
address = 127.0.0.1
port = 12340
}
}
protocol sieve {
managesieve_logout_format = bytes ( in=%i : out=%o )
}
plugin {
sieve_dir = /var/vmail/%d/%n/sieve/scripts/
sieve = /var/vmail/%d/%n/sieve/active-script.sieve
sieve_extensions = +vacation-seconds +editheader
sieve_vacation_min_period = 1min
fts = lucene
fts_lucene = whitespace_chars=@.
quota_warning = storage=100%% quota-warning 100 %u
quota_warning2 = storage=95%% quota-warning 95 %u
quota_warning3 = storage=90%% quota-warning 90 %u
quota_warning4 = storage=85%% quota-warning 85 %u
quota_grace = 10%%
quota_status_success = DUNNO
quota_status_nouser = DUNNO
quota_status_overquota = "552 5.2.2 Mailbox is full"
}
# If you have Dovecot v2.2.8+ you may get a significant performance improvement with fetch-headers:
imapc_features = $imapc_features fetch-headers
# Read multiple mails in parallel, improves performance
mail_prefetch_count = 20
'';
modules = [
pkgs.dovecot_pigeonhole
];
protocols = [
"sieve"
];
};
users.users.vmail = {
home = "/var/vmail";
createHome = true;
isSystemUser = true;
uid = 1000;
shell = "/run/current-system/sw/bin/nologin";
};
security.dhparams = {
enable = true;
params.dovecot2 = { };
};
sops.secrets.dovecot-ldap-password = { };
systemd.services.dovecot2.preStart = ''
sed -e "s/@ldap-password@/$(cat ${config.sops.secrets.dovecot-ldap-password.path})/" ${ldapConfig} > /run/dovecot2/ldap.conf
'';
systemd.services.dovecot2 = {
wants = [ "acme-imap.${domain}.service" ];
after = [ "acme-imap.${domain}.service" ];
};
users.groups.acme.members = [ "openldap" ];
/* trigger the actual certificate generation for your hostname */
security.acme.certs."imap.${domain}" = {
extraDomainNames = [
"imap-test.${domain}"
"imap-02.${domain}"
];
postRun = "systemctl restart dovecot2.service";
};
networking.firewall.allowedTCPPorts = [
143 # imap
993 # imaps
4190 # sieve
];
}

View File

@@ -0,0 +1,507 @@
{
pkgs,
config,
...
}:
let
domain = config.networking.domain;
in {
services.openldap = {
enable = true;
urlList = [ "ldap:///" "ldaps:///" ];
settings.attrs = {
olcLogLevel = "-1";
olcTLSCACertificateFile = "/var/lib/acme/ldap.${domain}/full.pem";
olcTLSCertificateFile = "/var/lib/acme/ldap.${domain}/cert.pem";
olcTLSCertificateKeyFile = "/var/lib/acme/ldap.${domain}/key.pem";
olcTLSCipherSuite = "HIGH:MEDIUM:+3DES:+RC4:+aNULL";
olcTLSCRLCheck = "none";
olcTLSVerifyClient = "never";
olcTLSProtocolMin = "3.1";
olcSecurity = "tls=1";
};
settings.children = {
"cn=schema".includes = [
"${pkgs.openldap}/etc/schema/core.ldif"
"${pkgs.openldap}/etc/schema/cosine.ldif"
"${pkgs.openldap}/etc/schema/inetorgperson.ldif"
"${pkgs.openldap}/etc/schema/nis.ldif"
];
"olcDatabase={1}mdb".attrs = {
objectClass = ["olcDatabaseConfig" "olcMdbConfig"];
olcDatabase = "{1}mdb";
olcDbDirectory = "/var/lib/openldap/data";
olcSuffix = "dc=cloonar,dc=com";
olcRootDN = "cn=admin,dc=cloonar,dc=com";
olcRootPW.path = config.sops.secrets.openldap-rootpw.path;
olcAccess = [
''
{0}to attrs=userPassword
by self write
by anonymous auth
by dn="cn=owncloud,ou=system,ou=users,dc=cloonar,dc=com" write
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * none
''
''
{1}to attrs=loginShell
by self write
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * none
''
''
{2}to dn.subtree="ou=system,ou=users,dc=cloonar,dc=com"
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * none
''
''
{3}to *
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by dn="cn=admin,dc=cloonar,dc=com" write
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * none
''
];
};
"olcOverlay=memberof,olcDatabase={1}mdb".attrs = {
objectClass = [ "olcOverlayConfig" "olcMemberOf" ];
olcOverlay = "memberof";
olcMemberOfRefint = "TRUE";
};
"olcOverlay=ppolicy,olcDatabase={1}mdb".attrs = {
objectClass = [ "olcOverlayConfig" "olcPPolicyConfig" ];
olcOverlay = "ppolicy";
olcPPolicyHashCleartext = "TRUE";
};
# "olcOverlay=syncprov,olcDatabase={1}mdb".attrs = {
# objectClass = ["olcOverlayConfig" "olcSyncProvConfig"];
# olcOverlay = "syncprov";
# olcSpSessionLog = "100";
# };
"olcDatabase={2}monitor".attrs = {
olcDatabase = "{2}monitor";
objectClass = ["olcDatabaseConfig" "olcMonitorConfig"];
olcAccess = [
''
{0}to *
by dn.exact="cn=netdata,ou=system,ou=users,dc=cloonar,dc=com" read
by * none
''
];
};
"olcDatabase={3}mdb".attrs = {
objectClass = ["olcDatabaseConfig" "olcMdbConfig"];
olcDatabase = "{3}mdb";
olcDbDirectory = "/var/lib/openldap/data";
olcSuffix = "dc=ghetto,dc=at";
olcAccess = [
''
{0}to attrs=userPassword
by self write
by anonymous auth
by dn="cn=owncloud,ou=system,ou=users,dc=cloonar,dc=com" write
by dn="cn=authelia,ou=system,ou=users,dc=cloonar,dc=com" write
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * none
''
''
{1}to *
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * read
''
];
};
"olcOverlay=memberof,olcDatabase={3}mdb".attrs = {
objectClass = [ "olcOverlayConfig" "olcMemberOf" ];
olcOverlay = "memberof";
olcMemberOfRefint = "TRUE";
};
"olcOverlay=ppolicy,olcDatabase={3}mdb".attrs = {
objectClass = [ "olcOverlayConfig" "olcPPolicyConfig" ];
olcOverlay = "ppolicy";
olcPPolicyHashCleartext = "TRUE";
};
"olcDatabase={4}mdb".attrs = {
objectClass = ["olcDatabaseConfig" "olcMdbConfig"];
olcDatabase = "{4}mdb";
olcDbDirectory = "/var/lib/openldap/data";
olcSuffix = "dc=superbros,dc=tv";
olcAccess = [
''
{0}to attrs=userPassword
by self write
by anonymous auth
by dn="cn=owncloud,ou=system,ou=users,dc=cloonar,dc=com" write
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * none
''
''
{1}to *
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * read
''
];
};
"olcOverlay=memberof,olcDatabase={4}mdb".attrs = {
objectClass = [ "olcOverlayConfig" "olcMemberOf" ];
olcOverlay = "memberof";
olcMemberOfRefint = "TRUE";
};
"olcOverlay=ppolicy,olcDatabase={4}mdb".attrs = {
objectClass = [ "olcOverlayConfig" "olcPPolicyConfig" ];
olcOverlay = "ppolicy";
olcPPolicyHashCleartext = "TRUE";
};
"olcDatabase={6}mdb".attrs = {
objectClass = ["olcDatabaseConfig" "olcMdbConfig"];
olcDatabase = "{6}mdb";
olcDbDirectory = "/var/lib/openldap/data";
olcSuffix = "dc=szaku-consulting,dc=at";
olcAccess = [
''
{0}to attrs=userPassword
by self write
by anonymous auth
by dn="cn=owncloud,ou=system,ou=users,dc=cloonar,dc=com" write
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * none
''
''
{1}to *
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * read
''
];
};
# "olcOverlay=memberof,olcDatabase={6}mdb".attrs = {
# objectClass = [ "olcOverlayConfig" "olcMemberOf" ];
# olcOverlay = "memberof";
# olcMemberOfRefint = "TRUE";
# };
# "olcOverlay=ppolicy,olcDatabase={6}mdb".attrs = {
# objectClass = [ "olcOverlayConfig" "olcPPolicyConfig" ];
# olcOverlay = "ppolicy";
# olcPPolicyHashCleartext = "TRUE";
# };
"olcDatabase={7}mdb".attrs = {
objectClass = ["olcDatabaseConfig" "olcMdbConfig"];
olcDatabase = "{7}mdb";
olcDbDirectory = "/var/lib/openldap/data";
olcSuffix = "dc=myhidden,dc=life";
olcAccess = [
''
{0}to attrs=userPassword
by self write
by anonymous auth
by dn="cn=owncloud,ou=system,ou=users,dc=cloonar,dc=com" write
by dn="cn=authelia,ou=system,ou=users,dc=cloonar,dc=com" write
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * none
''
''
{1}to *
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * read
''
];
};
# "olcOverlay=memberof,olcDatabase={7}mdb".attrs = {
# objectClass = [ "olcOverlayConfig" "olcMemberOf" ];
# olcOverlay = "memberof";
# olcMemberOfRefint = "TRUE";
# };
# "olcOverlay=ppolicy,olcDatabase={7}mdb".attrs = {
# objectClass = [ "olcOverlayConfig" "olcPPolicyConfig" ];
# olcOverlay = "ppolicy";
# olcPPolicyHashCleartext = "TRUE";
# };
"olcDatabase={8}mdb".attrs = {
objectClass = ["olcDatabaseConfig" "olcMdbConfig"];
olcDatabase = "{8}mdb";
olcDbDirectory = "/var/lib/openldap/data";
olcSuffix = "dc=korean-skin,dc=care";
olcAccess = [
''
{0}to attrs=userPassword
by self write
by anonymous auth
by dn="cn=owncloud,ou=system,ou=users,dc=cloonar,dc=com" write
by dn="cn=authelia,ou=system,ou=users,dc=cloonar,dc=com" write
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * none
''
''
{1}to *
by dn.subtree="ou=system,ou=users,dc=cloonar,dc=com" read
by group.exact="cn=Administrators,ou=groups,dc=cloonar,dc=com" write
by * read
''
];
};
# "cn=module{0},cn=config" = {
# attrs = {
# objectClass = "olcModuleList";
# cn = "module{0}";
# olcModuleLoad = "ppolicy.la";
# };
# };
"cn={3}cloonar,cn=schema" = {
attrs = {
cn = "{1}cloonar";
objectClass = "olcSchemaConfig";
olcObjectClasses = [
''
(1.3.6.1.4.1.28298.1.2.4 NAME 'cloonarUser'
SUP (mailAccount) AUXILIARY
DESC 'Cloonar Account'
MAY (sshPublicKey $ ownCloudQuota $ quota))
''
];
};
};
"cn={2}postfix,cn=schema".attrs = {
cn = "{2}postfix";
objectClass = "olcSchemaConfig";
olcAttributeTypes = [
''
(1.3.6.1.4.1.12461.1.1.1 NAME 'postfixTransport'
DESC 'A string directing postfix which transport to use'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{20} SINGLE-VALUE)''
''
(1.3.6.1.4.1.12461.1.1.5 NAME 'mailbox'
DESC 'The absolute path to the mailbox for a mail account in a non-default location'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE)
''
''
(1.3.6.1.4.1.12461.1.1.6 NAME 'quota'
DESC 'A string that represents the quota on a mailbox'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE)
''
''
(1.3.6.1.4.1.12461.1.1.8 NAME 'maildrop'
DESC 'RFC822 Mailbox - mail alias'
EQUALITY caseIgnoreIA5Match
SUBSTR caseIgnoreIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256})
''
];
olcObjectClasses = [
''
(1.3.6.1.4.1.12461.1.2.1 NAME 'mailAccount'
SUP top AUXILIARY
DESC 'Mail account objects'
MUST ( mail $ userPassword )
MAY ( cn $ description $ quota))
''
''
(1.3.6.1.4.1.12461.1.2.2 NAME 'mailAlias'
SUP top STRUCTURAL
DESC 'Mail aliasing/forwarding entry'
MUST ( mail $ maildrop )
MAY ( cn $ description ))
''
''
(1.3.6.1.4.1.12461.1.2.3 NAME 'mailDomain'
SUP domain STRUCTURAL
DESC 'Virtual Domain entry to be used with postfix transport maps'
MUST ( dc )
MAY ( postfixTransport $ description ))
''
''
(1.3.6.1.4.1.12461.1.2.4 NAME 'mailPostmaster'
SUP top AUXILIARY
DESC 'Added to a mailAlias to create a postmaster entry'
MUST roleOccupant)
''
];
};
"cn={1}openssh,cn=schema".attrs = {
cn = "{1}openssh";
objectClass = "olcSchemaConfig";
olcAttributeTypes = [
''
(1.3.6.1.4.1.24552.500.1.1.1.13
NAME 'sshPublicKey'
DESC 'MANDATORY: OpenSSH Public key'
EQUALITY octetStringMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
''
];
olcObjectClasses = [
''
(1.3.6.1.4.1.24552.500.1.1.2.0
NAME 'ldapPublicKey'
SUP top AUXILIARY
DESC 'MANDATORY: OpenSSH LPK objectclass'
MUST ( sshPublicKey $ uid ))
''
];
};
"cn={1}nextcloud,cn=schema".attrs = {
cn = "{1}nextcloud";
objectClass = "olcSchemaConfig";
olcAttributeTypes = [
''
(1.3.6.1.4.1.39430.1.1.1
NAME 'ownCloudQuota'
DESC 'User Quota (e.g. 15 GB)'
SYNTAX '1.3.6.1.4.1.1466.115.121.1.15')
''
];
olcObjectClasses = [
''
(1.3.6.1.4.1.39430.1.2.1
NAME 'ownCloud'
DESC 'ownCloud LDAP Schema'
AUXILIARY
MUST ( mail $ userPassword )
MAY ( ownCloudQuota ))
''
];
};
"cn={1}gogs,cn=schema".attrs = {
cn = "{1}gogs";
objectClass = "olcSchemaConfig";
olcObjectClasses = [
''
( 1.3.6.1.4.1.28293.1.2.4 NAME 'gitlab'
SUP uidObject AUXILIARY
DESC 'Added to an account to allow gitlab access'
MUST (mail))
''
];
};
"cn={1}homeAssistant,cn=schema".attrs = {
cn = "{1}homeAssistant";
objectClass = "olcSchemaConfig";
olcObjectClasses = [
''
(1.3.6.1.4.1.28297.1.2.4 NAME 'homeAssistant'
SUP uidObject AUXILIARY
DESC 'Added to an account to allow home-assistant access'
MUST (mail) )
''
];
};
# "cn={1}ttrss,cn=schema".attrs = {
# cn = "{1}ttrss";
# objectClass = "olcSchemaConfig";
# olcObjectClasses = [
# ''
# ( 1.3.6.1.4.1.28294.1.2.4 NAME 'ttrss'
# SUP top AUXILIARY
# DESC 'Added to an account to allow tinytinyrss access'
# MUST ( mail $ userPassword ))
# ''
# ];
# };
# "cn={1}prometheus,cn=schema".attrs = {
# cn = "{1}prometheus";
# objectClass = "olcSchemaConfig";
# olcObjectClasses = [
# ''
# ( 1.3.6.1.4.1.28296.1.2.4
# NAME 'prometheus'
# SUP uidObject AUXILIARY
# DESC 'Added to an account to allow prometheus access'
# MUST (mail))
# ''
# ];
# };
# "cn={1}loki,cn=schema".attrs = {
# cn = "{1}loki";
# objectClass = "olcSchemaConfig";
# olcObjectClasses = [
# ''
# ( 1.3.6.1.4.1.28299.1.2.4
# NAME 'loki'
# SUP uidObject AUXILIARY
# DESC 'Added to an account to allow loki access'
# MUST (mail))
# ''
# ];
# };
# "cn={1}flood,cn=schema".attrs = {
# cn = "{1}flood";
# objectClass = "olcSchemaConfig";
# olcObjectClasses = [
# ''
# (1.3.6.1.4.1.28300.1.2.4 NAME 'flood'
# SUP uidObject AUXILIARY
# DESC 'Added to an account to allow flood access'
# MUST (mail))
# ''
# ];
# };
};
};
/* ensure openldap is launched after certificates are created */
systemd.services.openldap = {
wants = [ "acme-${domain}.service" ];
after = [ "acme-${domain}.service" ];
};
users.groups.acme.members = [ "openldap" ];
/* trigger the actual certificate generation for your hostname */
security.acme.certs."ldap.${domain}" = {
extraDomainNames = [
"ldap-test.${domain}"
"ldap-02.${domain}"
];
postRun = "systemctl restart openldap.service";
};
sops.secrets.openldap-rootpw.owner = "openldap";
networking.firewall.allowedTCPPorts = [ 389 636 ];
}

View File

@@ -0,0 +1,246 @@
{ pkgs
, lib
, config
, ...
}:
let
domain = config.networking.domain;
ldapServer = "ldap.cloonar.com";
# domain = "cloonar.com";
domains = pkgs.writeText "domains.cf" ''
server_host = ldap://${ldapServer}
search_base = ou=domains,dc=cloonar,dc=com
version = 3
bind = yes
start_tls = yes
bind_dn = cn=vmail,ou=system,ou=users,dc=cloonar,dc=com
bind_pw = @ldap-password@
scope = one
query_filter = (&(dc=%s)(objectClass=mailDomain))
result_attribute = postfixTransport
debuglevel = 0
'';
mailboxes = pkgs.writeText "mailboxes.cf" ''
server_host = ldap://${ldapServer}
search_base = ou=users,dc=%2,dc=%1
version = 3
bind = yes
start_tls = yes
bind_dn = cn=vmail,ou=system,ou=users,dc=cloonar,dc=com
bind_pw = @ldap-password@
scope = sub
query_filter = (&(uid=%u)(objectClass=mailAccount))
result_attribute = mail
debuglevel = 0
'';
senderLoginMaps = pkgs.writeText "sender_login_maps.cf" ''
server_host = ldap://${ldapServer}
search_base = dc=%2,dc=%1
version = 3
bind = yes
start_tls = yes
bind_dn = cn=vmail,ou=system,ou=users,dc=cloonar,dc=com
bind_pw = @ldap-password@
scope = sub
query_filter = (|(&(objectClass=mailAccount)(uid=%u))(&(objectClass=mailAlias)(mail=%s)))
result_attribute = maildrop, mail
debuglevel = 0
'';
accountsmap = pkgs.writeText "accountsmap.cf" ''
server_host = ldap://${ldapServer}
search_base = ou=users,dc=%2,dc=%1
version = 3
bind = yes
start_tls = yes
bind_dn = cn=vmail,ou=system,ou=users,dc=cloonar,dc=com
bind_pw = @ldap-password@
scope = sub
query_filter = (&(objectClass=mailAccount)(uid=%u))
result_attribute = mail
debuglevel = 0
'';
aliases = pkgs.writeText "aliases.cf" ''
server_host = ldap://${ldapServer}
search_base = ou=aliases,dc=%2,dc=%1
version = 3
bind = yes
start_tls = yes
bind_dn = cn=vmail,ou=system,ou=users,dc=cloonar,dc=com
bind_pw = @ldap-password@
scope = one
query_filter = (&(objectClass=mailAlias)(mail=%s))
result_attribute = maildrop
debuglevel = 0
'';
helo_access = pkgs.writeText "helo_access" ''
/^([0-9\.]+)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server sent non RFC compliant HELO identity (''${1})
cloonar.com REJECT ACCESS DENIED. Your email was rejected because the sending mail server sent non RFC compliant HELO identity (''${1})
ghetto.at REJECT ACCESS DENIED. Your email was rejected because the sending mail server sent non RFC compliant HELO identity (''${1})
'';
in
{
services.postfix = {
enable = true;
enableSubmission = true;
hostname = "mail.${domain}";
domain = "cloonar.com";
masterConfig."465" = {
type = "inet";
private = false;
command = "smtpd";
args = [
"-o smtpd_client_restrictions=permit_sasl_authenticated,reject"
"-o syslog_name=postfix/smtps"
"-o smtpd_tls_wrappermode=yes"
"-o smtpd_sasl_auth_enable=yes"
"-o smtpd_tls_security_level=none"
"-o smtpd_reject_unlisted_recipient=no"
"-o smtpd_recipient_restrictions="
"-o smtpd_relay_restrictions=permit_sasl_authenticated,reject"
"-o milter_macro_daemon_name=ORIGINATING"
];
};
mapFiles."helo_access" = helo_access;
config = {
# debug_peer_list = "10.42.96.190";
# smtp_bind_address = config.networking.eve.ipv4.address;
# smtp_bind_address6 = "2a01:4f9:2b:1605::1";
mailbox_transport = "lmtp:unix:private/dovecot-lmtp";
virtual_mailbox_domains = "ldap:/run/postfix/domains.cf";
virtual_mailbox_maps = "ldap:/run/postfix/mailboxes.cf";
virtual_alias_maps = "ldap:/run/postfix/accountsmap.cf,ldap:/run/postfix/aliases.cf";
virtual_transport = "lmtp:unix:private/dovecot-lmtp";
smtpd_sender_login_maps = "ldap:/run/postfix/sender_login_maps.cf";
# Do not display the name of the recipient table in the "User unknown" responses.
# The extra detail makes trouble shooting easier but also reveals information
# that is nobody elses business.
show_user_unknown_table_name = "no";
compatibility_level = "2";
# bigger attachement size
mailbox_size_limit = "202400000";
message_size_limit = "51200000";
smtpd_helo_required = "yes";
smtpd_delay_reject = "yes";
strict_rfc821_envelopes = "yes";
# send Limit
smtpd_error_sleep_time = "1s";
smtpd_soft_error_limit = "10";
smtpd_hard_error_limit = "20";
smtpd_use_tls = "yes";
smtp_tls_note_starttls_offer = "yes";
smtpd_tls_security_level = "may";
smtpd_tls_auth_only = "yes";
smtp_dns_support_level = "dnssec";
smtp_tls_security_level = "dane";
smtpd_tls_cert_file = "/var/lib/acme/mail.cloonar.com/full.pem";
smtpd_tls_key_file = "/var/lib/acme/mail.cloonar.com/key.pem";
smtpd_tls_CAfile = "/var/lib/acme/mail.cloonar.com/fullchain.pem";
smtpd_tls_dh512_param_file = config.security.dhparams.params.postfix512.path;
smtpd_tls_dh1024_param_file = config.security.dhparams.params.postfix2048.path;
smtpd_tls_session_cache_database = ''btree:''${data_directory}/smtpd_scache'';
smtpd_tls_mandatory_protocols = "!SSLv2,!SSLv3,!TLSv1,!TLSv1.1";
smtpd_tls_protocols = "!SSLv2,!SSLv3,!TLSv1,!TLSv1.1";
smtpd_tls_mandatory_ciphers = "medium";
tls_medium_cipherlist = "AES128+EECDH:AES128+EDH";
# authentication
smtpd_sasl_auth_enable = "yes";
smtpd_sasl_local_domain = "$mydomain";
smtpd_sasl_security_options = "noanonymous";
smtpd_sasl_tls_security_options = "$smtpd_sasl_security_options";
smtpd_sasl_type = "dovecot";
smtpd_sasl_path = "/var/lib/postfix/queue/private/auth";
smtpd_relay_restrictions = "
permit_mynetworks,
permit_sasl_authenticated,
defer_unauth_destination";
smtpd_client_restrictions = "
permit_mynetworks,
permit_sasl_authenticated,
reject_invalid_hostname,
reject_unknown_client,
permit";
smtpd_helo_restrictions = "
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_pipelining,
reject_non_fqdn_hostname,
reject_invalid_hostname,
warn_if_reject reject_unknown_hostname,
permit";
smtpd_recipient_restrictions = "
permit_mynetworks,
permit_sasl_authenticated,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
reject_non_fqdn_hostname,
reject_invalid_hostname,
reject_unknown_sender_domain,
reject_unknown_recipient_domain,
reject_unknown_client_hostname,
reject_unauth_pipelining,
reject_unknown_client,
permit";
smtpd_sender_restrictions = "
reject_non_fqdn_sender,
reject_unlisted_sender,
reject_authenticated_sender_login_mismatch,
permit_mynetworks,
permit_sasl_authenticated,
reject_unknown_sender_domain,
reject_unknown_client_hostname,
reject_unknown_address";
smtpd_etrn_restrictions = "permit_mynetworks, reject";
smtpd_data_restrictions = "reject_unauth_pipelining, reject_multi_recipient_bounce, permit";
};
};
systemd.tmpfiles.rules = [ "d /run/postfix 0750 postfix postfix -" ];
systemd.services.postfix.preStart = ''
sed -e "s/@ldap-password@/$(cat ${config.sops.secrets.dovecot-ldap-password.path})/" ${domains} > /run/postfix/domains.cf
sed -e "s/@ldap-password@/$(cat ${config.sops.secrets.dovecot-ldap-password.path})/" ${mailboxes} > /run/postfix/mailboxes.cf
sed -e "s/@ldap-password@/$(cat ${config.sops.secrets.dovecot-ldap-password.path})/" ${accountsmap} > /run/postfix/accountsmap.cf
sed -e "s/@ldap-password@/$(cat ${config.sops.secrets.dovecot-ldap-password.path})/" ${aliases} > /run/postfix/aliases.cf
sed -e "s/@ldap-password@/$(cat ${config.sops.secrets.dovecot-ldap-password.path})/" ${senderLoginMaps} > /run/postfix/sender_login_maps.cf
'';
security.dhparams = {
enable = true;
params.postfix512.bits = 512;
params.postfix2048.bits = 1024;
};
security.acme.certs."mail.${domain}" = {
extraDomainNames = [
"mail-test.${domain}"
"mail-02.${domain}"
];
postRun = "systemctl restart postfix.service";
group = "postfix";
};
networking.firewall.allowedTCPPorts = [
25 # smtp
465 # smtps
587 # submission
];
}

View File

@@ -0,0 +1,131 @@
{ pkgs
, config
, ...
}:
let
domain = config.networking.domain;
localConfig = pkgs.writeText "local.conf" ''
logging {
level = "notice";
}
classifier "bayes" {
autolearn = true;
}
dkim_signing {
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
selector = "default";
allow_username_mismatch = true;
}
arc {
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
selector = "default";
allow_username_mismatch = true;
}
milter_headers {
use = ["authentication-results", "x-spam-status"];
authenticated_headers = ["authentication-results"];
}
replies {
action = "no action";
}
url_reputation {
enabled = true;
}
phishing {
openphish_enabled = true;
# too much memory
#phishtank_enabled = true;
}
neural {
enabled = true;
}
neural_group {
symbols = {
"NEURAL_SPAM" {
weight = 3.0; # sample weight
description = "Neural network spam";
}
"NEURAL_HAM" {
weight = -3.0; # sample weight
description = "Neural network ham";
}
}
}
'';
sieve-spam-filter = pkgs.callPackage ../pkgs/sieve-spam-filter { };
in
{
services.rspamd = {
enable = true;
extraConfig = ''
.include(priority=1,duplicate=merge) "${localConfig}"
'';
postfix.enable = true;
workers.controller = {
extraConfig = ''
count = 1;
static_dir = "''${WWWDIR}";
password = "$2$7rb4gnnw8qbcy3x3m7au8c4mezecfjim$da4ahtt3gnjtbj7ni6bt1q8jwgqtzxp5ck6941m6prjxsz3udfgb";
enable_password = "$2$xo1qdd1zgozwto8yazr1o35zbarbzcgp$u8mx6hcsb1qdscejb4zadcb3iucmm4mw6btgmim9h6e5d8cpy5ib";
'';
};
};
services.dovecot2 = {
mailboxes.Spam = {
auto = "subscribe";
specialUse = "Junk";
};
extraConfig = ''
protocol imap {
mail_plugins = $mail_plugins imap_sieve
}
plugin {
sieve_plugins = sieve_imapsieve sieve_extprograms
# From elsewhere to Spam folder
imapsieve_mailbox1_name = Spam
imapsieve_mailbox1_causes = COPY
imapsieve_mailbox1_before = file:/var/lib/dovecot/sieve/report-spam.sieve
# From Spam folder to elsewhere
imapsieve_mailbox2_name = *
imapsieve_mailbox2_from = Spam
imapsieve_mailbox2_causes = COPY
imapsieve_mailbox2_before = file:/var/lib/dovecot/sieve/report-ham.sieve
# Move Spam emails to Spam folder
sieve_before = /var/lib/dovecot/sieve/move-to-spam.sieve
sieve_pipe_bin_dir = ${sieve-spam-filter}/bin
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment
}
'';
};
services.nginx.enable = true;
services.nginx.virtualHosts."rspamd.${domain}" = {
forceSSL = true;
enableACME = true;
acmeRoot = null;
locations."/".extraConfig = ''
proxy_pass http://localhost:11334;
'';
};
# systemd.services.rspamd.serviceConfig.SupplementaryGroups = [ "redis-rspamd" ];
systemd.services.dovecot2.preStart = ''
mkdir -p /var/lib/dovecot/sieve/
for i in ${sieve-spam-filter}/share/sieve-rspamd-filter/*.sieve; do
dest="/var/lib/dovecot/sieve/$(basename $i)"
cp "$i" "$dest"
${pkgs.dovecot_pigeonhole}/bin/sievec "$dest"
done
chown -R "${config.services.dovecot2.mailUser}:${config.services.dovecot2.mailGroup}" /var/lib/dovecot/sieve
'';
}