···235235 # https://github.com/NixOS/nixpkgs/pull/81371#issuecomment-605526099
236236 wantedBy = optionals (!config.boot.isContainer) [ "multi-user.target" ];
237237238238- path = with pkgs; [ lego coreutils diffutils ];
238238+ path = with pkgs; [ lego coreutils diffutils openssl ];
239239240240 serviceConfig = commonServiceConfig // {
241241 Group = data.group;
···274274 script = ''
275275 set -euxo pipefail
276276277277+ # This reimplements the expiration date check, but without querying
278278+ # the acme server first. By doing this offline, we avoid errors
279279+ # when the network or DNS are unavailable, which can happen during
280280+ # nixos-rebuild switch.
281281+ is_expiration_skippable() {
282282+ pem=$1
283283+284284+ # This function relies on set -e to exit early if any of the
285285+ # conditions or programs fail.
286286+287287+ [[ -e $pem ]]
288288+289289+ expiration_line="$(
290290+ set -euxo pipefail
291291+ openssl x509 -noout -enddate <$pem \
292292+ | grep notAfter \
293293+ | sed -e 's/^notAfter=//'
294294+ )"
295295+ [[ -n "$expiration_line" ]]
296296+297297+ expiration_date="$(date -d "$expiration_line" +%s)"
298298+ now="$(date +%s)"
299299+ expiration_s=$[expiration_date - now]
300300+ expiration_days=$[expiration_s / (3600 * 24)] # rounds down
301301+302302+ [[ $expiration_days -gt ${toString cfg.validMinDays} ]]
303303+ }
304304+277305 ${optionalString (data.webroot != null) ''
278306 # Ensure the webroot exists
279307 mkdir -p '${data.webroot}/.well-known/acme-challenge'
···288316 # When domains are updated, there's no need to do a full
289317 # Lego run, but it's likely renew won't work if days is too low.
290318 if [ -e certificates/domainhash.txt ] && cmp -s domainhash.txt certificates/domainhash.txt; then
291291- lego ${renewOpts} --days ${toString cfg.validMinDays}
319319+ if is_expiration_skippable out/full.pem; then
320320+ echo 1>&2 "nixos-acme: skipping renewal because expiration isn't within the coming ${toString cfg.validMinDays} days"
321321+ else
322322+ echo 1>&2 "nixos-acme: renewing now, because certificate expires within the configured ${toString cfg.validMinDays} days"
323323+ lego ${renewOpts} --days ${toString cfg.validMinDays}
324324+ fi
292325 else
326326+ echo 1>&2 "certificate domain(s) have changed; will renew now"
293327 # Any number > 90 works, but this one is over 9000 ;-)
294328 lego ${renewOpts} --days 9001
295329 fi