Merge pull request #31833 from pngwjpgh/feat/acme/delay

nixos/acme: Allow for time window between cert issue and activation
This commit is contained in:
Matthew Justin Bauer 2018-04-24 10:06:02 -05:00 committed by GitHub
commit 1b0a7bfe38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -58,9 +58,11 @@ let
default = "";
example = "systemctl reload nginx.service";
description = ''
Commands to run after certificates are re-issued. Typically
Commands to run after new certificates go live. Typically
the web server and other servers using certificates need to
be reloaded.
Executed in the same directory with the new certificate.
'';
};
@ -78,6 +80,27 @@ let
'';
};
activationDelay = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Systemd time span expression to delay copying new certificates to main
state directory. See <citerefentry><refentrytitle>systemd.time</refentrytitle>
<manvolnum>7</manvolnum></citerefentry>.
'';
};
preDelay = mkOption {
type = types.lines;
default = "";
description = ''
Commands to run after certificates are re-issued but before they are
activated. Typically the new certificate is published to DNS.
Executed in the same directory with the new certificate.
'';
};
extraDomains = mkOption {
type = types.attrsOf (types.nullOr types.str);
default = {};
@ -186,14 +209,15 @@ in
servicesLists = mapAttrsToList certToServices cfg.certs;
certToServices = cert: data:
let
cpath = "${cfg.directory}/${cert}";
domain = if data.domain != null then data.domain else cert;
cpath = lpath + optionalString (data.activationDelay != null) ".staging";
lpath = "${cfg.directory}/${cert}";
rights = if data.allowKeysForGroup then "750" else "700";
cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin ]
++ optionals (data.email != null) [ "--email" data.email ]
++ concatMap (p: [ "-f" p ]) data.plugins
++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains)
++ (if cfg.production then []
else ["--server" "https://acme-staging.api.letsencrypt.org/directory"]);
++ optionals (!cfg.production) ["--server" "https://acme-staging.api.letsencrypt.org/directory"];
acmeService = {
description = "Renew ACME Certificate for ${cert}";
after = [ "network.target" "network-online.target" ];
@ -206,7 +230,7 @@ in
Group = data.group;
PrivateTmp = true;
};
path = [ pkgs.simp_le ];
path = with pkgs; [ simp_le systemd ];
preStart = ''
mkdir -p '${cfg.directory}'
chown 'root:root' '${cfg.directory}'
@ -229,15 +253,36 @@ in
exit "$EXITCODE"
'';
postStop = ''
cd '${cpath}'
if [ -e /tmp/lastExitCode ] && [ "$(cat /tmp/lastExitCode)" = "0" ]; then
echo "Executing postRun hook..."
${data.postRun}
${if data.activationDelay != null then ''
${data.preDelay}
if [ -d '${lpath}' ]; then
systemd-run --no-block --on-active='${data.activationDelay}' --unit acme-setlive-${cert}.service
else
systemctl --wait start acme-setlive-${cert}.service
fi
'' else data.postRun}
fi
'';
before = [ "acme-certificates.target" ];
wantedBy = [ "acme-certificates.target" ];
};
delayService = {
description = "Set certificate for ${cert} live";
path = with pkgs; [ rsync ];
serviceConfig = {
Type = "oneshot";
};
script = ''
rsync -a --delete-after '${cpath}/' '${lpath}'
'';
postStop = data.postRun;
};
selfsignedService = {
description = "Create preliminary self-signed certificate for ${cert}";
path = [ pkgs.openssl ];
@ -302,11 +347,8 @@ in
};
in (
[ { name = "acme-${cert}"; value = acmeService; } ]
++
(if cfg.preliminarySelfsigned
then [ { name = "acme-selfsigned-${cert}"; value = selfsignedService; } ]
else []
)
++ optional cfg.preliminarySelfsigned { name = "acme-selfsigned-${cert}"; value = selfsignedService; }
++ optional (data.activationDelay != null) { name = "acme-setlive-${cert}"; value = delayService; }
);
servicesAttr = listToAttrs services;
injectServiceDep = {