Merge pull request #114752 from hercules-ci/lazy-offline-acme

acme: Determine offline whether renewal is due

authored by

Robert Hensing and committed by
GitHub
bef62e8c 9007023a

+36 -2
+36 -2
nixos/modules/security/acme.nix
··· 235 235 # https://github.com/NixOS/nixpkgs/pull/81371#issuecomment-605526099 236 236 wantedBy = optionals (!config.boot.isContainer) [ "multi-user.target" ]; 237 237 238 - path = with pkgs; [ lego coreutils diffutils ]; 238 + path = with pkgs; [ lego coreutils diffutils openssl ]; 239 239 240 240 serviceConfig = commonServiceConfig // { 241 241 Group = data.group; ··· 274 274 script = '' 275 275 set -euxo pipefail 276 276 277 + # This reimplements the expiration date check, but without querying 278 + # the acme server first. By doing this offline, we avoid errors 279 + # when the network or DNS are unavailable, which can happen during 280 + # nixos-rebuild switch. 281 + is_expiration_skippable() { 282 + pem=$1 283 + 284 + # This function relies on set -e to exit early if any of the 285 + # conditions or programs fail. 286 + 287 + [[ -e $pem ]] 288 + 289 + expiration_line="$( 290 + set -euxo pipefail 291 + openssl x509 -noout -enddate <$pem \ 292 + | grep notAfter \ 293 + | sed -e 's/^notAfter=//' 294 + )" 295 + [[ -n "$expiration_line" ]] 296 + 297 + expiration_date="$(date -d "$expiration_line" +%s)" 298 + now="$(date +%s)" 299 + expiration_s=$[expiration_date - now] 300 + expiration_days=$[expiration_s / (3600 * 24)] # rounds down 301 + 302 + [[ $expiration_days -gt ${toString cfg.validMinDays} ]] 303 + } 304 + 277 305 ${optionalString (data.webroot != null) '' 278 306 # Ensure the webroot exists 279 307 mkdir -p '${data.webroot}/.well-known/acme-challenge' ··· 288 316 # When domains are updated, there's no need to do a full 289 317 # Lego run, but it's likely renew won't work if days is too low. 290 318 if [ -e certificates/domainhash.txt ] && cmp -s domainhash.txt certificates/domainhash.txt; then 291 - lego ${renewOpts} --days ${toString cfg.validMinDays} 319 + if is_expiration_skippable out/full.pem; then 320 + echo 1>&2 "nixos-acme: skipping renewal because expiration isn't within the coming ${toString cfg.validMinDays} days" 321 + else 322 + echo 1>&2 "nixos-acme: renewing now, because certificate expires within the configured ${toString cfg.validMinDays} days" 323 + lego ${renewOpts} --days ${toString cfg.validMinDays} 324 + fi 292 325 else 326 + echo 1>&2 "certificate domain(s) have changed; will renew now" 293 327 # Any number > 90 works, but this one is over 9000 ;-) 294 328 lego ${renewOpts} --days 9001 295 329 fi