From a86d6ed9b1aa42abace60a1f70676c26c9326c75 Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Thu, 16 Apr 2026 16:06:03 +0200 Subject: [PATCH 1/2] nixos/acme: make webroot world-readable at creation Up to now, the webroot mechanism wasn't usable without extra steps when the certificate was not intended for the web server. In my case, I wanted to obtain a certificate for use in other services from a private CA, but validate via a webroot served by nginx. The (sensibly!) restrictive umask of acme-$name.service led to the webroot being created with o-rx permissions, which meant that nginx could not serve the webroot and the certificate could not be obtained. This retains the mkdir invocation as a best-effort attempt to create the webroot if for example the state has been cleared since systemd-tmpfiles setup, but relies primarily on systemd-tmpfiles to create the webroots with appropriate permissions. --- nixos/modules/security/acme/default.nix | 26 +++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/nixos/modules/security/acme/default.nix b/nixos/modules/security/acme/default.nix index 2a2e816512e9..95f60c80867d 100644 --- a/nixos/modules/security/acme/default.nix +++ b/nixos/modules/security/acme/default.nix @@ -443,13 +443,16 @@ let done ${lib.optionalString (data.webroot != null) '' - # Ensure the webroot exists. Fixing group is required in case configuration was changed between runs. + # Ensure the webroot exists. # Lego will fail if the webroot does not exist at all. ( - mkdir -p '${data.webroot}/.well-known/acme-challenge' \ - && chgrp '${data.group}' ${data.webroot}/.well-known/acme-challenge + # If creating the webroot dir, make it world-readable so that the + # web server can access it regardless of whether the certificate + # is actually being generated for this web server. + umask 0022 \ + && mkdir -p '${data.webroot}/.well-known/acme-challenge' ) || ( - echo 'Please ensure ${data.webroot}/.well-known/acme-challenge exists and is writable by acme:${data.group}' \ + echo 'Please ensure ${data.webroot}/.well-known/acme-challenge exists and is writable by the user ${user}.' \ && exit 1 ) ''} @@ -1281,6 +1284,21 @@ in ) (lib.groupBy (conf: conf.accountHash) (lib.attrValues certConfigs)); in accountTargets; + + systemd.tmpfiles.settings."10-acme" = + lib.genAttrs + (lib.concatMap (dir: [ + dir + (dir + "/.well-known") + (dir + "/.well-known/acme-challenge") + ]) webroots) + (dir: { + "d" = { + inherit user; + group = "acme"; + mode = "0755"; + }; + }); }) ]; From d25d63903113ef973cc627ebc63641eb9900a40b Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Thu, 16 Apr 2026 16:41:56 +0200 Subject: [PATCH 2/2] nixos/acme: remove scripted webroot creation Situations where systemd-tmpfiles rules aren't applied but acme-$name.service is rerun are quite unlikely, so it should be fine to remove the mkdir completely. --- nixos/modules/security/acme/default.nix | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/nixos/modules/security/acme/default.nix b/nixos/modules/security/acme/default.nix index 95f60c80867d..1d69922a4046 100644 --- a/nixos/modules/security/acme/default.nix +++ b/nixos/modules/security/acme/default.nix @@ -441,21 +441,6 @@ let chown -R ${user}:${data.group} "$fixpath" fi done - - ${lib.optionalString (data.webroot != null) '' - # Ensure the webroot exists. - # Lego will fail if the webroot does not exist at all. - ( - # If creating the webroot dir, make it world-readable so that the - # web server can access it regardless of whether the certificate - # is actually being generated for this web server. - umask 0022 \ - && mkdir -p '${data.webroot}/.well-known/acme-challenge' - ) || ( - echo 'Please ensure ${data.webroot}/.well-known/acme-challenge exists and is writable by the user ${user}.' \ - && exit 1 - ) - ''} ''; };