From c98cf6835b4f1e9aed4bfd0d3ac6143e2cbaaf11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Sun, 21 Dec 2025 16:02:06 +0100 Subject: [PATCH] nixos/acme: fix ReadWritePaths for acme-${domain}.service MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently ReadWritePaths is only sufficiently specificed for acme-order-renew-${domain}.service, and not acme-${domain}.service. This results in service failure if specifying the webroot outside of /var/lib/acme, for example /var/www/challenges: acme-example.com-start[1379]: + mkdir -p /var/www/challenges//.well-known/acme-challenge acme-example.com-start[1382]: mkdir: cannot create directory ‘/var/www/challenges//.well-known’: Read-only file system systemd[1]: acme-example.com.service: Main process exited, code=exited, status=1/FAILURE Fix it by adding the webroots to ReadWritePaths in the common serviceConfig, where it can affect both acme-order-renew-${domain}.service AND acme-${domain}.service. Avoid adding subdirs of existing ReadWritePaths entries, because otherwise systemd will fail to set up the services, for example: acme-zeroconf.example.test.service: Failed to set up mount namespacing: /run/acme: No such file or directory (Confusingly, the path shown in the error message isn't necessarily related to the problematic path.) --- nixos/modules/security/acme/default.nix | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/nixos/modules/security/acme/default.nix b/nixos/modules/security/acme/default.nix index e16d38afe93d..1b262e285e19 100644 --- a/nixos/modules/security/acme/default.nix +++ b/nixos/modules/security/acme/default.nix @@ -51,6 +51,12 @@ let '' + script; + # We need to collect all the ACME webroots to grant them write + # access in the systemd service. + webroots = lib.remove null ( + lib.unique (map (certAttrs: certAttrs.webroot) (lib.attrValues config.security.acme.certs)) + ); + # There are many services required to make cert renewals work. # They all follow a common structure: # - They inherit this commonServiceConfig @@ -69,7 +75,9 @@ let ReadWritePaths = [ "/var/lib/acme" lockdir - ]; + ] + # Prevent runtime breakage by only adding non-overlapping paths. + ++ (lib.filter (x: !(lib.strings.hasPrefix "/var/lib/acme/" x)) webroots); PrivateTmp = true; WorkingDirectory = "/tmp"; @@ -299,12 +307,6 @@ let ++ data.extraLegoRenewFlags ); - # We need to collect all the ACME webroots to grant them write - # access in the systemd service. - webroots = lib.remove null ( - lib.unique (map (certAttrs: certAttrs.webroot) (lib.attrValues config.security.acme.certs)) - ); - certificateKey = if data.csrKey != null then "${data.csrKey}" else "certificates/${keyName}.key"; in { @@ -469,8 +471,6 @@ let "acme/.lego/accounts/${accountHash}" ]; - ReadWritePaths = commonServiceConfig.ReadWritePaths ++ webroots; - # Needs to be space separated, but can't use a multiline string because that'll include newlines BindPaths = [ "${accountDir}:/tmp/accounts"