nixos/acme: drop email requirement (#489983)

This commit is contained in:
Franz Pletz
2026-03-22 11:53:08 +00:00
committed by GitHub
2 changed files with 42 additions and 11 deletions

View File

@@ -15,9 +15,21 @@ let
numCerts = lib.length (builtins.attrNames cfg.certs);
_24hSecs = 60 * 60 * 24;
# The placerholder email address used by lego in case none gets passed
placeholderEmail = "noemail@example.com";
# Used to make unique paths for each cert/account config set
mkHash = with builtins; val: lib.substring 0 20 (hashString "sha256" val);
mkAccountHash = acmeServer: data: mkHash "${toString acmeServer} ${data.keyType} ${data.email}";
mkAccountHash =
acmeServer: data:
mkHash (
lib.concatStringsSep " " [
(toString acmeServer)
data.keyType
(if (data.email != null) then data.email else placeholderEmail)
]
);
accountDirRoot = "/var/lib/acme/.lego/accounts/";
isIP =
@@ -273,6 +285,8 @@ let
"--accept-tos" # Checking the option is covered by the assertions
"--path"
"."
]
++ lib.optionals (data.email != null) [
"--email"
data.email
]
@@ -580,7 +594,12 @@ let
# Check if a new order is needed
# We can only renew if the list of domains has not changed.
# We also need an account key. Avoids #190493
if cmp -s domainhash.txt certificates/domainhash.txt && [ -e '${certificateKey}' ] && [ -e 'certificates/${keyName}.crt' ] && [ -n "$(find accounts -name '${data.email}.key')" ]; then
if cmp -s domainhash.txt certificates/domainhash.txt && [ -e '${certificateKey}' ] && \
[ -e 'certificates/${keyName}.crt' ] && \
[ -n "$(find accounts -name '${
if (data.email != null) then data.email else placeholderEmail
}.key')" ];
then
# Even if a cert is not expired, it may be revoked by the CA.
# Try to renew, and silently fail if the cert is not expired.
# Avoids #85794 and resolves #129838
@@ -1118,14 +1137,6 @@ in
certs = lib.attrValues cfg.certs;
in
[
{
assertion = cfg.defaults.email != null || lib.all (certOpts: certOpts.email != null) certs;
message = ''
You must define `security.acme.certs.<name>.email` or
`security.acme.defaults.email` to register with the CA. Note that using
many different addresses for certs may trigger account rate limits.
'';
}
{
assertion = cfg.acceptTerms;
message = ''

View File

@@ -52,7 +52,14 @@ in
};
accountchange.configuration = {
security.acme.certs."${config.networking.fqdn}".email = "admin@example.test";
# Providing an email address is optional
security.acme.certs."${config.networking.fqdn}".email = null;
};
emailplaceholder.configuration = {
# but Lego will default to this email address, which should not
# result in any change when configured
security.acme.certs."${config.networking.fqdn}".email = "noemail@example.com";
};
keytype.configuration = {
@@ -157,6 +164,7 @@ in
certName = nodes.builtin.networking.fqdn;
caDomain = nodes.acme.test-support.acme.caDomain;
in
# python
''
${(import ./utils.nix).pythonUtils}
@@ -217,11 +225,23 @@ in
hash_after = builtin.succeed(f"sha256sum /var/lib/acme/{cert}/cert.pem")
# Has to do a full run to register account, which creates new certs.
assert hash != hash_after, "Certificate was not renewed"
hash = hash_after
builtin.succeed("systemctl stop renew-triggered.target")
switch_to(builtin, "emailplaceholder")
builtin.wait_for_unit("renew-triggered.target")
# Check that there are still two account directories
builtin.succeed("test $(ls -1 /var/lib/acme/.lego/accounts | tee /dev/stderr | wc -l) -eq 2")
hash_after = builtin.succeed(f"sha256sum /var/lib/acme/{cert}/cert.pem")
assert hash == hash_after, "Implicit to explicit email placeholder renewed the certificate"
# Remove the new account directory
builtin.succeed(
"cd /var/lib/acme/.lego/accounts"
" && ls -1 --sort=time | tee /dev/stderr | head -1 | xargs rm -rf"
)
# old_hash will be used in the preservation tests later
old_hash = hash_after