Merge branch 'module-rewrite'

This commit is contained in:
Robin Raymond 2017-09-03 15:31:37 +02:00
commit 1e0c203bf8
13 changed files with 533 additions and 366 deletions

260
default.nix Normal file
View file

@ -0,0 +1,260 @@
# nixos-mailserver: a simple mail server
# Copyright (C) 2016-2017 Robin Raymond
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.mailserver;
in
{
options.mailserver = {
enable = mkEnableOption "nixos-mailserver";
domain = mkOption {
type = types.str;
example = "example.com";
description = "The domain that this mail server serves. So far only one domain is supported";
};
hostPrefix = mkOption {
type = types.str;
default = "mail";
description = ''
The prefix of the FQDN of the server. In this example the FQDN of the server
is given by 'mail.example.com'
'';
};
loginAccounts = mkOption {
type = types.loaOf (types.submodule ({ name, ... }: {
options = {
name = mkOption {
type = types.str;
example = "user1";
description = "Username";
};
hashedPassword = mkOption {
type = types.str;
example = "$6$evQJs5CFQyPAW09S$Cn99Y8.QjZ2IBnSu4qf1vBxDRWkaIZWOtmu1Ddsm3.H3CFpeVc0JU4llIq8HQXgeatvYhh5O33eWG3TSpjzu6/";
description = ''
Hashed password. Use `mkpasswd` as follows
```
mkpasswd -m sha-512 "super secret password"
```
'';
};
};
config.name = mkDefault name;
}));
example = {
user1 = {
hashedPassword = "$6$evQJs5CFQyPAW09S$Cn99Y8.QjZ2IBnSu4qf1vBxDRWkaIZWOtmu1Ddsm3.H3CFpeVc0JU4llIq8HQXgeatvYhh5O33eWG3TSpjzu6/";
};
user2 = {
hashedPassword = "$6$oE0ZNv2n7Vk9gOf$9xcZWCCLGdMflIfuA0vR1Q1Xblw6RZqPrP94mEit2/81/7AKj2bqUai5yPyWE.QYPyv6wLMHZvjw3Rlg7yTCD/";
};
};
description = ''
The login account of the domain. Every account is mapped to a unix user,
e.g. `user1@example.com`. To generate the passwords use `mkpasswd` as
follows
```
mkpasswd -m sha-512 "super secret password"
```
'';
default = {};
};
virtualAliases = mkOption {
type = types.attrsOf (types.enum (builtins.attrNames cfg.loginAccounts));
example = {
info = "user1";
postmaster = "user1";
abuse = "user1";
};
description = ''
Virtual Aliases. A virtual alias `info = "user1"` means that
all mail to `info@example.com` is forwarded to `user1@example.com`. Note
that it is expected that `postmaster@example.com` and `abuse@example.com` is
forwarded to some valid email address. (Alternatively you can create login
accounts for `postmaster` and (or) `abuse`).
'';
default = {};
};
vmailUIDStart = mkOption {
type = types.int;
default = 5000;
description = ''
The unix UID where the loginAccounts are created. 5000 means that the first
user will get 5000, the second 5001, ...
'';
};
vmailUserName = mkOption {
type = types.str;
default = "vmail";
description = ''
The user name and group name of the user that owns the directory where all
the mail is stored.
'';
};
vmailGroupName = mkOption {
type = types.str;
default = "vmail";
description = ''
The user name and group name of the user that owns the directory where all
the mail is stored.
'';
};
mailDirectory = mkOption {
type = types.string;
default = "/var/vmail";
description = ''
Where to store the mail.
'';
};
certificateScheme = mkOption {
type = types.enum [ 1 2 ];
default = 2;
description = ''
Certificate Files. There are three options for these.
1) You specify locations and manually copy certificates there.
2) You let the server create new (self signed) certificates on the fly.
3) You let the server create a certificate via `Let's Encrypt`. Note that
this implies that a stripped down webserver has to be started. This also
implies that the FQDN must be set as an `A` record to point to the IP of
the server. TODO: Explain more details
TODO: Only certificate scheme 1) and 2) work as of yet.
'';
};
certificateFile = mkOption {
type = types.path;
example = "/root/mail-server.crt";
description = ''
Scheme 1)
Location of the certificate
'';
};
keyFile = mkOption {
type = types.path;
example = "/root/mail-server.key";
description = ''
Scheme 1)
Location of the key file
'';
};
certificateDirectory = mkOption {
type = types.path;
default = "/var/certs";
description = ''
Sceme 2)
This is the folder where the certificate will be created. The name is
hardcoded to "cert-<domain>.pem" and "key-<domain>.pem" and the
certificate is valid for 10 years.
'';
};
enableImap = mkOption {
type = types.bool;
default = true;
description = ''
Whether to enable imap / pop3. Both variants are only supported in the
(sane) startTLS configuration. (TODO: Allow SSL ports). The ports are
110 - Pop3
143 - IMAP
587 - SMTP with login
'';
};
enablePop3 = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable POP3. Both variants are only supported in the
(sane) startTLS configuration. (TODO: Allow SSL ports). The ports are
110 - Pop3
143 - IMAP
587 - SMTP with login
'';
};
# imapSsl = mkOption {} #< TODO
# pop3Ssl = mkOption {} #< TODO
virusScanning = mkOption {
type = types.bool;
default = false;
description = ''
Whether to activate virus scanning. Note that virus scanning is _very_
expensive memory wise.
'';
};
dkimSigning = mkOption {
type = types.bool;
default = true;
description = ''
Whether to activate dkim signing.
TODO: Explain how to put signature into domain record
'';
};
dkimSelector = mkOption {
type = types.string;
default = "mail";
description = ''
'';
};
dkimKeyDirectory = mkOption {
type = types.path;
default = "/var/dkim";
description = ''
'';
};
};
imports = [
./mail-server/clamav.nix
./mail-server/users.nix
./mail-server/environment.nix
./mail-server/networking.nix
./mail-server/systemd.nix
./mail-server/dovecot.nix
./mail-server/postfix.nix
./mail-server/rmilter.nix
];
}

View file

@ -1,161 +0,0 @@
# nixos-mailserver: a simple mail server
# Copyright (C) 2016-2017 Robin Raymond
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
{ config, pkgs, ... }:
let
#
# The domain that this mail server serves. So far only one domain is supported
#
domain = "example.com";
#
# The prefix of the FQDN of the server. In this example the FQDN of the server
# is given by 'mail.example.com'
#
host_prefix = "mail";
#
# The login account of the domain. Every account is mapped to a unix user,
# e.g. `user1@example.com`. To generate the passwords use `mkpasswd` as
# follows
#
# ```
# mkpasswd -m sha-512 "super secret password"
# ```
#
login_accounts = [
{ name = "user1";
password = "$6$evQJs5CFQyPAW09S$Cn99Y8.QjZ2IBnSu4qf1vBxDRWkaIZWOtmu1Ddsm3.H3CFpeVc0JU4llIq8HQXgeatvYhh5O33eWG3TSpjzu6/";
}
{ name = "user2";
password = "$6$oE0ZNv2n7Vk9gOf$9xcZWCCLGdMflIfuA0vR1Q1Xblw6RZqPrP94mEit2/81/7AKj2bqUai5yPyWE.QYPyv6wLMHZvjw3Rlg7yTCD/";
}
];
#
# Virtual Aliases. A virtual alias { from = "info"; to = "user1"; } means that
# all mail to `info@example.com` is forwarded to `user1@example.com`. Note
# that it is expected that `postmaster@example.com` and `abuse@example.com` is
# forwarded to some valid email address. (Alternatively you can create login
# accounts for `postmaster` and (or) `abuse`).
#
valiases = [
{ from = "info";
to = "user1";
}
{ from = "postmaster";
to = "user1";
}
{ from = "abuse";
to = "user1";
}
];
#
# The unix UID where the login_accounts are created. 5000 means that the first
# user will get 5000, the second 5001, ...
#
vmail_id_start = 5000;
#
# The user name and group name of the user that owns the directory where all
# the mail is stored.
#
vmail_user_name = "vmail";
vmail_group_name = "vmail";
#
# Where to store the mail.
#
mail_dir = "/var/vmail";
#
# Certificate Files. There are three options for these.
#
# 1) You specify locations and manually copy certificates there.
# 2) You let the server create new (self signed) certificates on the fly.
# 3) You let the server create a certificate via `Let's Encrypt`. Note that
# this implies that a stripped down webserver has to be started. This also
# implies that the FQDN must be set as an `A` record to point to the IP of
# the server. TODO: Explain more details
#
# TODO: Only certificate scheme 1) and 2) work as of yet.
certificate_scheme = 2;
# Sceme 1)
cert_file = "/root/mail-server.crt";
key_file = "/root/mail-server.key";
# Sceme 2)
# This is the folder where the certificate will be created. The name is
# hardcoded to "cert-${domain}.pem" and "key-${domain}.pem" and the
# certificate is valid for 10 years.
cert_dir = "/var/certs";
#
# Whether to enable imap / pop3. Both variants are only supported in the
# (sane) startTLS configuration. (TODO: Allow SSL ports). The ports are
#
# 110 - Pop3
# 143 - IMAP
# 587 - SMTP with login
#
enable_imap = true;
enable_pop3 = false;
# imap_ssl = false; #< TODO
# pop3_ssl = false; #< TODO
#
# Whether to activate virus scanning. Note that virus scanning is _very_
# expensive memory wise.
#
virus_scanning = false;
#
# Whether to activate dkim signing.
# TODO: Explain how to put signature into domain record
#
dkim_signing = true;
dkim_selector = "mail";
dkim_dir = "/var/dkim";
in
{
services = import ./mail-server/services.nix {
inherit mail_dir vmail_user_name vmail_group_name valiases domain
enable_imap enable_pop3 virus_scanning dkim_signing dkim_selector
dkim_dir certificate_scheme cert_file key_file cert_dir;
};
environment = import ./mail-server/environment.nix {
inherit pkgs certificate_scheme;
};
networking = import ./mail-server/networking.nix {
inherit domain host_prefix enable_imap enable_pop3;
};
systemd = import ./mail-server/systemd.nix {
inherit mail_dir vmail_group_name certificate_scheme cert_dir host_prefix
domain pkgs dkim_selector dkim_dir;
};
users = import ./mail-server/users.nix {
inherit vmail_id_start vmail_user_name vmail_group_name domain mail_dir
login_accounts;
};
}

View file

@ -14,10 +14,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/> # along with this program. If not, see <http://www.gnu.org/licenses/>
{ virus_scanning }: { config, pkgs, lib, ... }:
let
cfg = config.mailserver;
in
{ {
daemon.enable = virus_scanning; config = lib.mkIf cfg.virusScanning {
updater.enable = virus_scanning; services.clamav.daemon.enable = true;
services.clamav.updater.enable = true;
};
} }

36
mail-server/common.nix Normal file
View file

@ -0,0 +1,36 @@
# nixos-mailserver: a simple mail server
# Copyright (C) 2016-2017 Robin Raymond
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
{ config }:
let
cfg = config.mailserver;
in
{
# cert :: PATH
certificatePath = if cfg.certificateScheme == 1
then cfg.certificateFile
else if cfg.certificateScheme == 2
then "${cfg.certificateDirectory}/cert-${cfg.domain}.pem"
else "";
# key :: PATH
keyPath = if cfg.certificateScheme == 1
then cfg.keyFile
else if cfg.certificateScheme == 2
then "${cfg.certificateDirectory}/key-${cfg.domain}.pem"
else "";
}

View file

@ -14,73 +14,80 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/> # along with this program. If not, see <http://www.gnu.org/licenses/>
{ vmail_group_name, vmail_user_name, mail_dir, enable_imap, enable_pop3, cert, { config, pkgs, lib, ... }:
key }:
with (import ./common.nix { inherit config; });
let let
cfg = config.mailserver;
# maildir in format "/${domain}/${user}/" # maildir in format "/${domain}/${user}/"
dovecot_maildir = "maildir:${mail_dir}/%d/%n/"; dovecot_maildir = "maildir:${cfg.mailDirectory}/%d/%n/";
in in
{ {
enable = true; config = with cfg; lib.mkIf enable {
enableImap = enable_imap; services.dovecot2 = {
enablePop3 = enable_pop3; enable = true;
mailGroup = vmail_group_name; enableImap = enableImap;
mailUser = vmail_user_name; enablePop3 = enablePop3;
mailLocation = dovecot_maildir; mailGroup = vmailGroupName;
sslServerCert = cert; mailUser = vmailUserName;
sslServerKey = key; mailLocation = dovecot_maildir;
enableLmtp = true; sslServerCert = certificatePath;
extraConfig = '' sslServerKey = keyPath;
#Extra Config enableLmtp = true;
mail_access_groups = ${vmail_group_name} extraConfig = ''
ssl = required #Extra Config
mail_access_groups = ${vmailGroupName}
ssl = required
service lmtp { service lmtp {
unix_listener /var/lib/postfix/queue/private/dovecot-lmtp { unix_listener /var/lib/postfix/queue/private/dovecot-lmtp {
group = postfix group = postfix
mode = 0600 mode = 0600
user = postfix # TODO: < make variable user = postfix # TODO: < make variable
} }
} }
service auth { service auth {
unix_listener /var/lib/postfix/queue/private/auth { unix_listener /var/lib/postfix/queue/private/auth {
mode = 0660 mode = 0660
user = postfix # TODO: < make variable user = postfix # TODO: < make variable
group = postfix # TODO: < make variable group = postfix # TODO: < make variable
} }
} }
auth_mechanisms = plain login auth_mechanisms = plain login
namespace inbox { namespace inbox {
#prefix = INBOX. #prefix = INBOX.
# the namespace prefix isn't added again to the mailbox names. # the namespace prefix isn't added again to the mailbox names.
inbox = yes inbox = yes
# ... # ...
mailbox "Trash" { mailbox "Trash" {
auto = no auto = no
special_use = \Trash special_use = \Trash
} }
mailbox "Junk" { mailbox "Junk" {
auto = subscribe auto = subscribe
special_use = \Junk special_use = \Junk
} }
mailbox "Drafts" { mailbox "Drafts" {
auto = subscribe auto = subscribe
special_use = \Drafts special_use = \Drafts
} }
mailbox "Sent" { mailbox "Sent" {
auto = subscribe auto = subscribe
special_use = \Sent special_use = \Sent
} }
} }
''; '';
};
};
} }

View file

@ -14,10 +14,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/> # along with this program. If not, see <http://www.gnu.org/licenses/>
{ pkgs, certificate_scheme }: { config, pkgs, lib, ... }:
let
cfg = config.mailserver;
in
{ {
systemPackages = with pkgs; [ config = with cfg; lib.mkIf enable {
dovecot opendkim openssh postfix clamav rspamd rmilter environment.systemPackages = with pkgs; [
] ++ (if certificate_scheme == 2 then [ openssl ] else []); dovecot opendkim openssh postfix clamav rspamd rmilter
] ++ (if certificateScheme == 2 then [ openssl ] else []);
};
} }

View file

@ -14,14 +14,20 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/> # along with this program. If not, see <http://www.gnu.org/licenses/>
{ domain, host_prefix, enable_imap, enable_pop3 }: { config, pkgs, lib, ... }:
let
cfg = config.mailserver;
in
{ {
hostName = "${host_prefix}.${domain}"; config = with cfg; lib.mkIf enable {
firewall = { networking.hostName = "${hostPrefix}.${domain}";
allowedTCPPorts = [ 25 587 ]
++ (if enable_imap then [ 143 ] else []) networking.firewall = {
++ (if enable_pop3 then [ 110 ] else []); allowedTCPPorts = [ 25 587 ]
++ (if enableImap then [ 143 ] else [])
++ (if enablePop3 then [ 110 ] else []);
};
}; };
} }

View file

@ -14,25 +14,25 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/> # along with this program. If not, see <http://www.gnu.org/licenses/>
{ mail_dir, domain, valiases, cert, key }: { config, pkgs, lib, ... }:
with (import ./common.nix { inherit config; });
let let
# valiasToString :: { from = "..."; to = "..." } -> String cfg = config.mailserver;
valiasToString = x: "${x.from}@${domain} ${x.to}@${domain}\n";
# valiases_postfix :: [ String ] # valiases_postfix :: [ String ]
valiases_postfix = map valiasToString valiases; valiases_postfix = map
(from:
# concatString :: [ String ] -> String let to = cfg.virtualAliases.${from};
concatString = l: if l == [] in "${from}@${cfg.domain} ${to}@${cfg.domain}")
then "" (builtins.attrNames cfg.virtualAliases);
else (builtins.head l) + (concatString (builtins.tail l));
# valiases_file :: Path # valiases_file :: Path
valiases_file = builtins.toFile "valias" (concatString valiases_postfix); valiases_file = builtins.toFile "valias" (lib.concatStringsSep "\n" valiases_postfix);
# vhosts_file :: Path # vhosts_file :: Path
vhosts_file = builtins.toFile "vhosts" domain; vhosts_file = builtins.toFile "vhosts" cfg.domain;
# vaccounts_file :: Path # vaccounts_file :: Path
# see # see
@ -43,50 +43,54 @@ let
in in
{ {
enable = true; config = with cfg; lib.mkIf enable {
networksStyle = "host";
mapFiles."valias" = valiases_file;
mapFiles."vaccounts" = vaccounts_file;
sslCert = cert;
sslKey = key;
enableSubmission = true;
extraConfig = services.postfix = {
'' enable = true;
networksStyle = "host";
mapFiles."valias" = valiases_file;
mapFiles."vaccounts" = vaccounts_file;
sslCert = certificatePath;
sslKey = keyPath;
enableSubmission = true;
# Extra Config extraConfig =
''
# Extra Config
smtpd_banner = $myhostname ESMTP NO UCE smtpd_banner = $myhostname ESMTP NO UCE
smtpd_tls_auth_only = yes smtpd_tls_auth_only = yes
disable_vrfy_command = yes disable_vrfy_command = yes
message_size_limit = 20971520 message_size_limit = 20971520
# virtual mail system # virtual mail system
virtual_uid_maps = static:5000 virtual_uid_maps = static:5000
virtual_gid_maps = static:5000 virtual_gid_maps = static:5000
virtual_mailbox_base = ${mail_dir} virtual_mailbox_base = ${mailDirectory}
virtual_mailbox_domains = ${vhosts_file} virtual_mailbox_domains = ${vhosts_file}
virtual_alias_maps = hash:/var/lib/postfix/conf/valias virtual_alias_maps = hash:/var/lib/postfix/conf/valias
virtual_transport = lmtp:unix:private/dovecot-lmtp virtual_transport = lmtp:unix:private/dovecot-lmtp
# sasl with dovecot # sasl with dovecot
smtpd_sasl_type = dovecot smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes smtpd_sasl_auth_enable = yes
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
''; '';
submissionOptions = submissionOptions =
{ {
smtpd_tls_security_level = "encrypt"; smtpd_tls_security_level = "encrypt";
smtpd_sasl_auth_enable = "yes"; smtpd_sasl_auth_enable = "yes";
smtpd_sasl_type = "dovecot"; smtpd_sasl_type = "dovecot";
smtpd_sasl_path = "private/auth"; smtpd_sasl_path = "private/auth";
smtpd_sasl_security_options = "noanonymous"; smtpd_sasl_security_options = "noanonymous";
smtpd_sasl_local_domain = "$myhostname"; smtpd_sasl_local_domain = "$myhostname";
smtpd_client_restrictions = "permit_sasl_authenticated,reject"; smtpd_client_restrictions = "permit_sasl_authenticated,reject";
smtpd_sender_login_maps = "hash:/etc/postfix/vaccounts"; smtpd_sender_login_maps = "hash:/etc/postfix/vaccounts";
smtpd_sender_restrictions = "reject_sender_login_mismatch"; smtpd_sender_restrictions = "reject_sender_login_mismatch";
smtpd_recipient_restrictions = "reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject"; smtpd_recipient_restrictions = "reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject";
};
};
}; };
} }

View file

@ -14,10 +14,12 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/> # along with this program. If not, see <http://www.gnu.org/licenses/>
{ domain, virus_scanning, dkim_signing, dkim_dir, dkim_selector }: { config, pkgs, lib, ... }:
let let
clamav = if virus_scanning cfg = config.mailserver;
clamav = if cfg.virusScanning
then then
'' ''
clamav { clamav {
@ -25,14 +27,14 @@ let
}; };
'' ''
else ""; else "";
dkim = if dkim_signing dkim = if cfg.dkimSigning
then then
'' ''
dkim { dkim {
domain { domain {
key = "${dkim_dir}"; key = "${cfg.dkimKeyDirectory}";
domain = "*"; domain = "*";
selector = "${dkim_selector}"; selector = "${cfg.dkimSelector}";
}; };
sign_alg = sha256; sign_alg = sha256;
auth_only = yes; auth_only = yes;
@ -41,15 +43,19 @@ let
else ""; else "";
in in
{ {
enable = true; config = with cfg; lib.mkIf enable {
# debug = true; services.rmilter = {
postfix.enable = true; enable = true;
rspamd.enable = true; #debug = true;
extraConfig = postfix.enable = true;
'' rspamd.enable = true;
${clamav} extraConfig =
''
${clamav}
${dkim} ${dkim}
''; '';
};
};
} }

View file

@ -14,45 +14,31 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/> # along with this program. If not, see <http://www.gnu.org/licenses/>
{ mail_dir, vmail_user_name, vmail_group_name, valiases, domain, enable_imap,
enable_pop3, virus_scanning, dkim_signing, dkim_selector, dkim_dir, { config, pkgs, lib, ... }:
certificate_scheme, cert_file, key_file, cert_dir }:
let let
cfg = config.mailserver;
# cert :: PATH # cert :: PATH
cert = if certificate_scheme == 1 cert = if cfg.certificateScheme == 1
then cert_file then cfg.certificateFile
else if certificate_scheme == 2 else if cfg.certificateScheme == 2
then "${cert_dir}/cert-${domain}.pem" then "${cfg.certificateDirectory}/cert-${cfg.domain}.pem"
else ""; else "";
# key :: PATH # key :: PATH
key = if certificate_scheme == 1 key = if cfg.certificateScheme == 1
then key_file then cfg.keyFile
else if certificate_scheme == 2 else if cfg.certificateScheme == 2
then "${cert_dir}/key-${domain}.pem" then "${cfg.certificateDirectory}/key-${cfg.domain}.pem"
else ""; else "";
in in
{ {
# rspamd
rspamd = { imports = [
enable = true; ./rmilter.nix
}; ./postfix.nix key
./dovecot.nix
rmilter = import ./rmilter.nix { ];
inherit domain virus_scanning dkim_signing dkim_selector dkim_dir;
};
postfix = import ./postfix.nix {
inherit mail_dir domain valiases cert key;
};
dovecot2 = import ./dovecot.nix {
inherit vmail_group_name vmail_user_name mail_dir enable_imap
enable_pop3 cert key;
};
clamav = import ./clamav.nix {
inherit virus_scanning;
};
} }

View file

@ -14,22 +14,23 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/> # along with this program. If not, see <http://www.gnu.org/licenses/>
{ pkgs, mail_dir, vmail_group_name, certificate_scheme, cert_dir, host_prefix, { config, pkgs, lib, ... }:
domain, dkim_selector, dkim_dir}:
let let
create_certificate = if certificate_scheme == 2 then cfg = config.mailserver;
create_certificate = if cfg.certificateScheme == 2 then
'' ''
# Create certificates if they do not exist yet # Create certificates if they do not exist yet
dir="${cert_dir}" dir="${cfg.certificateDirectory}"
fqdn="${host_prefix}.${domain}" fqdn="${cfg.hostPrefix}.${cfg.domain}"
case $fqdn in /*) fqdn=$(cat "$fqdn");; esac case $fqdn in /*) fqdn=$(cat "$fqdn");; esac
key="''${dir}/key-${domain}.pem"; key="''${dir}/key-${cfg.domain}.pem";
cert="''${dir}/cert-${domain}.pem"; cert="''${dir}/cert-${cfg.domain}.pem";
if [ ! -f "''${key}" ] || [ ! -f "''${cert}" ] if [ ! -f "''${key}" ] || [ ! -f "''${cert}" ]
then then
mkdir -p "${cert_dir}" mkdir -p "${cfg.certificateDirectory}"
(umask 077; "${pkgs.openssl}/bin/openssl" genrsa -out "''${key}" 2048) && (umask 077; "${pkgs.openssl}/bin/openssl" genrsa -out "''${key}" 2048) &&
"${pkgs.openssl}/bin/openssl" req -new -key "''${key}" -x509 -subj "/CN=''${fqdn}" \ "${pkgs.openssl}/bin/openssl" req -new -key "''${key}" -x509 -subj "/CN=''${fqdn}" \
-days 3650 -out "''${cert}" -days 3650 -out "''${cert}"
@ -37,47 +38,50 @@ let
'' ''
else ""; else "";
dkim_key = "${dkim_dir}/${dkim_selector}.private"; dkim_key = "${cfg.dkimKeyDirectory}/${cfg.dkimSelector}.private";
dkim_txt = "${dkim_dir}/${dkim_selector}.txt"; dkim_txt = "${cfg.dkimKeyDirectory}/${cfg.dkimSelector}.txt";
create_dkim_cert = create_dkim_cert =
'' ''
# Create dkim dir # Create dkim dir
mkdir -p "${dkim_dir}" mkdir -p "${cfg.dkimKeyDirectory}"
chown rmilter:rmilter "${dkim_dir}" chown rmilter:rmilter "${cfg.dkimKeyDirectory}"
if [ ! -f "${dkim_key}" ] || [ ! -f "${dkim_txt}" ] if [ ! -f "${dkim_key}" ] || [ ! -f "${dkim_txt}" ]
then then
${pkgs.opendkim}/bin/opendkim-genkey -s "${dkim_selector}" \ ${pkgs.opendkim}/bin/opendkim-genkey -s "${cfg.dkimSelector}" \
-d ${domain} \ -d ${cfg.domain} \
--directory="${dkim_dir}" --directory="${cfg.dkimKeyDirectory}"
chown rmilter:rmilter "${dkim_key}" chown rmilter:rmilter "${dkim_key}"
fi fi
''; '';
in in
{ {
# Make sure postfix gets started first, so that the certificates are in place config = with cfg; lib.mkIf enable {
services.dovecot2.after = [ "postfix.service" ]; # Make sure postfix gets started first, so that the certificates are in place
systemd.services.dovecot2.after = [ "postfix.service" ];
# Create certificates and maildir folder # Create certificates and maildir folder
services.postfix = { systemd.services.postfix = {
preStart = preStart =
'' ''
# Create mail directory and set permissions. See # Create mail directory and set permissions. See
# <http://wiki2.dovecot.org/SharedMailboxes/Permissions>. # <http://wiki2.dovecot.org/SharedMailboxes/Permissions>.
mkdir -p "${mail_dir}" mkdir -p "${mail_dir}"
chgrp "${vmail_group_name}" "${mail_dir}" chgrp "${vmail_group_name}" "${mail_dir}"
chmod 02770 "${mail_dir}" chmod 02770 "${mail_dir}"
${create_certificate} ${create_certificate}
''; '';
}; };
# Create dkim certificates
systemd.services.rmilter = {
preStart =
''
${create_dkim_cert}
'';
};
# Create dkim certificates
services.rmilter = {
preStart =
''
${create_dkim_cert}
'';
}; };
} }

View file

@ -14,37 +14,41 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/> # along with this program. If not, see <http://www.gnu.org/licenses/>
{ vmail_id_start, vmail_user_name, vmail_group_name, domain, mail_dir, { config, pkgs, lib, ... }:
login_accounts }:
with config.mailserver;
let let
vmail_user = [{ vmail_user = [{
name = vmail_user_name; name = vmailUserName;
isNormalUser = false; isNormalUser = false;
uid = vmail_id_start; uid = vmailUIDStart;
home = mail_dir; home = mailDirectory;
createHome = true; createHome = true;
group = vmail_group_name; group = vmailGroupName;
}]; }];
# accountsToUser :: String -> UserRecord # accountsToUser :: String -> UserRecord
accountsToUser = x: { accountsToUser = account: {
name = x.name + "@" + domain; name = account.name + "@" + domain;
isNormalUser = false; isNormalUser = false;
group = vmail_group_name; group = vmailGroupName;
hashedPassword = x.password; inherit (account) hashedPassword;
}; };
# mail_user :: [ UserRecord ] # mail_user :: [ UserRecord ]
mail_user = map accountsToUser login_accounts; mail_user = map accountsToUser (lib.attrValues loginAccounts);
in in
{ {
# set the vmail gid to a specific value
groups = {
vmail = { gid = vmail_id_start; };
};
# define all users config = lib.mkIf enable {
extraUsers = vmail_user ++ mail_user; # set the vmail gid to a specific value
users.groups = {
vmail = { gid = vmailUIDStart; };
};
# define all users
users.extraUsers = vmail_user ++ mail_user;
};
} }

View file

@ -5,7 +5,12 @@
{ config, pkgs, ... }: { config, pkgs, ... }:
{ {
imports = [ imports = [
./../mail-config.nix ./../default.nix
]; ];
mailserver = {
enable = true;
domain = "example.com";
};
}; };
} }