From 85769a8cd8012e5dfb907f49555ccc7c3c5a9d35 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sun, 13 Dec 2020 20:22:33 +0000 Subject: [PATCH 1/9] nixos/acme: prevent mass account creation Closes #106565 When generating multiple certificates which all share the same server + email, lego will attempt to create an account multiple times. By adding an account creation target certificates which share an account will wait for one service (chosen at config build time) to complete first. --- nixos/modules/security/acme.nix | 46 +++++++++++++++++++++++---------- nixos/tests/acme.nix | 32 ++++++++++++++++++++++- 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 8e646ae1567..4a5ffb7ba19 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -7,6 +7,11 @@ let numCerts = length (builtins.attrNames cfg.certs); _24hSecs = 60 * 60 * 24; + # Used to make unique paths for each cert/account config set + mkHash = with builtins; val: substring 0 20 (hashString "sha256" val); + mkAccountHash = acmeServer: data: mkHash "${toString acmeServer} ${data.keyType} ${data.email}"; + accountDirRoot = "/var/lib/acme/.lego/accounts/"; + # There are many services required to make cert renewals work. # They all follow a common structure: # - They inherit this commonServiceConfig @@ -101,11 +106,10 @@ let ${toString acmeServer} ${toString data.dnsProvider} ${toString data.ocspMustStaple} ${data.keyType} ''; - mkHash = with builtins; val: substring 0 20 (hashString "sha256" val); certDir = mkHash hashData; domainHash = mkHash "${concatStringsSep " " extraDomains} ${data.domain}"; - othersHash = mkHash "${toString acmeServer} ${data.keyType} ${data.email}"; - accountDir = "/var/lib/acme/.lego/accounts/" + othersHash; + accountHash = (mkAccountHash acmeServer data); + accountDir = accountDirRoot + accountHash; protocolOpts = if useDns then ( [ "--dns" data.dnsProvider ] @@ -142,7 +146,7 @@ let ); in { - inherit accountDir selfsignedDeps; + inherit accountHash accountDir cert selfsignedDeps; webroot = data.webroot; group = data.group; @@ -253,8 +257,7 @@ let echo '${domainHash}' > domainhash.txt # Check if we can renew - # Certificates and account credentials must exist - if [ -e 'certificates/${keyName}.key' -a -e 'certificates/${keyName}.crt' -a "$(ls -1 accounts)" ]; then + if [ -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 # Lego run, but it's likely renew won't work if days is too low. @@ -670,15 +673,32 @@ in { "d /var/lib/acme/.lego/accounts - acme acme" ] ++ (unique (concatMap (conf: [ "d ${conf.accountDir} - acme acme" - ] ++ (optional (conf.webroot != null) "d ${conf.webroot}/.well-known/acme-challenge - acme ${conf.group}") + ] ++ (optionals (conf.webroot != null) [ + "d ${conf.webroot} - acme ${conf.group}" + "d ${conf.webroot}/.well-known - acme ${conf.group}" + "d ${conf.webroot}/.well-known/acme-challenge - acme ${conf.group}" + ]) ) (attrValues certConfigs))); - # Create some targets which can be depended on to be "active" after cert renewals - systemd.targets = mapAttrs' (cert: conf: nameValuePair "acme-finished-${cert}" { - wantedBy = [ "default.target" ]; - requires = [ "acme-${cert}.service" ] ++ conf.selfsignedDeps; - after = [ "acme-${cert}.service" ] ++ conf.selfsignedDeps; - }) certConfigs; + systemd.targets = let + # Create some targets which can be depended on to be "active" after cert renewals + finishedTargets = mapAttrs' (cert: conf: nameValuePair "acme-finished-${cert}" { + wantedBy = [ "default.target" ]; + requires = [ "acme-${cert}.service" ] ++ conf.selfsignedDeps; + after = [ "acme-${cert}.service" ] ++ conf.selfsignedDeps; + }) certConfigs; + + # Create targets to limit the number of simultaneous account creations + accountTargets = mapAttrs' (hash: confs: let + leader = "acme-${(builtins.head confs).cert}.service"; + dependantServices = map (conf: "acme-${conf.cert}.service") (builtins.tail confs); + in nameValuePair "acme-account-${hash}" { + requiredBy = dependantServices; + before = dependantServices; + requires = [ leader ]; + after = [ leader ]; + }) (groupBy (conf: conf.accountHash) (attrValues certConfigs)); + in finishedTargets // accountTargets; }) ]; diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix index eb152cf51a6..503d77f24f9 100644 --- a/nixos/tests/acme.nix +++ b/nixos/tests/acme.nix @@ -77,6 +77,27 @@ in import ./make-test-python.nix ({ lib, ... }: { after = [ "acme-a.example.test.service" "nginx-config-reload.service" ]; }; + # Test that account creation is collated into one service + specialisation.account-creation.configuration = { nodes, pkgs, lib, ... }: let + email = "newhostmaster@example.test"; + caDomain = nodes.acme.config.test-support.acme.caDomain; + # Exit 99 to make it easier to track if this is the reason a renew failed + testScript = '' + test -e accounts/${caDomain}/${email}/account.json || exit 99 + ''; + in { + security.acme.email = lib.mkForce email; + systemd.services."b.example.test".serviceConfig.preStart = testScript; + systemd.services."c.example.test".serviceConfig.preStart = testScript; + + services.nginx.virtualHosts."b.example.test" = (vhostBase pkgs) // { + enableACME = true; + }; + services.nginx.virtualHosts."c.example.test" = (vhostBase pkgs) // { + enableACME = true; + }; + }; + # Cert config changes will not cause the nginx configuration to change. # This tests that the reload service is correctly triggered. # It also tests that postRun is exec'd as root @@ -289,7 +310,7 @@ in import ./make-test-python.nix ({ lib, ... }: { acme.start() webserver.start() - acme.wait_for_unit("default.target") + acme.wait_for_unit("network-online.target") acme.wait_for_unit("pebble.service") client.succeed("curl https://${caDomain}:15000/roots/0 > /tmp/ca.crt") @@ -314,6 +335,15 @@ in import ./make-test-python.nix ({ lib, ... }: { check_issuer(webserver, "a.example.test", "pebble") check_connection(client, "a.example.test") + with subtest("Runs 1 cert for account creation before others"): + switch_to(webserver, "account-creation") + webserver.wait_for_unit("acme-finished-a.example.test.target") + check_connection(client, "a.example.test") + webserver.wait_for_unit("acme-finished-b.example.test.target") + webserver.wait_for_unit("acme-finished-c.example.test.target") + check_connection(client, "b.example.test") + check_connection(client, "c.example.test") + with subtest("Can reload web server when cert configuration changes"): switch_to(webserver, "cert-change") webserver.wait_for_unit("acme-finished-a.example.test.target") From 351065f9705c856ffc695af290a7c7aa2b57be6b Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sun, 13 Dec 2020 22:19:53 +0000 Subject: [PATCH 2/9] nixos/acme: reduce dependency on tmpfiles systemd-tmpfiles is no longer required for most of the critical paths in the module. The only one that remains is the webroot acme-challenge directory since there's no other good place for this to live and forcing users to do the right thing alone will only create more issues. --- nixos/modules/security/acme.nix | 49 ++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 4a5ffb7ba19..d9d8701ac30 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -59,9 +59,9 @@ let ''; }; - # Previously, all certs were owned by whatever user was configured in - # config.security.acme.certs..user. Now everything is owned by and - # run by the acme user. + # Ensures that directories which are shared across all certs + # exist and have the correct user and group, since group + # is configurable on a per-cert basis. userMigrationService = { description = "Fix owner and group of all ACME certificates"; @@ -74,8 +74,13 @@ let done '') certConfigs); - # We don't want this to run every time a renewal happens - serviceConfig.RemainAfterExit = true; + serviceConfig = { + # We don't want this to run every time a renewal happens + RemainAfterExit = true; + + # These StateDirectory entries negate the need for tmpfiles + StateDirectory = "acme acme/.lego acme/.lego/accounts"; + }; }; certToConfig = cert: data: let @@ -146,7 +151,7 @@ let ); in { - inherit accountHash accountDir cert selfsignedDeps; + inherit accountHash cert selfsignedDeps; webroot = data.webroot; group = data.group; @@ -226,10 +231,14 @@ let serviceConfig = commonServiceConfig // { Group = data.group; - # AccountDir dir will be created by tmpfiles to ensure correct permissions - # And to avoid deletion during systemctl clean - # acme/.lego/${cert} is listed so that it is deleted during systemctl clean - StateDirectory = "acme/${cert} acme/.lego/${cert} acme/.lego/${cert}/${certDir}"; + # Keep in mind that these directories will be deleted if the user runs + # systemctl clean --what=state + # acme/.lego/${cert} is listed for this reason. + StateDirectory = + "acme/${cert} " + + "acme/.lego/${cert} " + + "acme/.lego/${cert}/${certDir} " + + "acme/.lego/accounts/${accountHash} "; # Needs to be space separated, but can't use a multiline string because that'll include newlines BindPaths = @@ -667,18 +676,14 @@ in { systemd.timers = mapAttrs' (cert: conf: nameValuePair "acme-${cert}" conf.renewTimer) certConfigs; - # .lego and .lego/accounts specified to fix any incorrect permissions - systemd.tmpfiles.rules = [ - "d /var/lib/acme/.lego - acme acme" - "d /var/lib/acme/.lego/accounts - acme acme" - ] ++ (unique (concatMap (conf: [ - "d ${conf.accountDir} - acme acme" - ] ++ (optionals (conf.webroot != null) [ - "d ${conf.webroot} - acme ${conf.group}" - "d ${conf.webroot}/.well-known - acme ${conf.group}" - "d ${conf.webroot}/.well-known/acme-challenge - acme ${conf.group}" - ]) - ) (attrValues certConfigs))); + systemd.tmpfiles.rules = unique ( + flatten ( + mapAttrsToList ( + cert: conf: + optional (conf.webroot != null) "d ${conf.webroot}/.well-known/acme-challenge - acme ${conf.group}" + ) certConfigs + ) + ); systemd.targets = let # Create some targets which can be depended on to be "active" after cert renewals From f670e1dc23bab2af7fdd8d7121d9f281d744ed1b Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sun, 13 Dec 2020 22:33:27 +0000 Subject: [PATCH 3/9] nixos/acme: change service umask to 0023 Closes #106603 Some webservers (lighttpd) require that the files they are serving are world readable. We do our own chmods in the scripts anyway, and lego has sensible permissions on its output files, so this change is safe enough. --- nixos/modules/security/acme.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index d9d8701ac30..70c86d19680 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -24,7 +24,7 @@ let Type = "oneshot"; User = "acme"; Group = mkDefault "acme"; - UMask = 0027; + UMask = 0023; StateDirectoryMode = 750; ProtectSystem = "full"; PrivateTmp = true; From e5913db0c946b0d3408fc902858cdc2a26f7ad36 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sun, 13 Dec 2020 23:09:23 +0000 Subject: [PATCH 4/9] nixos/acme: update documentation and release notes The instructions on recreating the cert were missing --what=state. Also added a note on ensuring the group of manual certs is correct. --- nixos/doc/manual/release-notes/rl-2103.xml | 9 +++++++++ nixos/modules/security/acme.xml | 12 ++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2103.xml b/nixos/doc/manual/release-notes/rl-2103.xml index 38262b50899..4138f569c38 100644 --- a/nixos/doc/manual/release-notes/rl-2103.xml +++ b/nixos/doc/manual/release-notes/rl-2103.xml @@ -439,6 +439,15 @@ been dropped from upstream releases. + + + In the ACME module, the data used to build the hash for the account + directory has changed to accomodate new features to reduce account + rate limit issues. This will trigger new account creation on the first + rebuild following this update. No issues are expected to arise from this, + thanks to the new account creation handling. + + now always ensures home directory permissions to be 0700. diff --git a/nixos/modules/security/acme.xml b/nixos/modules/security/acme.xml index 517162d1a7b..3e7c8edfdf7 100644 --- a/nixos/modules/security/acme.xml +++ b/nixos/modules/security/acme.xml @@ -162,6 +162,9 @@ services.httpd = { ."foo.example.com" = { webroot = "/var/lib/acme/.challenges"; email = "foo@example.com"; + # Ensure that the web server you use can read the generated certs + # Take a look at the group option for the web server you choose. + group = "nginx"; # Since we have a wildcard vhost to handle port 80, # we can generate certs for anything! # Just make sure your DNS resolves them. @@ -257,10 +260,11 @@ chmod 400 /var/lib/secrets/certs.secret Should you need to regenerate a particular certificate in a hurry, such as when a vulnerability is found in Let's Encrypt, there is now a convenient - mechanism for doing so. Running systemctl clean acme-example.com.service - will remove all certificate files for the given domain, allowing you to then - systemctl start acme-example.com.service to generate fresh - ones. + mechanism for doing so. Running + systemctl clean --what=state acme-example.com.service + will remove all certificate files and the account data for the given domain, + allowing you to then systemctl start acme-example.com.service + to generate fresh ones.
From bfe07e21795685d023b0595d9305071f30e3d448 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Mon, 14 Dec 2020 19:40:28 +0000 Subject: [PATCH 5/9] nixos/acme: fix test config --- nixos/tests/acme.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix index 503d77f24f9..c6d393d9196 100644 --- a/nixos/tests/acme.nix +++ b/nixos/tests/acme.nix @@ -87,8 +87,8 @@ in import ./make-test-python.nix ({ lib, ... }: { ''; in { security.acme.email = lib.mkForce email; - systemd.services."b.example.test".serviceConfig.preStart = testScript; - systemd.services."c.example.test".serviceConfig.preStart = testScript; + systemd.services."b.example.test".preStart = testScript; + systemd.services."c.example.test".preStart = testScript; services.nginx.virtualHosts."b.example.test" = (vhostBase pkgs) // { enableACME = true; From 92a3a37153b159951d027a77cbb7b1ee7f92bde6 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Tue, 29 Dec 2020 15:01:08 +0000 Subject: [PATCH 6/9] nixos/acme: Remove all systemd-tmpfiles usage - Added an ExecPostStart to acme-$cert.service when webroot is defined to create the acme-challenge directory and fix required permissions. Lego always tries to create .well-known and acme-challenge, thus if any permissions in that tree are wrong it will crash and break cert renewal. - acme-fixperms now configured with acme User and Group, however the script still runs as root. This ensures the StateDirectories are owned by the acme user. - Switched to list syntax for systemd options where multiple values are specified. --- nixos/modules/security/acme.nix | 64 +++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 70c86d19680..d2d68eea9fd 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -62,24 +62,30 @@ let # Ensures that directories which are shared across all certs # exist and have the correct user and group, since group # is configurable on a per-cert basis. - userMigrationService = { - description = "Fix owner and group of all ACME certificates"; - + userMigrationService = let script = with builtins; concatStringsSep "\n" (mapAttrsToList (cert: data: '' - for fixpath in /var/lib/acme/${escapeShellArg cert} /var/lib/acme/.lego/${escapeShellArg cert}; do + chown -R acme .lego/accounts + for fixpath in ${escapeShellArg cert} .lego/${escapeShellArg cert}; do if [ -d "$fixpath" ]; then chmod -R u=rwX,g=rX,o= "$fixpath" chown -R acme:${data.group} "$fixpath" fi done '') certConfigs); + in { + description = "Fix owner and group of all ACME certificates"; - serviceConfig = { + serviceConfig = commonServiceConfig // { # We don't want this to run every time a renewal happens RemainAfterExit = true; # These StateDirectory entries negate the need for tmpfiles - StateDirectory = "acme acme/.lego acme/.lego/accounts"; + StateDirectory = [ "acme" "acme/.lego" "acme/.lego/accounts" ]; + StateDirectoryMode = 755; + WorkingDirectory = "/var/lib/acme"; + + # Run the start script as root + ExecStart = "+" + (pkgs.writeShellScript "acme-fixperms" script); }; }; @@ -153,7 +159,6 @@ let in { inherit accountHash cert selfsignedDeps; - webroot = data.webroot; group = data.group; renewTimer = { @@ -193,7 +198,10 @@ let StateDirectory = "acme/${cert}"; - BindPaths = "/var/lib/acme/.minica:/tmp/ca /var/lib/acme/${cert}:/tmp/${keyName}"; + BindPaths = [ + "/var/lib/acme/.minica:/tmp/ca" + "/var/lib/acme/${cert}:/tmp/${keyName}" + ]; }; # Working directory will be /tmp @@ -234,17 +242,19 @@ let # Keep in mind that these directories will be deleted if the user runs # systemctl clean --what=state # acme/.lego/${cert} is listed for this reason. - StateDirectory = - "acme/${cert} " + - "acme/.lego/${cert} " + - "acme/.lego/${cert}/${certDir} " + - "acme/.lego/accounts/${accountHash} "; + StateDirectory = [ + "acme/${cert}" + "acme/.lego/${cert}" + "acme/.lego/${cert}/${certDir}" + "acme/.lego/accounts/${accountHash}" + ]; # Needs to be space separated, but can't use a multiline string because that'll include newlines - BindPaths = - "${accountDir}:/tmp/accounts " + - "/var/lib/acme/${cert}:/tmp/out " + - "/var/lib/acme/.lego/${cert}/${certDir}:/tmp/certificates "; + BindPaths = [ + "${accountDir}:/tmp/accounts" + "/var/lib/acme/${cert}:/tmp/out" + "/var/lib/acme/.lego/${cert}/${certDir}:/tmp/certificates" + ]; # Only try loading the credentialsFile if the dns challenge is enabled EnvironmentFile = mkIf useDns data.credentialsFile; @@ -257,7 +267,16 @@ let ${data.postRun} fi ''); - }; + + } // (optionalAttrs (data.webroot != null) { + # Lego always tries to create .well-known/acme-challenge, but if webroot is owned + # by the wrong user then it will crash and break cert renewal. + ExecStartPre = "+" + pkgs.writeShellScript "acme-${cert}-make-webroot" '' + mkdir -p '${data.webroot}/.well-known/acme-challenge' + cd '${data.webroot}' + chown 'acme:${data.group}' . .well-known .well-known/acme-challenge + ''; + }); # Working directory will be /tmp script = '' @@ -676,15 +695,6 @@ in { systemd.timers = mapAttrs' (cert: conf: nameValuePair "acme-${cert}" conf.renewTimer) certConfigs; - systemd.tmpfiles.rules = unique ( - flatten ( - mapAttrsToList ( - cert: conf: - optional (conf.webroot != null) "d ${conf.webroot}/.well-known/acme-challenge - acme ${conf.group}" - ) certConfigs - ) - ); - systemd.targets = let # Create some targets which can be depended on to be "active" after cert renewals finishedTargets = mapAttrs' (cert: conf: nameValuePair "acme-finished-${cert}" { From a01df7dc4663650146cba65d25aaf225391f22ce Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sat, 9 Jan 2021 19:15:03 +0000 Subject: [PATCH 7/9] nixos/acme: Incorporate review suggestions --- nixos/modules/security/acme.nix | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index d2d68eea9fd..bf748d16821 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -63,15 +63,16 @@ let # exist and have the correct user and group, since group # is configurable on a per-cert basis. userMigrationService = let - script = with builtins; concatStringsSep "\n" (mapAttrsToList (cert: data: '' + script = with builtins; '' chown -R acme .lego/accounts + '' + (concatStringsSep "\n" (mapAttrsToList (cert: data: '' for fixpath in ${escapeShellArg cert} .lego/${escapeShellArg cert}; do if [ -d "$fixpath" ]; then chmod -R u=rwX,g=rX,o= "$fixpath" chown -R acme:${data.group} "$fixpath" fi done - '') certConfigs); + '') certConfigs)); in { description = "Fix owner and group of all ACME certificates"; @@ -704,6 +705,14 @@ in { }) certConfigs; # Create targets to limit the number of simultaneous account creations + # How it works: + # - Pick a "leader" cert service, which will be in charge of creating the account, + # and run first (requires + after) + # - Make all other cert services sharing the same account wait for the leader to + # finish before starting (requiredBy + before). + # Using a target here is fine - account creation is a one time event. Even if + # systemd clean --what=state is used to delete the account, so long as the user + # then runs one of the cert services, there won't be any issues. accountTargets = mapAttrs' (hash: confs: let leader = "acme-${(builtins.head confs).cert}.service"; dependantServices = map (conf: "acme-${conf.cert}.service") (builtins.tail confs); From 5b4f9c42442545ea0352e4cb776a0f94074fca5b Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sat, 9 Jan 2021 19:34:54 +0000 Subject: [PATCH 8/9] nixos/acme: Set up webroot as non-root user --- nixos/modules/security/acme.nix | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index bf748d16821..b0f2f7265c5 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -268,21 +268,19 @@ let ${data.postRun} fi ''); - - } // (optionalAttrs (data.webroot != null) { - # Lego always tries to create .well-known/acme-challenge, but if webroot is owned - # by the wrong user then it will crash and break cert renewal. - ExecStartPre = "+" + pkgs.writeShellScript "acme-${cert}-make-webroot" '' - mkdir -p '${data.webroot}/.well-known/acme-challenge' - cd '${data.webroot}' - chown 'acme:${data.group}' . .well-known .well-known/acme-challenge - ''; - }); + }; # Working directory will be /tmp script = '' set -euo pipefail + ${optionalString (data.webroot != null) '' + # Ensure the webroot exists + mkdir -p '${data.webroot}/.well-known/acme-challenge' + chown 'acme:${data.group}' ${data.webroot}/{.well-known,.well-known/acme-challenge} \ + || echo "Please fix the permissions under ${data.webroot}/.well-known/acme-challenge" && exit 1 + ''} + echo '${domainHash}' > domainhash.txt # Check if we can renew From 514a0b6d8adf9fa181549dd0ae5c52ee04846975 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Tue, 12 Jan 2021 19:11:50 +0000 Subject: [PATCH 9/9] nixos/acme: Fix bash issue, enable debug I found a logical error in the bash script, but during debugging I enabled command echoing and realised it would be a good idea to have it enabled all the time for ease of bug reporting. --- nixos/modules/security/acme.nix | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index b0f2f7265c5..6b62e5043ca 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -272,13 +272,12 @@ let # Working directory will be /tmp script = '' - set -euo pipefail + set -euxo pipefail ${optionalString (data.webroot != null) '' # Ensure the webroot exists mkdir -p '${data.webroot}/.well-known/acme-challenge' - chown 'acme:${data.group}' ${data.webroot}/{.well-known,.well-known/acme-challenge} \ - || echo "Please fix the permissions under ${data.webroot}/.well-known/acme-challenge" && exit 1 + chown 'acme:${data.group}' ${data.webroot}/{.well-known,.well-known/acme-challenge} ''} echo '${domainHash}' > domainhash.txt