Start dovecot before postfix and add target for certificates

It seemed weird to me that preStart on postfix was used to generate
files not needed directly by postfix and for the self-signed
certificate which is also needed by dovecot. nginx.service was also
used as a proxy for when ACME certificate generation was done.

So I have created mailserver-certificates.target for when certificates
are available for other services. For self-signed that means that a
new oneshot service called mailserver-selfsigned-certificate has been
run. And for ACME this means that the target
acme-selfsigned-certificates has been reached (which is when acme has
created the self-signed certificates used before the actual
certificates provided by LetsEncrypt are created). This setup has the
added bonus that if you want to run a service to provide your own
certificates you can set that to run before
mailserver-certificates.target.

DH Parameters are only needed by dovecot so generation of that file has
been moved to the dovecot2 preStart.

And lastly the only remaining reason to for dovecot to start before
postfix was that the auth and lmtp sockets where located in a directory
created by postfix. But since they could just as well be located in
/run/dovecot2 as long as postfix has access to them I have moved them
there.
This commit is contained in:
Brian Olsen 2018-05-04 16:52:58 +02:00 committed by Ruben Maher
parent 0fbfbafb6e
commit 8a27b941bf
3 changed files with 78 additions and 56 deletions

View file

@ -84,7 +84,7 @@ in
''} ''}
service lmtp { service lmtp {
unix_listener /var/lib/postfix/queue/private/dovecot-lmtp { unix_listener dovecot-lmtp {
group = ${postfixCfg.group} group = ${postfixCfg.group}
mode = 0600 mode = 0600
user = ${postfixCfg.user} user = ${postfixCfg.user}
@ -106,7 +106,7 @@ in
} }
service auth { service auth {
unix_listener /var/lib/postfix/queue/private/auth { unix_listener auth {
mode = 0660 mode = 0660
user = ${postfixCfg.user} user = ${postfixCfg.user}
group = ${postfixCfg.group} group = ${postfixCfg.group}

View file

@ -121,11 +121,11 @@ in
virtual_mailbox_domains = ${vhosts_file} virtual_mailbox_domains = ${vhosts_file}
virtual_mailbox_maps = hash:/var/lib/postfix/conf/valias virtual_mailbox_maps = hash:/var/lib/postfix/conf/valias
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:/run/dovecot2/dovecot-lmtp
# sasl with dovecot # sasl with dovecot
smtpd_sasl_type = dovecot smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth smtpd_sasl_path = /run/dovecot2/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
@ -158,7 +158,7 @@ in
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 = "/run/dovecot2/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";

View file

@ -19,37 +19,6 @@
let let
cfg = config.mailserver; cfg = config.mailserver;
create_certificate = if cfg.certificateScheme == 2 then
''
# Create certificates if they do not exist yet
dir="${cfg.certificateDirectory}"
fqdn="${cfg.fqdn}"
case $fqdn in /*) fqdn=$(cat "$fqdn");; esac
key="''${dir}/key-${cfg.fqdn}.pem";
cert="''${dir}/cert-${cfg.fqdn}.pem";
if [ ! -f "''${key}" ] || [ ! -f "''${cert}" ]
then
mkdir -p "${cfg.certificateDirectory}"
(umask 077; "${pkgs.openssl}/bin/openssl" genrsa -out "''${key}" 2048) &&
"${pkgs.openssl}/bin/openssl" req -new -key "''${key}" -x509 -subj "/CN=''${fqdn}" \
-days 3650 -out "''${cert}"
fi
''
else "";
createDhParameterFile =
''
# Create a dh parameter file
if [ ! -s "${cfg.certificateDirectory}/dh.pem" ]
then
mkdir -p "${cfg.certificateDirectory}"
${pkgs.openssl}/bin/openssl \
dhparam ${builtins.toString cfg.dhParamBitLength} \
> "${cfg.certificateDirectory}/dh.pem"
fi
'';
createDomainDkimCert = dom: createDomainDkimCert = dom:
let let
dkim_key = "${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.key"; dkim_key = "${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.key";
@ -76,34 +45,87 @@ let
chown -R rmilter:rmilter "${cfg.dkimKeyDirectory}" chown -R rmilter:rmilter "${cfg.dkimKeyDirectory}"
''; '';
createDhParameterFile = let
dovecotVersion = builtins.fromJSON
(builtins.readFile (pkgs.callPackage ./dovecot-version.nix {}));
in lib.optionalString
(dovecotVersion.major == 2 && dovecotVersion.minor >= 3)
''
# Create a dh parameter file
if [ ! -s "${cfg.certificateDirectory}/dh.pem" ]
then
mkdir -p "${cfg.certificateDirectory}"
${pkgs.openssl}/bin/openssl \
dhparam ${builtins.toString cfg.dhParamBitLength} \
> "${cfg.certificateDirectory}/dh.pem"
fi
'';
preliminarySelfsigned = config.security.acme.preliminarySelfsigned;
acmeWantsTarget = [ "acme-certificates.target" ]
++ (lib.optional preliminarySelfsigned "acme-selfsigned-certificates.target");
acmeAfterTarget = if preliminarySelfsigned
then [ "acme-selfsigned-certificates.target" ]
else [ "acme-certificates.target" ];
in in
{ {
config = with cfg; lib.mkIf enable { config = with cfg; lib.mkIf enable {
# Make sure postfix gets started first, so that the certificates are in place # Add target for when certificates are available
systemd.services.dovecot2.after = [ "postfix.service" ]; systemd.targets."mailserver-certificates" = {
wants = lib.mkIf (cfg.certificateScheme == 3) acmeWantsTarget;
after = lib.mkIf (cfg.certificateScheme == 3) acmeAfterTarget;
};
# Create certificates and maildir folder # Create self signed certificate
systemd.services.postfix = { systemd.services.mailserver-selfsigned-certificate = lib.mkIf (cfg.certificateScheme == 2) {
after = (if (certificateScheme == 3) then [ "nginx.service" ] else []); wantedBy = [ "mailserver-certificates.target" ];
preStart = after = [ "local-fs.target" ];
'' before = [ "mailserver-certificates.target" ];
script = ''
# Create certificates if they do not exist yet
dir="${cfg.certificateDirectory}"
fqdn="${cfg.fqdn}"
case $fqdn in /*) fqdn=$(cat "$fqdn");; esac
key="''${dir}/key-${cfg.fqdn}.pem";
cert="''${dir}/cert-${cfg.fqdn}.pem";
if [ ! -f "''${key}" ] || [ ! -f "''${cert}" ]
then
mkdir -p "${cfg.certificateDirectory}"
(umask 077; "${pkgs.openssl}/bin/openssl" genrsa -out "''${key}" 2048) &&
"${pkgs.openssl}/bin/openssl" req -new -key "''${key}" -x509 -subj "/CN=''${fqdn}" \
-days 3650 -out "''${cert}"
fi
'';
serviceConfig = {
Type = "oneshot";
PrivateTmp = true;
};
};
# Create maildir folder and dh parameters before dovecot startup
systemd.services.dovecot2 = {
after = [ "mailserver-certificates.target" ];
wants = [ "mailserver-certificates.target" ];
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 "${mailDirectory}" mkdir -p "${mailDirectory}"
chgrp "${vmailGroupName}" "${mailDirectory}" chgrp "${vmailGroupName}" "${mailDirectory}"
chmod 02770 "${mailDirectory}" chmod 02770 "${mailDirectory}"
${create_certificate} ${createDhParameterFile}
${let
dovecotVersion = builtins.fromJSON
(builtins.readFile (pkgs.callPackage ./dovecot-version.nix {}));
in lib.optionalString
(dovecotVersion.major == 2 && dovecotVersion.minor >= 3)
createDhParameterFile}
''; '';
}; };
# Postfix requires rmilter socket, dovecot lmtp socket, dovecot auth socket and certificate to work
systemd.services.postfix = {
after = [ "rmilter.socket" "dovecot2.service" "mailserver-certificates.target" ];
wants = [ "mailserver-certificates.target" ];
requires = [ "rmilter.socket" "dovecot2.service" ];
};
# Create dkim certificates # Create dkim certificates
systemd.services.rmilter = { systemd.services.rmilter = {
requires = [ "rmilter.socket" ]; requires = [ "rmilter.socket" ];