nixos/acme: Check for revoked certificates
Closes #129838 It is possible for the CA to revoke a cert that has not yet expired. We must run lego to validate this before expiration, but we must still ignore failures on unexpired certs to retain compatibility with #85794 Also changed domainHash logic such that a renewal will only be attempted at all if domains are unchanged, and do a full run otherwises. Resolves #147540 but will be partially reverted when go-acme/lego#1532 is resolved + available.
This commit is contained in:
parent
87403a0b07
commit
a7f0001328
@ -156,6 +156,7 @@ let
|
|||||||
${toString data.ocspMustStaple} ${data.keyType}
|
${toString data.ocspMustStaple} ${data.keyType}
|
||||||
'';
|
'';
|
||||||
certDir = mkHash hashData;
|
certDir = mkHash hashData;
|
||||||
|
# TODO remove domainHash usage entirely. Waiting on go-acme/lego#1532
|
||||||
domainHash = mkHash "${concatStringsSep " " extraDomains} ${data.domain}";
|
domainHash = mkHash "${concatStringsSep " " extraDomains} ${data.domain}";
|
||||||
accountHash = (mkAccountHash acmeServer data);
|
accountHash = (mkAccountHash acmeServer data);
|
||||||
accountDir = accountDirRoot + accountHash;
|
accountDir = accountDirRoot + accountHash;
|
||||||
@ -372,22 +373,19 @@ let
|
|||||||
|
|
||||||
echo '${domainHash}' > domainhash.txt
|
echo '${domainHash}' > domainhash.txt
|
||||||
|
|
||||||
# Check if we can renew
|
# Check if we can renew.
|
||||||
if [ -e 'certificates/${keyName}.key' -a -e 'certificates/${keyName}.crt' -a -n "$(ls -1 accounts)" ]; then
|
# We can only renew if the list of domains has not changed.
|
||||||
|
if cmp -s domainhash.txt certificates/domainhash.txt && [ -e 'certificates/${keyName}.key' -a -e 'certificates/${keyName}.crt' -a -n "$(ls -1 accounts)" ]; then
|
||||||
|
|
||||||
# When domains are updated, there's no need to do a full
|
# Even if a cert is not expired, it may be revoked by the CA.
|
||||||
# Lego run, but it's likely renew won't work if days is too low.
|
# Try to renew, and silently fail if the cert is not expired.
|
||||||
if [ -e certificates/domainhash.txt ] && cmp -s domainhash.txt certificates/domainhash.txt; then
|
# Avoids #85794 and resolves #129838
|
||||||
|
if ! lego ${renewOpts} --days ${toString cfg.validMinDays}; then
|
||||||
if is_expiration_skippable out/full.pem; then
|
if is_expiration_skippable out/full.pem; then
|
||||||
echo 1>&2 "nixos-acme: skipping renewal because expiration isn't within the coming ${toString cfg.validMinDays} days"
|
echo 1>&2 "nixos-acme: Ignoring failed renewal because expiration isn't within the coming ${toString cfg.validMinDays} days"
|
||||||
else
|
else
|
||||||
echo 1>&2 "nixos-acme: renewing now, because certificate expires within the configured ${toString cfg.validMinDays} days"
|
exit 3
|
||||||
lego ${renewOpts} --days ${toString cfg.validMinDays}
|
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
echo 1>&2 "certificate domain(s) have changed; will renew now"
|
|
||||||
# Any number > 90 works, but this one is over 9000 ;-)
|
|
||||||
lego ${renewOpts} --days 9001
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Otherwise do a full run
|
# Otherwise do a full run
|
||||||
@ -406,8 +404,7 @@ let
|
|||||||
chown 'acme:${data.group}' certificates/*
|
chown 'acme:${data.group}' certificates/*
|
||||||
|
|
||||||
# Copy all certs to the "real" certs directory
|
# Copy all certs to the "real" certs directory
|
||||||
CERT='certificates/${keyName}.crt'
|
if ! cmp -s 'certificates/${keyName}.crt' out/fullchain.pem; then
|
||||||
if [ -e "$CERT" ] && ! cmp -s "$CERT" out/fullchain.pem; then
|
|
||||||
touch out/renewed
|
touch out/renewed
|
||||||
echo Installing new certificate
|
echo Installing new certificate
|
||||||
cp -vp 'certificates/${keyName}.crt' out/fullchain.pem
|
cp -vp 'certificates/${keyName}.crt' out/fullchain.pem
|
||||||
|
@ -113,11 +113,19 @@ in import ./make-test-python.nix ({ lib, ... }: {
|
|||||||
|
|
||||||
# Now adding an alias to ensure that the certs are updated
|
# Now adding an alias to ensure that the certs are updated
|
||||||
specialisation.nginx-aliases.configuration = { pkgs, ... }: {
|
specialisation.nginx-aliases.configuration = { pkgs, ... }: {
|
||||||
services.nginx.virtualHosts."a.example.test" = {
|
services.nginx.virtualHosts."a.example.test" = (vhostBase pkgs) // {
|
||||||
serverAliases = [ "b.example.test" ];
|
serverAliases = [ "b.example.test" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Must be run after nginx-aliases
|
||||||
|
specialisation.remove-extra-domain.configuration = { pkgs, ... } : {
|
||||||
|
# This also validates that useACMEHost doesn't unexpectedly add the domain.
|
||||||
|
services.nginx.virtualHosts."b.example.test" = (vhostBase pkgs) // {
|
||||||
|
useACMEHost = "a.example.test";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Test OCSP Stapling
|
# Test OCSP Stapling
|
||||||
specialisation.ocsp-stapling.configuration = { pkgs, ... }: {
|
specialisation.ocsp-stapling.configuration = { pkgs, ... }: {
|
||||||
security.acme.certs."a.example.test" = {
|
security.acme.certs."a.example.test" = {
|
||||||
@ -408,6 +416,18 @@ in import ./make-test-python.nix ({ lib, ... }: {
|
|||||||
check_connection(client, "a.example.test")
|
check_connection(client, "a.example.test")
|
||||||
check_connection(client, "b.example.test")
|
check_connection(client, "b.example.test")
|
||||||
|
|
||||||
|
with subtest("Can remove extra domains from a cert"):
|
||||||
|
switch_to(webserver, "remove-extra-domain")
|
||||||
|
webserver.wait_for_unit("acme-finished-a.example.test.target")
|
||||||
|
webserver.wait_for_unit("nginx.service")
|
||||||
|
check_connection(client, "a.example.test")
|
||||||
|
rc, _ = client.execute(
|
||||||
|
"openssl s_client -CAfile /tmp/ca.crt -connect b.example.test:443"
|
||||||
|
" </dev/null 2>/dev/null | openssl x509 -noout -text"
|
||||||
|
" | grep DNS: | grep b.example.test"
|
||||||
|
)
|
||||||
|
assert rc > 0, "Removed extraDomainName was not removed from the cert"
|
||||||
|
|
||||||
with subtest("Can request certificates for vhost + aliases (apache-httpd)"):
|
with subtest("Can request certificates for vhost + aliases (apache-httpd)"):
|
||||||
try:
|
try:
|
||||||
switch_to(webserver, "httpd-aliases")
|
switch_to(webserver, "httpd-aliases")
|
||||||
|
Loading…
Reference in New Issue
Block a user